mirror of
https://github.com/crystalidea/qt6windows7.git
synced 2025-07-02 07:15:27 +08:00
qt 6.5.1 original
This commit is contained in:
21
tests/auto/network/CMakeLists.txt
Normal file
21
tests/auto/network/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
# SSL library include path is not propagated with private tests which results in
|
||||
# test not being able to find the ssl header when they are not in the standard
|
||||
# include paths
|
||||
if (QT_FEATURE_private_tests)
|
||||
if (QT_FEATURE_openssl AND QT_FEATURE_openssl_linked AND QT_FEATURE_ssl)
|
||||
include_directories($<TARGET_PROPERTY:OpenSSL::SSL,INTERFACE_INCLUDE_DIRECTORIES>)
|
||||
endif()
|
||||
|
||||
if (QT_FEATURE_openssl AND QT_FEATURE_ssl AND NOT QT_FEATURE_openssl_linked)
|
||||
include_directories($<TARGET_PROPERTY:WrapOpenSSLHeaders::WrapOpenSSLHeaders,INTERFACE_INCLUDE_DIRECTORIES>)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# add_subdirectory(selftest) # special case not ported
|
||||
add_subdirectory(access)
|
||||
add_subdirectory(kernel)
|
||||
add_subdirectory(ssl)
|
||||
add_subdirectory(socket)
|
20
tests/auto/network/access/CMakeLists.txt
Normal file
20
tests/auto/network/access/CMakeLists.txt
Normal file
@ -0,0 +1,20 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
add_subdirectory(qnetworkdiskcache)
|
||||
add_subdirectory(qnetworkcookiejar)
|
||||
add_subdirectory(qnetworkaccessmanager)
|
||||
add_subdirectory(qnetworkcookie)
|
||||
add_subdirectory(qnetworkrequest)
|
||||
add_subdirectory(qnetworkreply)
|
||||
add_subdirectory(qnetworkcachemetadata)
|
||||
add_subdirectory(qabstractnetworkcache)
|
||||
if(QT_FEATURE_private_tests)
|
||||
add_subdirectory(qhttpheaderparser)
|
||||
add_subdirectory(qhttpnetworkconnection)
|
||||
add_subdirectory(qhttpnetworkreply)
|
||||
add_subdirectory(hpack)
|
||||
add_subdirectory(http2)
|
||||
add_subdirectory(hsts)
|
||||
add_subdirectory(qdecompresshelper)
|
||||
endif()
|
15
tests/auto/network/access/hpack/CMakeLists.txt
Normal file
15
tests/auto/network/access/hpack/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_hpack Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_hpack
|
||||
SOURCES
|
||||
tst_hpack.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::Network
|
||||
Qt::NetworkPrivate
|
||||
)
|
805
tests/auto/network/access/hpack/tst_hpack.cpp
Normal file
805
tests/auto/network/access/hpack/tst_hpack.cpp
Normal file
@ -0,0 +1,805 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// Copyright (C) 2014 Governikus GmbH & Co. KG.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QTest>
|
||||
#include <QRandomGenerator>
|
||||
|
||||
#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();
|
||||
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(quint64(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 = QRandomGenerator::global()->bounded(1000) > 500;
|
||||
isA.push_back(isString);
|
||||
if (!isString) {
|
||||
integers.push_back(QRandomGenerator::global()->bounded(1000u));
|
||||
out.write(integers.back());
|
||||
} else {
|
||||
const auto start = QRandomGenerator::global()->bounded(uint(bytes.length()) / 2);
|
||||
auto end = start * 2;
|
||||
if (!end)
|
||||
end = unsigned(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(QRandomGenerator::global()->bounded(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()
|
||||
{
|
||||
const FieldLookupTable table(0, false /*all static, no need in 'search index'*/);
|
||||
const auto &staticTable = FieldLookupTable::staticPart();
|
||||
QByteArray name, value;
|
||||
quint32 currentIndex = 1; // HPACK is indexing starting from 1.
|
||||
for (const HeaderField &field : staticTable) {
|
||||
const quint32 index = table.indexOf(field.name, field.value);
|
||||
QVERIFY(index != 0);
|
||||
QCOMPARE(index, currentIndex);
|
||||
QVERIFY(table.field(index, &name, &value));
|
||||
QCOMPARE(name, field.name);
|
||||
QCOMPARE(value, field.value);
|
||||
++currentIndex;
|
||||
}
|
||||
}
|
||||
|
||||
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 = QRandomGenerator::global()->bounded(dataSize - 10);
|
||||
quint32 len = QRandomGenerator::global()->bounded(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"
|
17
tests/auto/network/access/hsts/CMakeLists.txt
Normal file
17
tests/auto/network/access/hsts/CMakeLists.txt
Normal file
@ -0,0 +1,17 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qhsts Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qhsts
|
||||
SOURCES
|
||||
tst_qhsts.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::Network
|
||||
Qt::NetworkPrivate
|
||||
)
|
||||
|
||||
# TEMPLATE = "app"
|
371
tests/auto/network/access/hsts/tst_qhsts.cpp
Normal file
371
tests/auto/network/access/hsts/tst_qhsts.cpp
Normal file
@ -0,0 +1,371 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include <QtCore/qdatetime.h>
|
||||
#include <QtCore/qdir.h>
|
||||
#include <QtCore/qlist.h>
|
||||
#include <QtCore/qpair.h>
|
||||
#include <QtCore/qurl.h>
|
||||
|
||||
#include <QtNetwork/private/qhstsstore_p.h>
|
||||
#include <QtNetwork/private/qhsts_p.h>
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
class tst_QHsts : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void testSingleKnownHost_data();
|
||||
void testSingleKnownHost();
|
||||
void testMultilpeKnownHosts();
|
||||
void testPolicyExpiration();
|
||||
void testSTSHeaderParser();
|
||||
void testStore();
|
||||
};
|
||||
|
||||
void tst_QHsts::testSingleKnownHost_data()
|
||||
{
|
||||
QTest::addColumn<QUrl>("knownHost");
|
||||
QTest::addColumn<QDateTime>("policyExpires");
|
||||
QTest::addColumn<bool>("includeSubDomains");
|
||||
QTest::addColumn<QUrl>("hostToTest");
|
||||
QTest::addColumn<bool>("isKnown");
|
||||
|
||||
const QDateTime currentUTC = QDateTime::currentDateTimeUtc();
|
||||
const QUrl knownHost(QLatin1String("http://example.com"));
|
||||
const QUrl validSubdomain(QLatin1String("https://sub.example.com/ohoho"));
|
||||
const QUrl unknownDomain(QLatin1String("http://example.org"));
|
||||
const QUrl subSubdomain(QLatin1String("https://level3.level2.example.com"));
|
||||
|
||||
const QDateTime validDate(currentUTC.addSecs(1000));
|
||||
QTest::newRow("same-known") << knownHost << validDate << false << knownHost << true;
|
||||
QTest::newRow("subexcluded") << knownHost << validDate << false << validSubdomain << false;
|
||||
QTest::newRow("subincluded") << knownHost << validDate << true << validSubdomain << true;
|
||||
QTest::newRow("unknown-subexcluded") << knownHost << validDate << false << unknownDomain << false;
|
||||
QTest::newRow("unknown-subincluded") << knownHost << validDate << true << unknownDomain << false;
|
||||
QTest::newRow("sub-subdomain-subincluded") << knownHost << validDate << true << subSubdomain << true;
|
||||
QTest::newRow("sub-subdomain-subexcluded") << knownHost << validDate << false << subSubdomain << false;
|
||||
|
||||
const QDateTime invalidDate;
|
||||
QTest::newRow("invalid-time") << knownHost << invalidDate << false << knownHost << false;
|
||||
QTest::newRow("invalid-time-subexcluded") << knownHost << invalidDate << false
|
||||
<< validSubdomain << false;
|
||||
QTest::newRow("invalid-time-subincluded") << knownHost << invalidDate << true
|
||||
<< validSubdomain << false;
|
||||
|
||||
const QDateTime expiredDate(currentUTC.addSecs(-1000));
|
||||
QTest::newRow("expired-time") << knownHost << expiredDate << false << knownHost << false;
|
||||
QTest::newRow("expired-time-subexcluded") << knownHost << expiredDate << false
|
||||
<< validSubdomain << false;
|
||||
QTest::newRow("expired-time-subincluded") << knownHost << expiredDate << true
|
||||
<< validSubdomain << false;
|
||||
const QUrl ipAsHost(QLatin1String("http://127.0.0.1"));
|
||||
QTest::newRow("ip-address-in-hostname") << ipAsHost << validDate << false
|
||||
<< ipAsHost << false;
|
||||
|
||||
const QUrl anyIPv4AsHost(QLatin1String("http://0.0.0.0"));
|
||||
QTest::newRow("anyip4-address-in-hostname") << anyIPv4AsHost << validDate
|
||||
<< false << anyIPv4AsHost << false;
|
||||
const QUrl anyIPv6AsHost(QLatin1String("http://[::]"));
|
||||
QTest::newRow("anyip6-address-in-hostname") << anyIPv6AsHost << validDate
|
||||
<< false << anyIPv6AsHost << false;
|
||||
|
||||
}
|
||||
|
||||
void tst_QHsts::testSingleKnownHost()
|
||||
{
|
||||
QFETCH(const QUrl, knownHost);
|
||||
QFETCH(const QDateTime, policyExpires);
|
||||
QFETCH(const bool, includeSubDomains);
|
||||
QFETCH(const QUrl, hostToTest);
|
||||
QFETCH(const bool, isKnown);
|
||||
|
||||
QHstsCache cache;
|
||||
cache.updateKnownHost(knownHost, policyExpires, includeSubDomains);
|
||||
QCOMPARE(cache.isKnownHost(hostToTest), isKnown);
|
||||
}
|
||||
|
||||
void tst_QHsts::testMultilpeKnownHosts()
|
||||
{
|
||||
const QDateTime currentUTC = QDateTime::currentDateTimeUtc();
|
||||
const QDateTime validDate(currentUTC.addSecs(10000));
|
||||
const QDateTime expiredDate(currentUTC.addSecs(-10000));
|
||||
const QUrl exampleCom(QLatin1String("https://example.com"));
|
||||
const QUrl subExampleCom(QLatin1String("https://sub.example.com"));
|
||||
|
||||
QHstsCache cache;
|
||||
// example.com is HSTS and includes subdomains:
|
||||
cache.updateKnownHost(exampleCom, validDate, true);
|
||||
QVERIFY(cache.isKnownHost(exampleCom));
|
||||
QVERIFY(cache.isKnownHost(subExampleCom));
|
||||
// example.com can set its policy not to include subdomains:
|
||||
cache.updateKnownHost(exampleCom, validDate, false);
|
||||
QVERIFY(!cache.isKnownHost(subExampleCom));
|
||||
// but sub.example.com can set its own policy:
|
||||
cache.updateKnownHost(subExampleCom, validDate, false);
|
||||
QVERIFY(cache.isKnownHost(subExampleCom));
|
||||
// let's say example.com's policy has expired:
|
||||
cache.updateKnownHost(exampleCom, expiredDate, false);
|
||||
QVERIFY(!cache.isKnownHost(exampleCom));
|
||||
// it should not affect sub.example.com's policy:
|
||||
QVERIFY(cache.isKnownHost(subExampleCom));
|
||||
|
||||
// clear cache and invalidate all policies:
|
||||
cache.clear();
|
||||
QVERIFY(!cache.isKnownHost(exampleCom));
|
||||
QVERIFY(!cache.isKnownHost(subExampleCom));
|
||||
|
||||
// siblings:
|
||||
const QUrl anotherSub(QLatin1String("https://sub2.example.com"));
|
||||
cache.updateKnownHost(subExampleCom, validDate, true);
|
||||
cache.updateKnownHost(anotherSub, validDate, true);
|
||||
QVERIFY(cache.isKnownHost(subExampleCom));
|
||||
QVERIFY(cache.isKnownHost(anotherSub));
|
||||
// they cannot set superdomain's policy:
|
||||
QVERIFY(!cache.isKnownHost(exampleCom));
|
||||
// a sibling cannot set another sibling's policy:
|
||||
cache.updateKnownHost(anotherSub, expiredDate, false);
|
||||
QVERIFY(cache.isKnownHost(subExampleCom));
|
||||
QVERIFY(!cache.isKnownHost(anotherSub));
|
||||
QVERIFY(!cache.isKnownHost(exampleCom));
|
||||
// let's make example.com known again:
|
||||
cache.updateKnownHost(exampleCom, validDate, true);
|
||||
// a subdomain cannot affect its superdomain's policy:
|
||||
cache.updateKnownHost(subExampleCom, expiredDate, true);
|
||||
QVERIFY(cache.isKnownHost(exampleCom));
|
||||
// and this superdomain includes subdomains in its HSTS policy:
|
||||
QVERIFY(cache.isKnownHost(subExampleCom));
|
||||
QVERIFY(cache.isKnownHost(anotherSub));
|
||||
|
||||
// a subdomain (with its subdomains) cannot affect its superdomain's policy:
|
||||
cache.updateKnownHost(exampleCom, expiredDate, true);
|
||||
cache.updateKnownHost(subExampleCom, validDate, true);
|
||||
QVERIFY(cache.isKnownHost(subExampleCom));
|
||||
QVERIFY(!cache.isKnownHost(exampleCom));
|
||||
}
|
||||
|
||||
void tst_QHsts::testPolicyExpiration()
|
||||
{
|
||||
QDateTime currentUTC = QDateTime::currentDateTimeUtc();
|
||||
const QUrl exampleCom(QLatin1String("http://example.com"));
|
||||
const QUrl subdomain(QLatin1String("http://subdomain.example.com"));
|
||||
const qint64 lifeTimeMS = 50;
|
||||
|
||||
QHstsCache cache;
|
||||
// start with 'includeSubDomains' and 5 s. lifetime:
|
||||
cache.updateKnownHost(exampleCom, currentUTC.addMSecs(lifeTimeMS), true);
|
||||
QVERIFY(cache.isKnownHost(exampleCom));
|
||||
QVERIFY(cache.isKnownHost(subdomain));
|
||||
// wait for approx. a half of lifetime:
|
||||
QTest::qWait(lifeTimeMS / 2);
|
||||
|
||||
if (QDateTime::currentDateTimeUtc() < currentUTC.addMSecs(lifeTimeMS)) {
|
||||
// Should still be valid:
|
||||
QVERIFY(cache.isKnownHost(exampleCom));
|
||||
QVERIFY(cache.isKnownHost(subdomain));
|
||||
}
|
||||
|
||||
QTest::qWait(lifeTimeMS);
|
||||
// expired:
|
||||
QVERIFY(!cache.isKnownHost(exampleCom));
|
||||
QVERIFY(!cache.isKnownHost(subdomain));
|
||||
|
||||
// now check that superdomain's policy expires, but not subdomain's policy:
|
||||
currentUTC = QDateTime::currentDateTimeUtc();
|
||||
cache.updateKnownHost(exampleCom, currentUTC.addMSecs(lifeTimeMS / 5), true);
|
||||
cache.updateKnownHost(subdomain, currentUTC.addMSecs(lifeTimeMS), true);
|
||||
QVERIFY(cache.isKnownHost(exampleCom));
|
||||
QVERIFY(cache.isKnownHost(subdomain));
|
||||
QTest::qWait(lifeTimeMS / 2);
|
||||
if (QDateTime::currentDateTimeUtc() < currentUTC.addMSecs(lifeTimeMS)) {
|
||||
QVERIFY(!cache.isKnownHost(exampleCom));
|
||||
QVERIFY(cache.isKnownHost(subdomain));
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QHsts::testSTSHeaderParser()
|
||||
{
|
||||
QHstsHeaderParser parser;
|
||||
using Header = QPair<QByteArray, QByteArray>;
|
||||
using Headers = QList<Header>;
|
||||
|
||||
QVERIFY(!parser.includeSubDomains());
|
||||
QVERIFY(!parser.expirationDate().isValid());
|
||||
Headers list;
|
||||
QVERIFY(!parser.parse(list));
|
||||
QVERIFY(!parser.includeSubDomains());
|
||||
QVERIFY(!parser.expirationDate().isValid());
|
||||
|
||||
list << Header("Strict-Transport-security", "200");
|
||||
QVERIFY(!parser.parse(list));
|
||||
QVERIFY(!parser.includeSubDomains());
|
||||
QVERIFY(!parser.expirationDate().isValid());
|
||||
|
||||
// This header is missing REQUIRED max-age directive, so we'll ignore it:
|
||||
list << Header("Strict-Transport-Security", "includeSubDomains");
|
||||
QVERIFY(!parser.parse(list));
|
||||
QVERIFY(!parser.includeSubDomains());
|
||||
QVERIFY(!parser.expirationDate().isValid());
|
||||
|
||||
list.pop_back();
|
||||
list << Header("Strict-Transport-Security", "includeSubDomains;max-age=1000");
|
||||
QVERIFY(parser.parse(list));
|
||||
QVERIFY(parser.expirationDate() > QDateTime::currentDateTimeUtc());
|
||||
QVERIFY(parser.includeSubDomains());
|
||||
|
||||
list.pop_back();
|
||||
list << Header("strict-transport-security", "includeSubDomains;max-age=1000");
|
||||
QVERIFY(parser.parse(list));
|
||||
QVERIFY(parser.expirationDate() > QDateTime::currentDateTimeUtc());
|
||||
QVERIFY(parser.includeSubDomains());
|
||||
|
||||
list.pop_back();
|
||||
// Invalid (includeSubDomains twice):
|
||||
list << Header("Strict-Transport-Security", "max-age = 1000 ; includeSubDomains;includeSubDomains");
|
||||
QVERIFY(!parser.parse(list));
|
||||
QVERIFY(!parser.includeSubDomains());
|
||||
QVERIFY(!parser.expirationDate().isValid());
|
||||
|
||||
list.pop_back();
|
||||
// Invalid (weird number of seconds):
|
||||
list << Header("Strict-Transport-Security", "max-age=-1000 ; includeSubDomains");
|
||||
QVERIFY(!parser.parse(list));
|
||||
QVERIFY(!parser.includeSubDomains());
|
||||
QVERIFY(!parser.expirationDate().isValid());
|
||||
|
||||
list.pop_back();
|
||||
// Note, directives are case-insensitive + we should ignore unknown directive.
|
||||
list << Header("Strict-Transport-Security", ";max-age=1000 ;includesubdomains;;"
|
||||
"nowsomeunknownheader=\"somevaluewithescapes\\;\"");
|
||||
QVERIFY(parser.parse(list));
|
||||
QVERIFY(parser.includeSubDomains());
|
||||
QVERIFY(parser.expirationDate().isValid());
|
||||
|
||||
list.pop_back();
|
||||
// Check that we know how to unescape max-age:
|
||||
list << Header("Strict-Transport-Security", "max-age=\"1000\"");
|
||||
QVERIFY(parser.parse(list));
|
||||
QVERIFY(!parser.includeSubDomains());
|
||||
QVERIFY(parser.expirationDate().isValid());
|
||||
|
||||
list.pop_back();
|
||||
// The only STS header, with invalid syntax though, to be ignored:
|
||||
list << Header("Strict-Transport-Security", "max-age; max-age=15768000");
|
||||
QVERIFY(!parser.parse(list));
|
||||
QVERIFY(!parser.includeSubDomains());
|
||||
QVERIFY(!parser.expirationDate().isValid());
|
||||
|
||||
// Now we check that our parse chosses the first valid STS header and ignores
|
||||
// others:
|
||||
list.clear();
|
||||
list << Header("Strict-Transport-Security", "includeSubdomains; max-age=\"hehehe\";");
|
||||
list << Header("Strict-Transport-Security", "max-age=10101");
|
||||
QVERIFY(parser.parse(list));
|
||||
QVERIFY(!parser.includeSubDomains());
|
||||
QVERIFY(parser.expirationDate().isValid());
|
||||
|
||||
|
||||
list.clear();
|
||||
list << Header("Strict-Transport-Security", "max-age=0");
|
||||
QVERIFY(parser.parse(list));
|
||||
QVERIFY(!parser.includeSubDomains());
|
||||
QVERIFY(parser.expirationDate() <= QDateTime::currentDateTimeUtc());
|
||||
|
||||
// Parsing is case-insensitive:
|
||||
list.pop_back();
|
||||
list << Header("Strict-Transport-Security", "Max-aGE=1000; InclUdesUbdomains");
|
||||
QVERIFY(parser.parse(list));
|
||||
QVERIFY(parser.includeSubDomains());
|
||||
QVERIFY(parser.expirationDate().isValid());
|
||||
|
||||
// Grammar of STS header is quite permissive, let's check we can parse
|
||||
// some weird but valid header:
|
||||
list.pop_back();
|
||||
list << Header("Strict-Transport-Security", ";;; max-age = 17; ; ; ; ;;; ;;"
|
||||
";;; ; includeSubdomains ;;thisIsUnknownDirective;;;;");
|
||||
QVERIFY(parser.parse(list));
|
||||
QVERIFY(parser.includeSubDomains());
|
||||
QVERIFY(parser.expirationDate().isValid());
|
||||
|
||||
list.pop_back();
|
||||
list << Header("Strict-Transport-Security", "max-age=1000; includeSubDomains bogon");
|
||||
QVERIFY(!parser.parse(list));
|
||||
QVERIFY(!parser.includeSubDomains());
|
||||
QVERIFY(!parser.expirationDate().isValid());
|
||||
}
|
||||
|
||||
const QLatin1String storeDir(".");
|
||||
|
||||
struct TestStoreDeleter
|
||||
{
|
||||
~TestStoreDeleter()
|
||||
{
|
||||
QDir cwd;
|
||||
if (!cwd.remove(QHstsStore::absoluteFilePath(storeDir)))
|
||||
qWarning() << "tst_QHsts::testStore: failed to remove the hsts store file";
|
||||
}
|
||||
};
|
||||
|
||||
void tst_QHsts::testStore()
|
||||
{
|
||||
// Delete the store's file after we finish the test.
|
||||
TestStoreDeleter cleaner;
|
||||
|
||||
const QUrl exampleCom(QStringLiteral("http://example.com"));
|
||||
const QUrl subDomain(QStringLiteral("http://subdomain.example.com"));
|
||||
const QDateTime validDate(QDateTime::currentDateTimeUtc().addDays(1));
|
||||
|
||||
{
|
||||
// We start from an empty cache and empty store:
|
||||
QHstsCache cache;
|
||||
QHstsStore store(storeDir);
|
||||
cache.setStore(&store);
|
||||
QVERIFY(!cache.isKnownHost(exampleCom));
|
||||
QVERIFY(!cache.isKnownHost(subDomain));
|
||||
// (1) This will also store the policy:
|
||||
cache.updateKnownHost(exampleCom, validDate, true);
|
||||
QVERIFY(cache.isKnownHost(exampleCom));
|
||||
QVERIFY(cache.isKnownHost(subDomain));
|
||||
}
|
||||
{
|
||||
// Test the policy stored at (1):
|
||||
QHstsCache cache;
|
||||
QHstsStore store(storeDir);
|
||||
cache.setStore(&store);
|
||||
QVERIFY(cache.isKnownHost(exampleCom));
|
||||
QVERIFY(cache.isKnownHost(subDomain));
|
||||
// (2) Remove subdomains:
|
||||
cache.updateKnownHost(exampleCom, validDate, false);
|
||||
QVERIFY(!cache.isKnownHost(subDomain));
|
||||
}
|
||||
{
|
||||
// Test the previous update (2):
|
||||
QHstsCache cache;
|
||||
QHstsStore store(storeDir);
|
||||
cache.setStore(&store);
|
||||
QVERIFY(cache.isKnownHost(exampleCom));
|
||||
QVERIFY(!cache.isKnownHost(subDomain));
|
||||
}
|
||||
{
|
||||
QHstsCache cache;
|
||||
cache.updateKnownHost(subDomain, validDate, false);
|
||||
QVERIFY(cache.isKnownHost(subDomain));
|
||||
QHstsStore store(storeDir);
|
||||
// (3) This should store policy from cache, over old policy from store:
|
||||
cache.setStore(&store);
|
||||
}
|
||||
{
|
||||
// Test that (3) was stored:
|
||||
QHstsCache cache;
|
||||
QHstsStore store(storeDir);
|
||||
cache.setStore(&store);
|
||||
QVERIFY(cache.isKnownHost(subDomain));
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QHsts)
|
||||
|
||||
#include "tst_qhsts.moc"
|
17
tests/auto/network/access/http2/CMakeLists.txt
Normal file
17
tests/auto/network/access/http2/CMakeLists.txt
Normal file
@ -0,0 +1,17 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_http2 Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_http2
|
||||
SOURCES
|
||||
http2srv.cpp http2srv.h
|
||||
tst_http2.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::Network
|
||||
Qt::NetworkPrivate
|
||||
Qt::TestPrivate
|
||||
)
|
34
tests/auto/network/access/http2/certs/fluke.cert
Normal file
34
tests/auto/network/access/http2/certs/fluke.cert
Normal file
@ -0,0 +1,34 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIF6zCCA9OgAwIBAgIUfo9amJtJGWqWE6f+SkAO85zkGr4wDQYJKoZIhvcNAQEL
|
||||
BQAwgYMxCzAJBgNVBAYTAk5PMQ0wCwYDVQQIDARPc2xvMQ0wCwYDVQQHDARPc2xv
|
||||
MRcwFQYDVQQKDA5UaGUgUXQgQ29tcGFueTEMMAoGA1UECwwDUiZEMRIwEAYDVQQD
|
||||
DAlIMiBUZXN0ZXIxGzAZBgkqhkiG9w0BCQEWDG1pbmltaUBxdC5pbzAgFw0yMDEw
|
||||
MjYxMjAxMzFaGA8yMTIwMTAwMjEyMDEzMVowgYMxCzAJBgNVBAYTAk5PMQ0wCwYD
|
||||
VQQIDARPc2xvMQ0wCwYDVQQHDARPc2xvMRcwFQYDVQQKDA5UaGUgUXQgQ29tcGFu
|
||||
eTEMMAoGA1UECwwDUiZEMRIwEAYDVQQDDAlIMiBUZXN0ZXIxGzAZBgkqhkiG9w0B
|
||||
CQEWDG1pbmltaUBxdC5pbzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
|
||||
AOiUp5+E4blouKH7q+rVNR8NoYX2XkBW+q+rpy1zu5ssRSzbqxAjDx9dkht7Qlnf
|
||||
VlDT00JvpOWdeuPon5915edQRsY4Unl6mKH29ra3OtUa1/yCJXsGVJTKCj7k4Bxb
|
||||
5mZzb/fTlZntMLdTIBMfUbw62FKir1WjKIcJ9fCoG8JaGeKVO4Rh5p0ezd4UUUId
|
||||
r1BXl5Nqdqy2vTMsEDnjOsD3egkv8I2SKN4O6n/C3wWYpMOWYZkGoZiKz7rJs/i/
|
||||
ez7bsV7JlwdzTlhpJzkcOSVFBP6JlEOxTNNxZ1wtKy7PtZGmsSSATq2e6+bw38Ae
|
||||
Op0XnzzqcGjtDDofBmT7OFzZWjS9VZS6+DOOe2QHWle1nCHcHyH4ku6IRlsr9xkR
|
||||
NAIlOfnvHHxqJUenoeaZ4oQDjCBKS1KXygJO/tL7BLTQVn/xK1EmPvKNnjzWk4tR
|
||||
PnibUhhs5635qpOU/YPqFBh1JjVruZbsWcDAhRcew0uxONXOa9E+4lttQ9ySYa1A
|
||||
LvWqJuAX7gu2BsBMLyqfm811YnA7CIFMyO+HlqmkLFfv5L/xIRAXR7l26YGO0VwX
|
||||
CGjMfz4NVPMMke4nB7qa9NkpXQBQKMms3Qzd5JW0Hy9Ruj5O8GPcFZmV0twjd1uJ
|
||||
PD/cAjkWLaXjdNsJ16QWc2nghQRS6HYqKRX6j+CXOxupAgMBAAGjUzBRMB0GA1Ud
|
||||
DgQWBBRSCOU58j9NJZkMamt623qyCrhN3TAfBgNVHSMEGDAWgBRSCOU58j9NJZkM
|
||||
amt623qyCrhN3TAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQCq
|
||||
q4jxsWeNDv5Nq14hJtF9HB+ZL64zcZtRjJP1YgNs0QppKICmjPOL2nIMGmI/jKrs
|
||||
0eGAL/9XXNVHPxm1OPOncvimMMmU6emZfpMdEtTfKP43+Pg9HgKRjLoQp406vGeQ
|
||||
8ki/mbBhrItVPgEm3tu2AFA02XTYi+YxCI9kRZLGkM3FbgtOuTLPl0Z9y+kiPc9F
|
||||
uCSC03anBEqv+vDSI8+wODymQ/IJ3Jyz1lxIRDfp4qAekmy0jU2c91VOHHEmOmqq
|
||||
kqygGFRdwbe99m9yP63r6q0b5K3X2UnJ6bns0hmTwThYwpVPXLU8jdaTddbMukN2
|
||||
/Ef96Tsw8nWOEOPMySHOTIPgwyZRp26b0kA9EmhLwOP401SxXVQCmSRmtwNagmtg
|
||||
jJKmZoYBN+//D45ibK8z6Q0oOm9P+Whf/uUXehcRxBxyV3xz7k0wKGQbHj/ddwcy
|
||||
IUoIN4lrAlib+lK170kTKN352PDmrpo2gmIzPEsfurKAIMSelDl6H+kih16BtZ8y
|
||||
Nz6fh9Soqrg3OSAware8pxV7k51crBMoPLN78KoRV8MFCK4K7Fddq4rRISq6hiXq
|
||||
r1nsjoEPuKM9huprmZVZe9t5YcDa2I+wb3IiE3uwpZbAdaLDyQ5n6F/qpsiIkZXn
|
||||
gtcF7oqpG5oYrwCcZ53y/ezUgUg7PlSz2XwAGvQtgg==
|
||||
-----END CERTIFICATE-----
|
52
tests/auto/network/access/http2/certs/fluke.key
Normal file
52
tests/auto/network/access/http2/certs/fluke.key
Normal file
@ -0,0 +1,52 @@
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDolKefhOG5aLih
|
||||
+6vq1TUfDaGF9l5AVvqvq6ctc7ubLEUs26sQIw8fXZIbe0JZ31ZQ09NCb6TlnXrj
|
||||
6J+fdeXnUEbGOFJ5epih9va2tzrVGtf8giV7BlSUygo+5OAcW+Zmc2/305WZ7TC3
|
||||
UyATH1G8OthSoq9VoyiHCfXwqBvCWhnilTuEYeadHs3eFFFCHa9QV5eTanastr0z
|
||||
LBA54zrA93oJL/CNkijeDup/wt8FmKTDlmGZBqGYis+6ybP4v3s+27FeyZcHc05Y
|
||||
aSc5HDklRQT+iZRDsUzTcWdcLSsuz7WRprEkgE6tnuvm8N/AHjqdF5886nBo7Qw6
|
||||
HwZk+zhc2Vo0vVWUuvgzjntkB1pXtZwh3B8h+JLuiEZbK/cZETQCJTn57xx8aiVH
|
||||
p6HmmeKEA4wgSktSl8oCTv7S+wS00FZ/8StRJj7yjZ481pOLUT54m1IYbOet+aqT
|
||||
lP2D6hQYdSY1a7mW7FnAwIUXHsNLsTjVzmvRPuJbbUPckmGtQC71qibgF+4LtgbA
|
||||
TC8qn5vNdWJwOwiBTMjvh5appCxX7+S/8SEQF0e5dumBjtFcFwhozH8+DVTzDJHu
|
||||
Jwe6mvTZKV0AUCjJrN0M3eSVtB8vUbo+TvBj3BWZldLcI3dbiTw/3AI5Fi2l43Tb
|
||||
CdekFnNp4IUEUuh2KikV+o/glzsbqQIDAQABAoICAFw1q6tr5I48vY7DF+rXsuLn
|
||||
5ZUWE1IQ6fzB4lr72nJv/9EEGnMgYzt9PpMUsD6vdCpBgS2C0+6RHArFzJtNA+RM
|
||||
iHLIG7K7702veyr/xBx/MwiSlMeMv/XpkFxVI6E6skMGG2s3AMXxKvJTy5CpRx+I
|
||||
eQFyLG+Ya1X2lgJes/q+/CpAHkOjCOpcLySQC5NZ74q734V7nSdmn+Zs3tYEh+O/
|
||||
eiuwTP/j5b38Te5vVTqDxTciJPmljmXLCwa0N100lWlbcpvw8qbqiTI2Jm3XCbUE
|
||||
AzHjW9vmrF3cRS1fXxKFGShw3SRqlkbxjfeWoi8qDPUBS4m8LOr8qG9Wo5Nfon0z
|
||||
zLP4bci3zHDvVcaaZrrsUBs/yZbg+Dgka1DmX7ekmeccr2yTdKDFgPupYUyxVbTl
|
||||
a9ZLJysjFD7rgBv1ZclHonLp6Vbm+ZoTqvteo4ikAy6L9RtBWJ23XEK34PkP/+c5
|
||||
2vWZaOrnjSeBHbFce8cdJSxqWpP+eSCI5I9XbDrYFIsQ/gqKgtzDKy2ihJ2Y8STL
|
||||
yO4hyFPFjxc+Gg4/P2PpmT5CY2ty44M0BWs+JGW96CJPrrplf2lmQUQJj5LZY66X
|
||||
Z/4C9L7ZYtKZ+bs5SvU46yWugAvQZX22Xm9xLXWyVXRdx3bj+3M3fDnF9di/zdbh
|
||||
CgLx7oWPNrXc7FCajnn9AoIBAQD5FMYwRpw9NWT9WDxQwx+cSI4Icbd88ByTW63S
|
||||
LzeRwZA0J9/SfwO+aBRupzc9GkGXCiZcGMw3AGsCtig8yFlw8E5KnzN7KlftDMnM
|
||||
9NUxxzlR8VwKyLnZfG7sDTl057ZlUujnqhmt/F8F7dIy7FVO1dE/8nngA+FYTCOG
|
||||
UZdGjwyBDlDM0JJdUWGY3xslutcpCDN5mzSTKjy9drMvImAshRawxRF6WBpn7vr2
|
||||
nC6vciqfx1Mzx1vyk0Jm0ilaydDdLMADjt/iL4Nkr0BEs4k+UzQiKDwp8gu7abQ1
|
||||
eBfxd9Iar4htQa2I1Ewl6P01G/q+ZYwgHhJ9RVn4AxQXefILAoIBAQDvCouORdQX
|
||||
C8wsyp7MwXlF/3NQeNN5/+B2mhbxrBOf7PmMCXLnkRWcjwJtzypWFqJ0sqai/2+0
|
||||
bqbMcjX5maT8stT2shl3zXe/Ejt2e3TBYpc1tyuses8Kb5BMU8hu6tTd3G2CMXpD
|
||||
dT6DVemJZCTtwj9aBNIxSizvlgMolJnCpzhPnlfHSI6E+g3m/LTTo3HwbjMSw/Uq
|
||||
irgjOpI2wSBB6LZPSgjvfcYPRyWUk16L4A5uSX0cADnovDFLa5/h0wJvN/OoCSQg
|
||||
rLCXG5E18EyL5Wc58BCY1ZvxmjG3lQtgPxYu2Jwc36R/y/JKlxW5suER5ZNpbbD4
|
||||
uOyTt2VxMQ2bAoIBAQC5+MzRFqdo/AjfL5Y5JrbfVTzXCTDa09xCGd16ZU60QTWN
|
||||
+4ed/r+o1sUKqUcRFB2MzEM/2DQBjQpZB/CbEWvWa1XJWXxypXbowveZU+QqOnmN
|
||||
uQvj8WLyA3o+PNF9e9QvauwCrHpn8VpxbtPWuaYoKnUFreFZZQxHhPGxRBIS2JOZ
|
||||
eDrT8ZaWnkCkh1AZp5smQ71LOprSlmKrg4jd1GjCVMxQR5N5KXbtyv0OTCZ/UFqK
|
||||
2aRBsMPyJgkaBChkZPLRcKwc+/wlQRx1fHQb14DNTApMxoXFO7eOwqmOkpAt9iyl
|
||||
SBIwoS0UUI5ab88+bBmXNvKcuFdNuQ4nowTJUn9pAoIBADMNkILBXSvS5DeIyuO2
|
||||
Sp1tkoZUV+5NfPY3sMDK3KIibaW/+t+EOBZo4L7tKQCb8vRzl21mmsfxfgRaPDbj
|
||||
3r3tv9g0b4YLxxBy52pFscj/soXRai17SS7UZwA2QK+XzgDYbDcLNC6mIsTQG4Gx
|
||||
dsWk3/zs3KuUSQaehmwrWK+fIUK38c1pLK8v7LoxrLkqxlHwZ04RthHw8KTthH7X
|
||||
Pnl1J0LF8CSeOyfWLSuPUfkT0GEzptnNHpEbaHfQM6R6eaGhVJPF6AZme4y6YYgg
|
||||
m2ihhSt1n0XVEWpHYWjxFy3mK2mz75unFC4LM+NEY2p2zuUQoCw7NjnY3QYrfCnx
|
||||
rRMCggEAXeXsMSLFjjyuoL7iKbAxo52HD/P0fBoy58LyRcwfNVr0lvYan4pYEx+o
|
||||
KijIh9K16PqXZXKMA9v003B+ulmF8bJ7SddCZ5NGvnFhUTDe4DdTKgp2RuwQ3Bsc
|
||||
3skPIDbhVETyOLCtys34USHrq8U/0DlGY3eLRfxw9GnbKxSBGa/KEu/qQLPNUo50
|
||||
7xHZDg7GKeC3kqNJeqKM9rkp0VzIGkEnaD9127LeNDmERDfftxJzFoC/THvUBLfU
|
||||
6Sus2ZYwRE8VFvKC30Q45t/c54X3IuhYvAuiCuTmyfE4ruyzyOwKzhUkeeLq1APX
|
||||
g0veFbyfzlJ0q8qzD/iffqqIa2ZSmQ==
|
||||
-----END PRIVATE KEY-----
|
985
tests/auto/network/access/http2/http2srv.cpp
Normal file
985
tests/auto/network/access/http2/http2srv.cpp
Normal file
@ -0,0 +1,985 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include <QtNetwork/private/http2protocol_p.h>
|
||||
#include <QtNetwork/private/bitstreams_p.h>
|
||||
|
||||
#include "http2srv.h"
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
#include <QtNetwork/qsslconfiguration.h>
|
||||
#include <QtNetwork/qsslsocket.h>
|
||||
#include <QtNetwork/qsslkey.h>
|
||||
#endif
|
||||
|
||||
#include <QtNetwork/qtcpsocket.h>
|
||||
|
||||
#include <QtCore/qtimer.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 <= quint32(std::numeric_limits<qint32>::max());
|
||||
}
|
||||
|
||||
void fill_push_header(const HttpHeader &originalRequest, HttpHeader &promisedRequest)
|
||||
{
|
||||
for (const auto &field : originalRequest) {
|
||||
if (field.name == QByteArray(":authority") ||
|
||||
field.name == QByteArray(":scheme")) {
|
||||
promisedRequest.push_back(field);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Http2Server::Http2Server(H2Type type, const RawSettings &ss, const RawSettings &cs)
|
||||
: connectionType(type),
|
||||
serverSettings(ss),
|
||||
expectedClientSettings(cs)
|
||||
{
|
||||
#if !QT_CONFIG(ssl)
|
||||
Q_ASSERT(type != H2Type::h2Alpn && type != H2Type::h2Direct);
|
||||
#endif
|
||||
|
||||
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::enablePushPromise(bool pushEnabled, const QByteArray &path)
|
||||
{
|
||||
pushPromiseEnabled = pushEnabled;
|
||||
pushPath = path;
|
||||
}
|
||||
|
||||
void Http2Server::setResponseBody(const QByteArray &body)
|
||||
{
|
||||
responseBody = body;
|
||||
}
|
||||
|
||||
void Http2Server::setContentEncoding(const QByteArray &encoding)
|
||||
{
|
||||
contentEncoding = encoding;
|
||||
}
|
||||
|
||||
void Http2Server::setAuthenticationHeader(const QByteArray &authentication)
|
||||
{
|
||||
authenticationHeader = authentication;
|
||||
}
|
||||
|
||||
void Http2Server::setRedirect(const QByteArray &url, int count)
|
||||
{
|
||||
redirectUrl = url;
|
||||
redirectCount = count;
|
||||
}
|
||||
|
||||
void Http2Server::setSendTrailingHEADERS(bool enable)
|
||||
{
|
||||
sendTrailingHEADERS = enable;
|
||||
}
|
||||
|
||||
void Http2Server::emulateGOAWAY(int timeout)
|
||||
{
|
||||
Q_ASSERT(timeout >= 0);
|
||||
testingGOAWAY = true;
|
||||
goawayTimeout = timeout;
|
||||
}
|
||||
|
||||
void Http2Server::redirectOpenStream(quint16 port)
|
||||
{
|
||||
redirectWhileReading = true;
|
||||
targetPort = port;
|
||||
}
|
||||
|
||||
bool Http2Server::isClearText() const
|
||||
{
|
||||
return connectionType == H2Type::h2c || connectionType == H2Type::h2cDirect;
|
||||
}
|
||||
|
||||
QByteArray Http2Server::requestAuthorizationHeader()
|
||||
{
|
||||
const auto isAuthHeader = [](const HeaderField &field) {
|
||||
return field.name == "authorization";
|
||||
};
|
||||
const auto requestHeaders = decoder.decodedHeader();
|
||||
const auto authentication =
|
||||
std::find_if(requestHeaders.cbegin(), requestHeaders.cend(), isAuthHeader);
|
||||
return authentication == requestHeaders.cend() ? QByteArray() : authentication->value;
|
||||
}
|
||||
|
||||
void Http2Server::startServer()
|
||||
{
|
||||
if (listen()) {
|
||||
if (isClearText())
|
||||
authority = QStringLiteral("127.0.0.1:%1").arg(serverPort()).toLatin1();
|
||||
emit serverStarted(serverPort());
|
||||
}
|
||||
}
|
||||
|
||||
bool Http2Server::sendProtocolSwitchReply()
|
||||
{
|
||||
Q_ASSERT(socket);
|
||||
Q_ASSERT(connectionType == H2Type::h2c);
|
||||
// The first and the last HTTP/1.1 response we send:
|
||||
const char response[] = "HTTP/1.1 101 Switching Protocols\r\n"
|
||||
"Connection: Upgrade\r\n"
|
||||
"Upgrade: h2c\r\n\r\n";
|
||||
const qint64 size = sizeof response - 1;
|
||||
return socket->write(response, size) == size;
|
||||
}
|
||||
|
||||
void Http2Server::sendServerSettings()
|
||||
{
|
||||
Q_ASSERT(socket);
|
||||
|
||||
if (!serverSettings.size())
|
||||
return;
|
||||
|
||||
writer.start(FrameType::SETTINGS, FrameFlag::EMPTY, connectionStreamID);
|
||||
for (auto it = serverSettings.cbegin(); it != serverSettings.cend(); ++it) {
|
||||
writer.append(it.key());
|
||||
writer.append(it.value());
|
||||
if (it.key() == Settings::INITIAL_WINDOW_SIZE_ID)
|
||||
streamRecvWindowSize = it.value();
|
||||
}
|
||||
writer.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);
|
||||
|
||||
writer.start(FrameType::GOAWAY, FrameFlag::EMPTY, streamID);
|
||||
writer.append(lastStreamID);
|
||||
writer.append(error);
|
||||
writer.write(*socket);
|
||||
}
|
||||
|
||||
void Http2Server::sendRST_STREAM(quint32 streamID, quint32 error)
|
||||
{
|
||||
Q_ASSERT(socket);
|
||||
|
||||
writer.start(FrameType::RST_STREAM, FrameFlag::EMPTY, streamID);
|
||||
writer.append(error);
|
||||
writer.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()));
|
||||
|
||||
quint32 bytesToSend = std::min<quint32>(windowSize, responseBody.size() - offset);
|
||||
quint32 bytesSent = 0;
|
||||
const quint32 frameSizeLimit(clientSetting(Settings::MAX_FRAME_SIZE_ID, Http2::minPayloadLimit));
|
||||
const uchar *src = reinterpret_cast<const uchar *>(responseBody.constData() + offset);
|
||||
const bool last = offset + bytesToSend == quint32(responseBody.size());
|
||||
|
||||
// The payload can significantly exceed frameSizeLimit. Internally, writer
|
||||
// will do needed fragmentation, but if some test failed, there is no need
|
||||
// to wait for writer to send all DATA frames, we check 'interrupted' and
|
||||
// stop early instead.
|
||||
const quint32 framesInChunk = 10;
|
||||
while (bytesToSend) {
|
||||
if (interrupted.loadAcquire())
|
||||
return;
|
||||
const quint32 chunkSize = std::min<quint32>(framesInChunk * frameSizeLimit, bytesToSend);
|
||||
writer.start(FrameType::DATA, FrameFlag::EMPTY, streamID);
|
||||
writer.writeDATA(*socket, frameSizeLimit, src, chunkSize);
|
||||
src += chunkSize;
|
||||
bytesToSend -= chunkSize;
|
||||
bytesSent += chunkSize;
|
||||
if (frameSizeLimit != Http2::minPayloadLimit) {
|
||||
// Our test is probably interested in how many DATA frames were sent.
|
||||
emit sendingData();
|
||||
}
|
||||
}
|
||||
|
||||
if (interrupted.loadAcquire())
|
||||
return;
|
||||
|
||||
if (last) {
|
||||
if (sendTrailingHEADERS) {
|
||||
writer.start(FrameType::HEADERS,
|
||||
FrameFlag::PRIORITY | FrameFlag::END_HEADERS | FrameFlag::END_STREAM, streamID);
|
||||
const quint32 maxFrameSize(clientSetting(Settings::MAX_FRAME_SIZE_ID,
|
||||
Http2::maxPayloadSize));
|
||||
// 5 bytes for PRIORITY data:
|
||||
writer.append(quint32(0)); // streamID 0 (32-bit)
|
||||
writer.append(quint8(0)); // + weight 0 (8-bit)
|
||||
writer.writeHEADERS(*socket, maxFrameSize);
|
||||
} else {
|
||||
writer.start(FrameType::DATA, FrameFlag::END_STREAM, streamID);
|
||||
writer.setPayloadSize(0);
|
||||
writer.write(*socket);
|
||||
}
|
||||
suspendedStreams.erase(it);
|
||||
activeRequests.erase(streamID);
|
||||
|
||||
Q_ASSERT(closedStreams.find(streamID) == closedStreams.end());
|
||||
closedStreams.insert(streamID);
|
||||
} else {
|
||||
it->second += bytesSent;
|
||||
}
|
||||
}
|
||||
|
||||
void Http2Server::sendWINDOW_UPDATE(quint32 streamID, quint32 delta)
|
||||
{
|
||||
Q_ASSERT(socket);
|
||||
|
||||
writer.start(FrameType::WINDOW_UPDATE, FrameFlag::EMPTY, streamID);
|
||||
writer.append(delta);
|
||||
writer.write(*socket);
|
||||
}
|
||||
|
||||
void Http2Server::incomingConnection(qintptr socketDescriptor)
|
||||
{
|
||||
if (isClearText()) {
|
||||
socket.reset(new QTcpSocket);
|
||||
const bool set = socket->setSocketDescriptor(socketDescriptor);
|
||||
Q_ASSERT(set);
|
||||
// Stop listening:
|
||||
close();
|
||||
upgradeProtocol = connectionType == H2Type::h2c;
|
||||
QMetaObject::invokeMethod(this, "connectionEstablished",
|
||||
Qt::QueuedConnection);
|
||||
} else {
|
||||
#if QT_CONFIG(ssl)
|
||||
socket.reset(new QSslSocket);
|
||||
QSslSocket *sslSocket = static_cast<QSslSocket *>(socket.data());
|
||||
|
||||
if (connectionType == H2Type::h2Alpn) {
|
||||
// Add HTTP2 as supported protocol:
|
||||
auto conf = QSslConfiguration::defaultConfiguration();
|
||||
auto protos = conf.allowedNextProtocols();
|
||||
protos.prepend(QSslConfiguration::ALPNProtocolHTTP2);
|
||||
conf.setAllowedNextProtocols(protos);
|
||||
sslSocket->setSslConfiguration(conf);
|
||||
}
|
||||
// SSL-related setup ...
|
||||
sslSocket->setPeerVerifyMode(QSslSocket::VerifyNone);
|
||||
sslSocket->setProtocol(QSsl::TlsV1_2OrLater);
|
||||
connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)),
|
||||
this, SLOT(ignoreErrorSlot()));
|
||||
QFile file(QT_TESTCASE_SOURCEDIR "/certs/fluke.key");
|
||||
file.open(QIODevice::ReadOnly);
|
||||
QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
|
||||
sslSocket->setPrivateKey(key);
|
||||
auto localCert = QSslCertificate::fromPath(QT_TESTCASE_SOURCEDIR "/certs/fluke.cert");
|
||||
sslSocket->setLocalCertificateChain(localCert);
|
||||
sslSocket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState);
|
||||
// Stop listening.
|
||||
close();
|
||||
// Start SSL handshake and ALPN:
|
||||
connect(sslSocket, SIGNAL(encrypted()), this, SLOT(connectionEstablished()));
|
||||
sslSocket->startServerEncryption();
|
||||
#else
|
||||
Q_ASSERT(0);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
quint32 Http2Server::clientSetting(Http2::Settings identifier, quint32 defaultValue)
|
||||
{
|
||||
const auto it = expectedClientSettings.find(identifier);
|
||||
if (it != expectedClientSettings.end())
|
||||
return it.value();
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
bool Http2Server::readMethodLine()
|
||||
{
|
||||
// We know for sure that Qt did the right thing sending us the correct
|
||||
// Request-line with CRLF at the end ...
|
||||
// We're overly simplistic here but all we need to know - the method.
|
||||
while (socket->bytesAvailable()) {
|
||||
char c = 0;
|
||||
if (socket->read(&c, 1) != 1)
|
||||
return false;
|
||||
if (c == '\n' && requestLine.endsWith('\r')) {
|
||||
if (requestLine.startsWith("GET"))
|
||||
requestType = QHttpNetworkRequest::Get;
|
||||
else if (requestLine.startsWith("POST"))
|
||||
requestType = QHttpNetworkRequest::Post;
|
||||
else
|
||||
requestType = QHttpNetworkRequest::Custom; // 'invalid'.
|
||||
requestLine.clear();
|
||||
|
||||
return true;
|
||||
} else {
|
||||
requestLine.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Http2Server::verifyProtocolUpgradeRequest()
|
||||
{
|
||||
Q_ASSERT(protocolUpgradeHandler.data());
|
||||
|
||||
bool connectionOk = false;
|
||||
bool upgradeOk = false;
|
||||
bool settingsOk = false;
|
||||
|
||||
QHttpNetworkReplyPrivate *firstRequestReader = protocolUpgradeHandler->d_func();
|
||||
|
||||
// That's how we append them, that's what I expect to find:
|
||||
for (const auto &header : firstRequestReader->headers()) {
|
||||
if (header.first == "Connection")
|
||||
connectionOk = header.second.contains("Upgrade, HTTP2-Settings");
|
||||
else if (header.first == "Upgrade")
|
||||
upgradeOk = header.second.contains("h2c");
|
||||
else if (header.first == "HTTP2-Settings")
|
||||
settingsOk = true;
|
||||
}
|
||||
|
||||
return connectionOk && upgradeOk && settingsOk;
|
||||
}
|
||||
|
||||
void Http2Server::triggerGOAWAYEmulation()
|
||||
{
|
||||
Q_ASSERT(testingGOAWAY);
|
||||
auto timer = new QTimer(this);
|
||||
timer->setSingleShot(true);
|
||||
connect(timer, &QTimer::timeout, [this]() {
|
||||
sendGOAWAY(quint32(connectionStreamID), quint32(INTERNAL_ERROR), 0);
|
||||
});
|
||||
timer->start(goawayTimeout);
|
||||
}
|
||||
|
||||
void Http2Server::connectionEstablished()
|
||||
{
|
||||
using namespace Http2;
|
||||
|
||||
if (testingGOAWAY && !isClearText())
|
||||
return triggerGOAWAYEmulation();
|
||||
|
||||
// For clearTextHTTP2 we first have to respond with 'protocol switch'
|
||||
// and then continue with whatever logic we have (testingGOAWAY or not),
|
||||
// otherwise our 'peer' cannot process HTTP/2 frames yet.
|
||||
|
||||
connect(socket.data(), SIGNAL(readyRead()),
|
||||
this, SLOT(readReady()));
|
||||
|
||||
waitingClientPreface = true;
|
||||
waitingClientAck = false;
|
||||
waitingClientSettings = false;
|
||||
settingsSent = false;
|
||||
|
||||
if (connectionType == H2Type::h2c) {
|
||||
requestLine.clear();
|
||||
// Now we have to handle HTTP/1.1 request. We use Get/Post in our test,
|
||||
// so set requestType to something unsupported:
|
||||
requestType = QHttpNetworkRequest::Options;
|
||||
} else {
|
||||
// We immediately send our settings so that our client
|
||||
// can use flow control correctly.
|
||||
sendServerSettings();
|
||||
}
|
||||
|
||||
if (socket->bytesAvailable())
|
||||
readReady();
|
||||
}
|
||||
|
||||
void Http2Server::ignoreErrorSlot()
|
||||
{
|
||||
#ifndef QT_NO_SSL
|
||||
static_cast<QSslSocket *>(socket.data())->ignoreSslErrors();
|
||||
#endif
|
||||
}
|
||||
|
||||
// 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 (redirectSent) {
|
||||
// We are a "single shot" server, working in 'h2' mode,
|
||||
// responding with a redirect code. Don't bother to handle
|
||||
// anything else now.
|
||||
return;
|
||||
}
|
||||
|
||||
if (upgradeProtocol) {
|
||||
handleProtocolUpgrade();
|
||||
} else if (waitingClientPreface) {
|
||||
handleConnectionPreface();
|
||||
} else {
|
||||
const auto status = reader.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::handleProtocolUpgrade()
|
||||
{
|
||||
using ReplyPrivate = QHttpNetworkReplyPrivate;
|
||||
Q_ASSERT(upgradeProtocol);
|
||||
|
||||
if (!protocolUpgradeHandler.data())
|
||||
protocolUpgradeHandler.reset(new Http11Reply);
|
||||
|
||||
QHttpNetworkReplyPrivate *firstRequestReader = protocolUpgradeHandler->d_func();
|
||||
|
||||
// QHttpNetworkReplyPrivate parses ... reply. It will, unfortunately, fail
|
||||
// on the first line ... which is a part of request. So we read this line
|
||||
// and extract the method first.
|
||||
if (firstRequestReader->state == ReplyPrivate::NothingDoneState) {
|
||||
if (!readMethodLine())
|
||||
return;
|
||||
|
||||
if (requestType != QHttpNetworkRequest::Get && requestType != QHttpNetworkRequest::Post) {
|
||||
emit invalidRequest(1);
|
||||
return;
|
||||
}
|
||||
|
||||
firstRequestReader->state = ReplyPrivate::ReadingHeaderState;
|
||||
}
|
||||
|
||||
if (!socket->bytesAvailable())
|
||||
return;
|
||||
|
||||
if (firstRequestReader->state == ReplyPrivate::ReadingHeaderState)
|
||||
firstRequestReader->readHeader(socket.data());
|
||||
else if (firstRequestReader->state == ReplyPrivate::ReadingDataState)
|
||||
firstRequestReader->readBodyFast(socket.data(), &firstRequestReader->responseData);
|
||||
|
||||
switch (firstRequestReader->state) {
|
||||
case ReplyPrivate::ReadingHeaderState:
|
||||
return;
|
||||
case ReplyPrivate::ReadingDataState:
|
||||
if (requestType == QHttpNetworkRequest::Post)
|
||||
return;
|
||||
break;
|
||||
case ReplyPrivate::AllDoneState:
|
||||
break;
|
||||
default:
|
||||
socket->close();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!verifyProtocolUpgradeRequest() || !sendProtocolSwitchReply()) {
|
||||
socket->close();
|
||||
return;
|
||||
}
|
||||
|
||||
upgradeProtocol = false;
|
||||
protocolUpgradeHandler.reset(nullptr);
|
||||
|
||||
if (testingGOAWAY)
|
||||
return triggerGOAWAYEmulation();
|
||||
|
||||
// HTTP/1.1 'fields' we have in firstRequestRead are useless (they are not
|
||||
// even allowed in HTTP/2 header). Let's pretend we have received
|
||||
// valid HTTP/2 headers and can extract fields we need:
|
||||
HttpHeader h2header;
|
||||
h2header.push_back(HeaderField(":scheme", "http")); // we are in clearTextHTTP2 mode.
|
||||
h2header.push_back(HeaderField(":authority", authority));
|
||||
activeRequests[1] = std::move(h2header);
|
||||
// After protocol switch we immediately send our SETTINGS.
|
||||
sendServerSettings();
|
||||
if (requestType == QHttpNetworkRequest::Get)
|
||||
emit receivedRequest(1);
|
||||
else
|
||||
emit receivedData(1);
|
||||
}
|
||||
|
||||
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 (testingGOAWAY) {
|
||||
// GOAWAY test is simplistic for now: after HTTP/2 was
|
||||
// negotiated (via ALPN/NPN or a protocol switch), send
|
||||
// a GOAWAY frame after some (probably non-zero) timeout.
|
||||
// We do not handle any frames, but timeout gives QNAM
|
||||
// more time to initiate more streams and thus make the
|
||||
// test more interesting/complex (on a client side).
|
||||
return;
|
||||
}
|
||||
|
||||
inboundFrame = std::move(reader.inboundFrame());
|
||||
|
||||
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 = Http2::Settings(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:
|
||||
writer.start(FrameType::SETTINGS, FrameFlag::ACK, connectionStreamID);
|
||||
writer.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;
|
||||
}
|
||||
|
||||
const auto payloadSize = inboundFrame.payloadSize();
|
||||
if (sessionCurrRecvWindow < 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 < payloadSize) {
|
||||
emit invalidRequest(streamID);
|
||||
connectionError = true;
|
||||
sendGOAWAY(connectionStreamID, FLOW_CONTROL_ERROR, connectionStreamID);
|
||||
return;
|
||||
}
|
||||
|
||||
it->second -= payloadSize;
|
||||
if (it->second < streamRecvWindowSize / 2) {
|
||||
sendWINDOW_UPDATE(streamID, streamRecvWindowSize / 2);
|
||||
it->second += streamRecvWindowSize / 2;
|
||||
}
|
||||
|
||||
sessionCurrRecvWindow -= 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)) {
|
||||
if (responseBody.isEmpty()) {
|
||||
closedStreams.insert(streamID); // Enter "half-closed remote" state.
|
||||
streamWindows.erase(it);
|
||||
}
|
||||
emit receivedData(streamID);
|
||||
}
|
||||
emit receivedDATAFrame(streamID,
|
||||
QByteArray(reinterpret_cast<const char *>(inboundFrame.dataBegin()),
|
||||
inboundFrame.dataSize()));
|
||||
}
|
||||
|
||||
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());
|
||||
|
||||
const quint32 maxFrameSize(clientSetting(Settings::MAX_FRAME_SIZE_ID,
|
||||
Http2::maxPayloadSize));
|
||||
|
||||
if (pushPromiseEnabled) {
|
||||
// A real server supporting PUSH_PROMISE will probably first send
|
||||
// PUSH_PROMISE and then a normal response (to a real request),
|
||||
// so that a client parsing this response and discovering another
|
||||
// resource it needs, will _already_ have this additional resource
|
||||
// in PUSH_PROMISE.
|
||||
lastPromisedStream += 2;
|
||||
|
||||
writer.start(FrameType::PUSH_PROMISE, FrameFlag::END_HEADERS, streamID);
|
||||
writer.append(lastPromisedStream);
|
||||
|
||||
HttpHeader pushHeader;
|
||||
fill_push_header(activeRequests[streamID], pushHeader);
|
||||
pushHeader.push_back(HeaderField(":method", "GET"));
|
||||
pushHeader.push_back(HeaderField(":path", pushPath));
|
||||
|
||||
// Now interesting part, let's make it into 'stream':
|
||||
activeRequests[lastPromisedStream] = pushHeader;
|
||||
|
||||
HPack::BitOStream ostream(writer.outboundFrame().buffer);
|
||||
const bool result = encoder.encodeRequest(ostream, pushHeader);
|
||||
Q_ASSERT(result);
|
||||
|
||||
// Well, it's not HEADERS, it's PUSH_PROMISE with ... HEADERS block.
|
||||
// Should work.
|
||||
writer.writeHEADERS(*socket, maxFrameSize);
|
||||
qDebug() << "server sent a PUSH_PROMISE on" << lastPromisedStream;
|
||||
|
||||
if (responseBody.isEmpty())
|
||||
responseBody = QByteArray("I PROMISE (AND PUSH) YOU ...");
|
||||
|
||||
// Now we send this promised data as a normal response on our reserved
|
||||
// stream (disabling PUSH_PROMISE for the moment to avoid recursion):
|
||||
pushPromiseEnabled = false;
|
||||
sendResponse(lastPromisedStream, false);
|
||||
pushPromiseEnabled = true;
|
||||
// Now we'll continue with _normal_ response.
|
||||
}
|
||||
|
||||
writer.start(FrameType::HEADERS, FrameFlag::END_HEADERS, streamID);
|
||||
if (emptyBody)
|
||||
writer.addFlag(FrameFlag::END_STREAM);
|
||||
|
||||
// We assume any auth is correct. Leaves the checking to the test itself
|
||||
const bool hasAuth = !requestAuthorizationHeader().isEmpty();
|
||||
|
||||
HttpHeader header;
|
||||
if (redirectWhileReading) {
|
||||
if (redirectSent) {
|
||||
// This is a "single-shot" server responding with a redirect code.
|
||||
return;
|
||||
}
|
||||
|
||||
redirectSent = true;
|
||||
|
||||
qDebug("server received HEADERS frame (followed by DATA frames), redirecting ...");
|
||||
Q_ASSERT(targetPort);
|
||||
header.push_back({":status", "308"});
|
||||
const QString url("%1://localhost:%2/");
|
||||
header.push_back({"location", url.arg(isClearText() ? QStringLiteral("http") : QStringLiteral("https"),
|
||||
QString::number(targetPort)).toLatin1()});
|
||||
} else if (redirectCount > 0) { // Not redirecting while reading, unlike above
|
||||
--redirectCount;
|
||||
header.push_back({":status", "308"});
|
||||
header.push_back({"location", redirectUrl});
|
||||
} else if (!authenticationHeader.isEmpty() && !hasAuth) {
|
||||
header.push_back({ ":status", "401" });
|
||||
header.push_back(HPack::HeaderField("www-authenticate", authenticationHeader));
|
||||
authenticationHeader.clear();
|
||||
} else {
|
||||
header.push_back({":status", "200"});
|
||||
}
|
||||
|
||||
if (!emptyBody) {
|
||||
header.push_back(HPack::HeaderField("content-length",
|
||||
QString("%1").arg(responseBody.size()).toLatin1()));
|
||||
}
|
||||
|
||||
if (!contentEncoding.isEmpty())
|
||||
header.push_back(HPack::HeaderField("content-encoding", contentEncoding));
|
||||
|
||||
HPack::BitOStream ostream(writer.outboundFrame().buffer);
|
||||
const bool result = encoder.encodeResponse(ostream, header);
|
||||
Q_ASSERT(result);
|
||||
|
||||
writer.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::stopSendingDATAFrames()
|
||||
{
|
||||
interrupted.storeRelease(1);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Actually, if needed, we can do a comparison here.
|
||||
activeRequests[streamID] = decoder.decodedHeader();
|
||||
if (headersFrame.flags().testFlag(FrameFlag::END_STREAM))
|
||||
emit receivedRequest(streamID);
|
||||
|
||||
if (redirectWhileReading) {
|
||||
sendResponse(streamID, true);
|
||||
// Don't try to read any DATA frames ...
|
||||
socket->disconnect();
|
||||
} // else - we're waiting for incoming DATA frames ...
|
||||
|
||||
continuedRequest.clear();
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
217
tests/auto/network/access/http2/http2srv.h
Normal file
217
tests/auto/network/access/http2/http2srv.h
Normal file
@ -0,0 +1,217 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#ifndef HTTP2SRV_H
|
||||
#define HTTP2SRV_H
|
||||
|
||||
#include <QtNetwork/private/qhttpnetworkrequest_p.h>
|
||||
#include <QtNetwork/private/qhttpnetworkreply_p.h>
|
||||
#include <QtNetwork/private/http2protocol_p.h>
|
||||
#include <QtNetwork/private/http2frames_p.h>
|
||||
#include <QtNetwork/private/hpack_p.h>
|
||||
|
||||
#include <QtNetwork/qabstractsocket.h>
|
||||
#include <QtCore/qsharedpointer.h>
|
||||
#include <QtCore/qscopedpointer.h>
|
||||
#include <QtNetwork/qtcpserver.h>
|
||||
#include <QtCore/qbytearray.h>
|
||||
#include <QtCore/qatomic.h>
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <QtCore/qmap.h>
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
// At the moment we do not have any public API parsing HTTP headers. Even worse -
|
||||
// the code that can do this exists only in QHttpNetworkReplyPrivate class.
|
||||
// To be able to access reply's d_func() we have these classes:
|
||||
class Http11ReplyPrivate : public QHttpNetworkReplyPrivate
|
||||
{
|
||||
};
|
||||
|
||||
class Http11Reply : public QHttpNetworkReply
|
||||
{
|
||||
public:
|
||||
Q_DECLARE_PRIVATE(Http11Reply)
|
||||
};
|
||||
|
||||
enum class H2Type {
|
||||
h2Alpn, // Secure connection, ALPN to negotiate h2.
|
||||
h2c, // Clear text with protocol upgrade.
|
||||
h2Direct, // Secure connection, ALPN not supported.
|
||||
h2cDirect, // Clear text direct
|
||||
};
|
||||
|
||||
using RawSettings = QMap<Http2::Settings, quint32>;
|
||||
|
||||
class Http2Server : public QTcpServer
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
Http2Server(H2Type type, const RawSettings &serverSettings,
|
||||
const RawSettings &clientSettings);
|
||||
|
||||
~Http2Server();
|
||||
|
||||
|
||||
// To be called before server started:
|
||||
void enablePushPromise(bool enabled, const QByteArray &path = QByteArray());
|
||||
void setResponseBody(const QByteArray &body);
|
||||
// No content encoding is actually performed, call setResponseBody with already encoded data
|
||||
void setContentEncoding(const QByteArray &contentEncoding);
|
||||
// No authentication data is generated for the method, the full header value must be set
|
||||
void setAuthenticationHeader(const QByteArray &authentication);
|
||||
// Set the redirect URL and count. The server will return a redirect response with the url
|
||||
// 'count' amount of times
|
||||
void setRedirect(const QByteArray &redirectUrl, int count);
|
||||
// Send a trailing HEADERS frame with PRIORITY and END_STREAM flag
|
||||
void setSendTrailingHEADERS(bool enable);
|
||||
void emulateGOAWAY(int timeout);
|
||||
void redirectOpenStream(quint16 targetPort);
|
||||
|
||||
bool isClearText() const;
|
||||
|
||||
QByteArray requestAuthorizationHeader();
|
||||
|
||||
// Invokables, since we can call them from the main thread,
|
||||
// but server (can) work on its own thread.
|
||||
Q_INVOKABLE void startServer();
|
||||
bool sendProtocolSwitchReply();
|
||||
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 handleProtocolUpgrade();
|
||||
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);
|
||||
|
||||
void stopSendingDATAFrames();
|
||||
|
||||
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);
|
||||
// Emitted for every DATA frame. Includes the content of the frame as \a body.
|
||||
void receivedDATAFrame(quint32 streamID, const QByteArray &body);
|
||||
void windowUpdate(quint32 streamID);
|
||||
void sendingData();
|
||||
|
||||
private slots:
|
||||
void connectionEstablished();
|
||||
void readReady();
|
||||
|
||||
private:
|
||||
void incomingConnection(qintptr socketDescriptor) override;
|
||||
|
||||
quint32 clientSetting(Http2::Settings identifier, quint32 defaultValue);
|
||||
bool readMethodLine();
|
||||
bool verifyProtocolUpgradeRequest();
|
||||
void triggerGOAWAYEmulation();
|
||||
|
||||
QScopedPointer<QAbstractSocket> socket;
|
||||
|
||||
H2Type connectionType = H2Type::h2Alpn;
|
||||
// Connection preface:
|
||||
bool waitingClientPreface = false;
|
||||
bool waitingClientSettings = false;
|
||||
bool settingsSent = false;
|
||||
bool waitingClientAck = false;
|
||||
|
||||
RawSettings serverSettings;
|
||||
RawSettings expectedClientSettings;
|
||||
|
||||
bool connectionError = false;
|
||||
|
||||
Http2::FrameReader reader;
|
||||
Http2::Frame inboundFrame;
|
||||
Http2::FrameWriter writer;
|
||||
|
||||
using FrameSequence = std::vector<Http2::Frame>;
|
||||
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;
|
||||
bool pushPromiseEnabled = false;
|
||||
quint32 lastPromisedStream = 0;
|
||||
QByteArray pushPath;
|
||||
|
||||
bool testingGOAWAY = false;
|
||||
int goawayTimeout = 0;
|
||||
|
||||
// Clear text HTTP/2, we have to deal with the protocol upgrade request
|
||||
// from the initial HTTP/1.1 request.
|
||||
bool upgradeProtocol = false;
|
||||
QByteArray requestLine;
|
||||
QHttpNetworkRequest::Operation requestType;
|
||||
// We need QHttpNetworkReply (actually its private d-object) to handle the
|
||||
// first HTTP/1.1 request. QHttpNetworkReplyPrivate does parsing + in case
|
||||
// of POST it is also reading the body for us.
|
||||
QScopedPointer<Http11Reply> protocolUpgradeHandler;
|
||||
// We need it for PUSH_PROMISE, with the correct port number appended,
|
||||
// when replying to essentially 1.1 request.
|
||||
QByteArray authority;
|
||||
// Redirect, with status code 308, as soon as we've seen headers, while client
|
||||
// may still be sending DATA frames. See tst_Http2::earlyResponse().
|
||||
bool redirectWhileReading = false;
|
||||
bool redirectSent = false;
|
||||
quint16 targetPort = 0;
|
||||
QAtomicInt interrupted;
|
||||
|
||||
QByteArray contentEncoding;
|
||||
QByteArray authenticationHeader;
|
||||
|
||||
QByteArray redirectUrl;
|
||||
int redirectCount = 0;
|
||||
|
||||
bool sendTrailingHEADERS = false;
|
||||
protected slots:
|
||||
void ignoreErrorSlot();
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
|
1527
tests/auto/network/access/http2/tst_http2.cpp
Normal file
1527
tests/auto/network/access/http2/tst_http2.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,23 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qabstractnetworkcache Test:
|
||||
#####################################################################
|
||||
|
||||
# Collect test data
|
||||
file(GLOB_RECURSE test_data_glob
|
||||
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
tests/*)
|
||||
list(APPEND test_data ${test_data_glob})
|
||||
|
||||
qt_internal_add_test(tst_qabstractnetworkcache
|
||||
SOURCES
|
||||
tst_qabstractnetworkcache.cpp
|
||||
LIBRARIES
|
||||
Qt::Network
|
||||
TESTDATA ${test_data}
|
||||
QT_TEST_SERVER_LIST "apache2"
|
||||
)
|
||||
|
||||
# QT_TEST_SERVER_LIST = "apache2"
|
@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
# cache control takes precedence over expires
|
||||
echo "Cache-Control: max-age=-1"
|
||||
echo "Expires: Mon, 30 Oct 2028 14:19:41 GMT"
|
||||
echo "Content-type: text/html";
|
||||
echo ""
|
||||
echo "Hello World!"
|
@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
if [ ! -z ${HTTP_IF_MODIFIED_SINCE} ] ; then
|
||||
echo "Status: 304"
|
||||
echo ""
|
||||
exit;
|
||||
fi
|
||||
|
||||
cc=`echo "${QUERY_STRING}" | sed -e s/%20/\ /g`
|
||||
echo "Cache-Control: $cc"
|
||||
echo "Last-Modified: Sat, 31 Oct 1981 06:00:00 GMT"
|
||||
echo "Content-type: text/html";
|
||||
echo ""
|
||||
echo "Hello World!"
|
@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
cc=`echo "${QUERY_STRING}" | sed -e s/%20/\ /g`
|
||||
echo "Status: 200"
|
||||
echo "Cache-Control: $cc"
|
||||
echo "Last-Modified: Sat, 31 Oct 1981 06:00:00 GMT"
|
||||
echo "Content-type: text/html";
|
||||
echo "X-Script: $0"
|
||||
echo ""
|
||||
echo "Hello World!"
|
@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
echo "ETag: foo"
|
||||
echo "Content-type: text/html";
|
||||
echo ""
|
||||
echo "Hello World!"
|
@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
if [ ! -z ${HTTP_IF_NONE_MATCH} ] ; then
|
||||
echo "Status: 304"
|
||||
echo ""
|
||||
exit;
|
||||
fi
|
||||
|
||||
echo "ETag: foo"
|
||||
echo "Content-type: text/html";
|
||||
echo ""
|
||||
echo "Hello World!"
|
@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
echo "Expires: Sat, 31 Oct 1981 6:00:00 GMT"
|
||||
echo "Content-type: text/html";
|
||||
echo ""
|
||||
echo "Hello World!"
|
@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
if [ ${HTTP_IF_MODIFIED_SINCE} == "Mon, 30 Oct 2028 14:19:41 GMT" ] ; then
|
||||
echo "Status: 304"
|
||||
echo ""
|
||||
exit;
|
||||
fi
|
||||
|
||||
echo "Expires: Mon, 30 Oct 2028 14:19:41 GMT"
|
||||
echo "Content-type: text/html";
|
||||
echo ""
|
||||
echo "Hello World!"
|
@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
if [ ! -z ${HTTP_IF_MODIFIED_SINCE} ] ; then
|
||||
echo "Status: 500"
|
||||
echo ""
|
||||
exit;
|
||||
fi
|
||||
|
||||
echo "Expires: Mon, 30 Oct 2028 14:19:41 GMT"
|
||||
echo "Content-type: text/html";
|
||||
echo ""
|
||||
echo "Hello World!"
|
@ -0,0 +1,5 @@
|
||||
#!/bin/bash
|
||||
echo "Last-Modified: Sat, 31 Oct 1981 6:00:00 GMT"
|
||||
echo "Content-type: text/html";
|
||||
echo ""
|
||||
echo "Hello World!"
|
@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
if [ ${HTTP_IF_MODIFIED_SINCE} == "Sat, 31 Oct 1981 06:00:00 GMT" ] ; then
|
||||
echo "Status: 304"
|
||||
echo ""
|
||||
exit;
|
||||
fi
|
||||
|
||||
echo "Last-Modified: Sat, 31 Oct 1981 06:00:00 GMT"
|
||||
echo "Content-type: text/html";
|
||||
echo ""
|
||||
echo "Hello World!"
|
@ -0,0 +1,372 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QTemporaryDir>
|
||||
#include <QTest>
|
||||
#include <QtNetwork/QtNetwork>
|
||||
#include <QSignalSpy>
|
||||
|
||||
#include "../../../network-settings.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#define TESTFILE QLatin1String("http://") + QtNetworkSettings::httpServerName() + QLatin1String("/qtest/cgi-bin/")
|
||||
|
||||
class tst_QAbstractNetworkCache : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
tst_QAbstractNetworkCache();
|
||||
virtual ~tst_QAbstractNetworkCache();
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void expires_data();
|
||||
void expires();
|
||||
void expiresSynchronous_data();
|
||||
void expiresSynchronous();
|
||||
|
||||
void lastModified_data();
|
||||
void lastModified();
|
||||
void lastModifiedSynchronous_data();
|
||||
void lastModifiedSynchronous();
|
||||
|
||||
void etag_data();
|
||||
void etag();
|
||||
void etagSynchronous_data();
|
||||
void etagSynchronous();
|
||||
|
||||
void cacheControl_data();
|
||||
void cacheControl();
|
||||
void cacheControlSynchronous_data();
|
||||
void cacheControlSynchronous();
|
||||
|
||||
void deleteCache();
|
||||
|
||||
private:
|
||||
void runTest();
|
||||
void checkSynchronous();
|
||||
|
||||
};
|
||||
|
||||
class NetworkDiskCache : public QNetworkDiskCache
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
NetworkDiskCache(QObject *parent = nullptr)
|
||||
: QNetworkDiskCache(parent)
|
||||
, tempDir(QDir::tempPath() + QLatin1String("/tst_qabstractnetworkcache.XXXXXX"))
|
||||
, gotData(false)
|
||||
{
|
||||
setCacheDirectory(tempDir.path());
|
||||
clear();
|
||||
}
|
||||
|
||||
QIODevice *data(const QUrl &url) override
|
||||
{
|
||||
gotData = true;
|
||||
return QNetworkDiskCache::data(url);
|
||||
}
|
||||
|
||||
QTemporaryDir tempDir;
|
||||
bool gotData;
|
||||
};
|
||||
|
||||
|
||||
tst_QAbstractNetworkCache::tst_QAbstractNetworkCache()
|
||||
{
|
||||
QCoreApplication::setOrganizationName(QLatin1String("QtProject"));
|
||||
QCoreApplication::setApplicationName(QLatin1String("autotest_qabstractnetworkcache"));
|
||||
QCoreApplication::setApplicationVersion(QLatin1String("1.0"));
|
||||
}
|
||||
|
||||
tst_QAbstractNetworkCache::~tst_QAbstractNetworkCache()
|
||||
{
|
||||
}
|
||||
|
||||
static bool AlwaysTrue = true;
|
||||
static bool AlwaysFalse = false;
|
||||
|
||||
Q_DECLARE_METATYPE(QNetworkRequest::CacheLoadControl)
|
||||
|
||||
|
||||
void tst_QAbstractNetworkCache::initTestCase()
|
||||
{
|
||||
#if defined(QT_TEST_SERVER)
|
||||
QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpServerName(), 80));
|
||||
#else
|
||||
if (!QtNetworkSettings::verifyTestNetworkSettings())
|
||||
QSKIP("No network test server available");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void tst_QAbstractNetworkCache::expires_data()
|
||||
{
|
||||
QTest::addColumn<QNetworkRequest::CacheLoadControl>("cacheLoadControl");
|
||||
QTest::addColumn<QString>("url");
|
||||
QTest::addColumn<bool>("fetchFromCache");
|
||||
|
||||
QTest::newRow("304-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_expires304.cgi" << AlwaysFalse;
|
||||
QTest::newRow("304-1") << QNetworkRequest::PreferNetwork << "httpcachetest_expires304.cgi" << true;
|
||||
QTest::newRow("304-2") << QNetworkRequest::AlwaysCache << "httpcachetest_expires304.cgi" << AlwaysTrue;
|
||||
QTest::newRow("304-3") << QNetworkRequest::PreferCache << "httpcachetest_expires304.cgi" << true;
|
||||
|
||||
QTest::newRow("500-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_expires500.cgi" << AlwaysFalse;
|
||||
QTest::newRow("500-1") << QNetworkRequest::PreferNetwork << "httpcachetest_expires500.cgi" << true;
|
||||
QTest::newRow("500-2") << QNetworkRequest::AlwaysCache << "httpcachetest_expires500.cgi" << AlwaysTrue;
|
||||
QTest::newRow("500-3") << QNetworkRequest::PreferCache << "httpcachetest_expires500.cgi" << true;
|
||||
|
||||
QTest::newRow("200-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_expires200.cgi" << AlwaysFalse;
|
||||
QTest::newRow("200-1") << QNetworkRequest::PreferNetwork << "httpcachetest_expires200.cgi" << false;
|
||||
QTest::newRow("200-2") << QNetworkRequest::AlwaysCache << "httpcachetest_expires200.cgi" << AlwaysTrue;
|
||||
QTest::newRow("200-3") << QNetworkRequest::PreferCache << "httpcachetest_expires200.cgi" << false;
|
||||
}
|
||||
|
||||
void tst_QAbstractNetworkCache::expires()
|
||||
{
|
||||
runTest();
|
||||
}
|
||||
|
||||
void tst_QAbstractNetworkCache::expiresSynchronous_data()
|
||||
{
|
||||
expires_data();
|
||||
}
|
||||
|
||||
void tst_QAbstractNetworkCache::expiresSynchronous()
|
||||
{
|
||||
checkSynchronous();
|
||||
}
|
||||
|
||||
void tst_QAbstractNetworkCache::lastModified_data()
|
||||
{
|
||||
QTest::addColumn<QNetworkRequest::CacheLoadControl>("cacheLoadControl");
|
||||
QTest::addColumn<QString>("url");
|
||||
QTest::addColumn<bool>("fetchFromCache");
|
||||
|
||||
QTest::newRow("304-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_lastModified304.cgi" << AlwaysFalse;
|
||||
QTest::newRow("304-1") << QNetworkRequest::PreferNetwork << "httpcachetest_lastModified304.cgi" << true;
|
||||
QTest::newRow("304-2") << QNetworkRequest::AlwaysCache << "httpcachetest_lastModified304.cgi" << AlwaysTrue;
|
||||
QTest::newRow("304-3") << QNetworkRequest::PreferCache << "httpcachetest_lastModified304.cgi" << true;
|
||||
|
||||
QTest::newRow("200-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_lastModified200.cgi" << AlwaysFalse;
|
||||
QTest::newRow("200-1") << QNetworkRequest::PreferNetwork << "httpcachetest_lastModified200.cgi" << true;
|
||||
QTest::newRow("200-2") << QNetworkRequest::AlwaysCache << "httpcachetest_lastModified200.cgi" << AlwaysTrue;
|
||||
QTest::newRow("200-3") << QNetworkRequest::PreferCache << "httpcachetest_lastModified200.cgi" << true;
|
||||
}
|
||||
|
||||
void tst_QAbstractNetworkCache::lastModified()
|
||||
{
|
||||
runTest();
|
||||
}
|
||||
|
||||
void tst_QAbstractNetworkCache::lastModifiedSynchronous_data()
|
||||
{
|
||||
tst_QAbstractNetworkCache::lastModified_data();
|
||||
}
|
||||
|
||||
void tst_QAbstractNetworkCache::lastModifiedSynchronous()
|
||||
{
|
||||
checkSynchronous();
|
||||
}
|
||||
|
||||
void tst_QAbstractNetworkCache::etag_data()
|
||||
{
|
||||
QTest::addColumn<QNetworkRequest::CacheLoadControl>("cacheLoadControl");
|
||||
QTest::addColumn<QString>("url");
|
||||
QTest::addColumn<bool>("fetchFromCache");
|
||||
|
||||
QTest::newRow("304-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_etag304.cgi" << AlwaysFalse;
|
||||
QTest::newRow("304-1") << QNetworkRequest::PreferNetwork << "httpcachetest_etag304.cgi" << true;
|
||||
QTest::newRow("304-2") << QNetworkRequest::AlwaysCache << "httpcachetest_etag304.cgi" << AlwaysTrue;
|
||||
QTest::newRow("304-3") << QNetworkRequest::PreferCache << "httpcachetest_etag304.cgi" << true;
|
||||
|
||||
QTest::newRow("200-0") << QNetworkRequest::AlwaysNetwork << "httpcachetest_etag200.cgi" << AlwaysFalse;
|
||||
QTest::newRow("200-1") << QNetworkRequest::PreferNetwork << "httpcachetest_etag200.cgi" << false;
|
||||
QTest::newRow("200-2") << QNetworkRequest::AlwaysCache << "httpcachetest_etag200.cgi" << AlwaysTrue;
|
||||
QTest::newRow("200-3") << QNetworkRequest::PreferCache << "httpcachetest_etag200.cgi" << false;
|
||||
}
|
||||
|
||||
void tst_QAbstractNetworkCache::etag()
|
||||
{
|
||||
runTest();
|
||||
}
|
||||
|
||||
void tst_QAbstractNetworkCache::etagSynchronous_data()
|
||||
{
|
||||
tst_QAbstractNetworkCache::etag_data();
|
||||
}
|
||||
|
||||
void tst_QAbstractNetworkCache::etagSynchronous()
|
||||
{
|
||||
checkSynchronous();
|
||||
}
|
||||
|
||||
void tst_QAbstractNetworkCache::cacheControl_data()
|
||||
{
|
||||
QTest::addColumn<QNetworkRequest::CacheLoadControl>("cacheLoadControl");
|
||||
QTest::addColumn<QString>("url");
|
||||
QTest::addColumn<bool>("fetchFromCache");
|
||||
QTest::newRow("200-0") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol.cgi?max-age=-1" << true;
|
||||
QTest::newRow("200-1") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol-expire.cgi" << false;
|
||||
|
||||
QTest::newRow("200-2") << QNetworkRequest::AlwaysNetwork << "httpcachetest_cachecontrol.cgi?no-cache" << AlwaysFalse;
|
||||
QTest::newRow("200-3") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol.cgi?no-cache" << true;
|
||||
QTest::newRow("200-4") << QNetworkRequest::AlwaysCache << "httpcachetest_cachecontrol.cgi?no-cache" << false;
|
||||
QTest::newRow("200-5") << QNetworkRequest::PreferCache << "httpcachetest_cachecontrol.cgi?no-cache" << true;
|
||||
|
||||
QTest::newRow("200-6") << QNetworkRequest::AlwaysNetwork << "httpcachetest_cachecontrol.cgi?no-store" << AlwaysFalse;
|
||||
QTest::newRow("200-7") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol.cgi?no-store" << false;
|
||||
QTest::newRow("200-8") << QNetworkRequest::AlwaysCache << "httpcachetest_cachecontrol.cgi?no-store" << false;
|
||||
QTest::newRow("200-9") << QNetworkRequest::PreferCache << "httpcachetest_cachecontrol.cgi?no-store" << false;
|
||||
|
||||
QTest::newRow("304-0") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol.cgi?max-age=1000" << true;
|
||||
|
||||
QTest::newRow("304-1") << QNetworkRequest::AlwaysNetwork << "httpcachetest_cachecontrol.cgi?max-age=1000, must-revalidate" << AlwaysFalse;
|
||||
QTest::newRow("304-2") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol.cgi?max-age=1000, must-revalidate" << true;
|
||||
QTest::newRow("304-3") << QNetworkRequest::AlwaysCache << "httpcachetest_cachecontrol.cgi?max-age=1000, must-revalidate" << false;
|
||||
QTest::newRow("304-4") << QNetworkRequest::PreferCache << "httpcachetest_cachecontrol.cgi?max-age=1000, must-revalidate" << true;
|
||||
|
||||
QTest::newRow("304-2b") << QNetworkRequest::PreferNetwork << "httpcachetest_cachecontrol200.cgi?private, max-age=1000" << true;
|
||||
QTest::newRow("304-4b") << QNetworkRequest::PreferCache << "httpcachetest_cachecontrol200.cgi?private, max-age=1000" << true;
|
||||
}
|
||||
|
||||
void tst_QAbstractNetworkCache::cacheControl()
|
||||
{
|
||||
runTest();
|
||||
}
|
||||
|
||||
void tst_QAbstractNetworkCache::cacheControlSynchronous_data()
|
||||
{
|
||||
tst_QAbstractNetworkCache::cacheControl_data();
|
||||
}
|
||||
|
||||
void tst_QAbstractNetworkCache::cacheControlSynchronous()
|
||||
{
|
||||
checkSynchronous();
|
||||
}
|
||||
|
||||
void tst_QAbstractNetworkCache::runTest()
|
||||
{
|
||||
QFETCH(QNetworkRequest::CacheLoadControl, cacheLoadControl);
|
||||
QFETCH(QString, url);
|
||||
QFETCH(bool, fetchFromCache);
|
||||
|
||||
QNetworkAccessManager manager;
|
||||
NetworkDiskCache *diskCache = new NetworkDiskCache(&manager);
|
||||
QVERIFY2(diskCache->tempDir.isValid(), qPrintable(diskCache->tempDir.errorString()));
|
||||
manager.setCache(diskCache);
|
||||
QCOMPARE(diskCache->gotData, false);
|
||||
|
||||
QUrl realUrl = url.contains("://") ? url : TESTFILE + url;
|
||||
QNetworkRequest request(realUrl);
|
||||
|
||||
// prime the cache
|
||||
QNetworkReply *reply = manager.get(request);
|
||||
QSignalSpy downloaded1(reply, SIGNAL(finished()));
|
||||
QTRY_COMPARE(downloaded1.size(), 1);
|
||||
QCOMPARE(diskCache->gotData, false);
|
||||
QByteArray goodData = reply->readAll();
|
||||
|
||||
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, cacheLoadControl);
|
||||
|
||||
// should be in the cache now
|
||||
QNetworkReply *reply2 = manager.get(request);
|
||||
QSignalSpy downloaded2(reply2, SIGNAL(finished()));
|
||||
QTRY_COMPARE(downloaded2.size(), 1);
|
||||
|
||||
QByteArray secondData = reply2->readAll();
|
||||
if (!fetchFromCache && cacheLoadControl == QNetworkRequest::AlwaysCache) {
|
||||
QCOMPARE(reply2->error(), QNetworkReply::ContentNotFoundError);
|
||||
QCOMPARE(secondData, QByteArray());
|
||||
} else {
|
||||
QCOMPARE(reply2->error(), QNetworkReply::NoError);
|
||||
QCOMPARE(QString(secondData), QString(goodData));
|
||||
QCOMPARE(secondData, goodData);
|
||||
QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
|
||||
}
|
||||
|
||||
if (fetchFromCache) {
|
||||
QList<QByteArray> rawHeaderList = reply->rawHeaderList();
|
||||
QList<QByteArray> rawHeaderList2 = reply2->rawHeaderList();
|
||||
std::sort(rawHeaderList.begin(), rawHeaderList.end());
|
||||
std::sort(rawHeaderList2.begin(), rawHeaderList2.end());
|
||||
}
|
||||
QCOMPARE(diskCache->gotData, fetchFromCache);
|
||||
}
|
||||
|
||||
void tst_QAbstractNetworkCache::checkSynchronous()
|
||||
{
|
||||
QSKIP("not working yet, see QTBUG-15221");
|
||||
|
||||
QFETCH(QNetworkRequest::CacheLoadControl, cacheLoadControl);
|
||||
QFETCH(QString, url);
|
||||
QFETCH(bool, fetchFromCache);
|
||||
|
||||
QNetworkAccessManager manager;
|
||||
NetworkDiskCache *diskCache = new NetworkDiskCache(&manager);
|
||||
QVERIFY2(diskCache->tempDir.isValid(), qPrintable(diskCache->tempDir.errorString()));
|
||||
manager.setCache(diskCache);
|
||||
QCOMPARE(diskCache->gotData, false);
|
||||
|
||||
QUrl realUrl = url.contains("://") ? url : TESTFILE + url;
|
||||
QNetworkRequest request(realUrl);
|
||||
|
||||
request.setAttribute(
|
||||
QNetworkRequest::SynchronousRequestAttribute,
|
||||
true);
|
||||
|
||||
// prime the cache
|
||||
QNetworkReply *reply = manager.get(request);
|
||||
QVERIFY(reply->isFinished()); // synchronous
|
||||
QCOMPARE(diskCache->gotData, false);
|
||||
QByteArray goodData = reply->readAll();
|
||||
|
||||
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, cacheLoadControl);
|
||||
|
||||
// should be in the cache now
|
||||
QNetworkReply *reply2 = manager.get(request);
|
||||
QVERIFY(reply2->isFinished()); // synchronous
|
||||
|
||||
QByteArray secondData = reply2->readAll();
|
||||
if (!fetchFromCache && cacheLoadControl == QNetworkRequest::AlwaysCache) {
|
||||
QCOMPARE(reply2->error(), QNetworkReply::ContentNotFoundError);
|
||||
QCOMPARE(secondData, QByteArray());
|
||||
} else {
|
||||
if (reply2->error() != QNetworkReply::NoError)
|
||||
qDebug() << reply2->errorString();
|
||||
QCOMPARE(reply2->error(), QNetworkReply::NoError);
|
||||
QCOMPARE(QString(secondData), QString(goodData));
|
||||
QCOMPARE(secondData, goodData);
|
||||
QCOMPARE(reply2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
|
||||
}
|
||||
|
||||
if (fetchFromCache) {
|
||||
QList<QByteArray> rawHeaderList = reply->rawHeaderList();
|
||||
QList<QByteArray> rawHeaderList2 = reply2->rawHeaderList();
|
||||
std::sort(rawHeaderList.begin(), rawHeaderList.end());
|
||||
std::sort(rawHeaderList2.begin(), rawHeaderList2.end());
|
||||
}
|
||||
QCOMPARE(diskCache->gotData, fetchFromCache);
|
||||
}
|
||||
|
||||
void tst_QAbstractNetworkCache::deleteCache()
|
||||
{
|
||||
QNetworkAccessManager manager;
|
||||
NetworkDiskCache *diskCache = new NetworkDiskCache(&manager);
|
||||
QVERIFY2(diskCache->tempDir.isValid(), qPrintable(diskCache->tempDir.errorString()));
|
||||
manager.setCache(diskCache);
|
||||
|
||||
QString url = "httpcachetest_cachecontrol.cgi?max-age=1000";
|
||||
QNetworkRequest request(QUrl(TESTFILE + url));
|
||||
QNetworkReply *reply = manager.get(request);
|
||||
QSignalSpy downloaded1(reply, SIGNAL(finished()));
|
||||
manager.setCache(0);
|
||||
QTRY_COMPARE(downloaded1.size(), 1);
|
||||
}
|
||||
|
||||
|
||||
QTEST_MAIN(tst_QAbstractNetworkCache)
|
||||
#include "tst_qabstractnetworkcache.moc"
|
||||
|
BIN
tests/auto/network/access/qdecompresshelper/10K.gz
Normal file
BIN
tests/auto/network/access/qdecompresshelper/10K.gz
Normal file
Binary file not shown.
BIN
tests/auto/network/access/qdecompresshelper/4G.br
Normal file
BIN
tests/auto/network/access/qdecompresshelper/4G.br
Normal file
Binary file not shown.
2
tests/auto/network/access/qdecompresshelper/BLACKLIST
Normal file
2
tests/auto/network/access/qdecompresshelper/BLACKLIST
Normal file
@ -0,0 +1,2 @@
|
||||
[bigZlib]
|
||||
macos arm
|
18
tests/auto/network/access/qdecompresshelper/CMakeLists.txt
Normal file
18
tests/auto/network/access/qdecompresshelper/CMakeLists.txt
Normal file
@ -0,0 +1,18 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qdecompresshelper Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qdecompresshelper
|
||||
SOURCES
|
||||
gzip.rcc.cpp
|
||||
inflate.rcc.cpp
|
||||
tst_qdecompresshelper.cpp
|
||||
zstandard.rcc.cpp
|
||||
DEFINES
|
||||
SRC_DIR=${CMAKE_CURRENT_SOURCE_DIR}
|
||||
LIBRARIES
|
||||
Qt::NetworkPrivate
|
||||
)
|
1235
tests/auto/network/access/qdecompresshelper/gzip.rcc.cpp
Normal file
1235
tests/auto/network/access/qdecompresshelper/gzip.rcc.cpp
Normal file
File diff suppressed because it is too large
Load Diff
617
tests/auto/network/access/qdecompresshelper/inflate.rcc.cpp
Normal file
617
tests/auto/network/access/qdecompresshelper/inflate.rcc.cpp
Normal file
@ -0,0 +1,617 @@
|
||||
/****************************************************************************
|
||||
** Resource object code
|
||||
**
|
||||
** Created by: The Resource Compiler for Qt version 6.0.0
|
||||
**
|
||||
** WARNING! All changes made in this file will be lost!
|
||||
*****************************************************************************/
|
||||
|
||||
static const unsigned char qt_resource_data[] = {
|
||||
// D:/projects/qt/dev/src/qtbase/tests/auto/network/access/qdecompresshelper/5GiB.txt.inflate
|
||||
0x0,0x0,0x20,0xca,
|
||||
0x0,
|
||||
0x4f,0x9f,0x57,0x78,0xda,0xec,0xdc,0x3d,0x2e,0x44,0x1,0x18,0x86,0xd1,0x3b,0x31,
|
||||
0x85,0x28,0xfc,0x14,0x2a,0x8d,0x65,0x68,0x24,0x34,0x6a,0x5,0xb5,0x58,0x80,0x5e,
|
||||
0x67,0x12,0xd1,0x5b,0x80,0x3d,0x4c,0x65,0x1,0x3a,0x3b,0x40,0x67,0x1,0xa2,0x50,
|
||||
0xa8,0xc4,0x88,0xbb,0x8c,0xfb,0x9c,0xb3,0x84,0xb7,0xfc,0xf2,0xe5,0xb9,0x7e,0xf8,
|
||||
0x78,0x9a,0xcd,0x86,0x61,0x58,0xdc,0xff,0x2e,0x3f,0xd7,0x37,0x6,0x0,0x0,0x0,
|
||||
0x60,0xe2,0x16,0x6f,0xb7,0x6b,0xff,0xc7,0x80,0x61,0x7f,0xf5,0x72,0x72,0x7a,0xe,
|
||||
0x0,0x0,0x0,0x4c,0xdd,0xe5,0xd6,0xe6,0x7c,0x3c,0xa,0xfc,0x5c,0xec,0x1d,0x9b,
|
||||
0x3,0x0,0x0,0x0,0x26,0xef,0xee,0xf0,0x60,0x7b,0xfc,0x10,0xf8,0xba,0xb9,0x9a,
|
||||
0x9b,0x3,0x0,0x0,0x0,0x26,0x6f,0xf7,0xfb,0xfd,0x68,0xbc,0x5,0x3c,0xaf,0x1e,
|
||||
0x77,0xce,0xec,0x1,0x0,0x0,0x0,0x93,0xf7,0x2a,0x18,0x0,0x0,0x0,0x0,0x29,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,
|
||||
0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,
|
||||
0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,
|
||||
0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,
|
||||
0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,
|
||||
0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,
|
||||
0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,
|
||||
0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,
|
||||
0x0,0x0,0x0,0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0x10,0x23,0x18,0x0,0x0,0x0,
|
||||
0x0,0x2d,0x82,0x1,0x0,0x0,0x0,0xd0,0x22,0x18,0x0,0x0,0x0,0x0,0x2d,0x82,
|
||||
0x1,0x0,0x7f,0xed,0xdc,0x31,0x15,0x40,0x0,0x0,0x40,0x41,0xde,0x63,0x65,0xd4,
|
||||
0x47,0xd,0x9,0xb4,0x53,0x41,0x2c,0x9b,0x45,0x7,0xc3,0xbf,0xb,0x72,0x0,0x0,
|
||||
0x10,0x23,0xc,0x0,0x0,0x0,0x80,0x16,0x61,0x0,0x0,0x0,0x0,0xb4,0x8,0x3,
|
||||
0x0,0x0,0x0,0xa0,0x45,0x18,0x0,0x0,0x0,0x0,0x31,0xc2,0x0,0x0,0x0,0x0,
|
||||
0x68,0x11,0x6,0x0,0x0,0x0,0x40,0x8b,0x30,0x0,0x0,0x0,0x0,0x5a,0x84,0x1,
|
||||
0x0,0x0,0x0,0x10,0x23,0xc,0x0,0x0,0x0,0x80,0x16,0x61,0x0,0x0,0x0,0x0,
|
||||
0xb4,0x8,0x3,0x0,0x0,0x0,0xa0,0x45,0x18,0x0,0x0,0x0,0x0,0x31,0xc2,0x0,
|
||||
0x0,0x0,0x0,0x68,0x11,0x6,0x0,0x0,0x0,0x40,0x8b,0x30,0x0,0x0,0x0,0x0,
|
||||
0x5a,0x84,0x1,0x0,0x0,0x0,0x10,0x23,0xc,0x0,0x0,0x0,0x80,0x16,0x61,0x0,
|
||||
0x0,0x0,0x0,0xb4,0x8,0x3,0x0,0x0,0x0,0xa0,0x45,0x18,0x0,0x0,0x0,0x0,
|
||||
0x31,0xc2,0x0,0x0,0x0,0x0,0x68,0x11,0x6,0x0,0x0,0x0,0x40,0x8b,0x30,0x0,
|
||||
0x0,0x0,0x0,0x5a,0x84,0x1,0x0,0x0,0x0,0x10,0x23,0xc,0x0,0x0,0x0,0x80,
|
||||
0x16,0x61,0x0,0x0,0x0,0x0,0xb4,0x8,0x3,0x0,0x0,0x0,0xa0,0x45,0x18,0x0,
|
||||
0x0,0x0,0x0,0x31,0xc2,0x0,0x0,0x0,0x0,0x68,0x11,0x6,0x0,0x0,0x0,0x40,
|
||||
0x8b,0x30,0x0,0x0,0x0,0x0,0x5a,0x84,0x1,0x0,0x0,0x0,0x10,0x23,0xc,0x0,
|
||||
0x0,0x0,0x80,0x16,0x61,0x0,0x0,0x0,0x0,0xb4,0x8,0x3,0x0,0x0,0x0,0xa0,
|
||||
0x45,0x18,0x0,0x0,0x0,0x0,0x31,0xc2,0x0,0x0,0x0,0x0,0x68,0x11,0x6,0x0,
|
||||
0x0,0x0,0x40,0x8b,0x30,0x0,0x0,0x0,0x0,0x5a,0x84,0x1,0x0,0x0,0x0,0x10,
|
||||
0x23,0xc,0x0,0x0,0x0,0x80,0x16,0x61,0x0,0x0,0x0,0x0,0xb4,0x8,0x3,0x0,
|
||||
0x0,0x0,0xa0,0x45,0x18,0x0,0x0,0x0,0x0,0x31,0xc2,0x0,0x0,0x0,0x0,0x68,
|
||||
0x11,0x6,0x0,0x0,0x0,0x40,0x8b,0x30,0x0,0x0,0x0,0x0,0x5a,0x84,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0xc,0x0,0x0,0x0,0x80,0x16,0x61,0x0,0x0,0x0,0x0,0xb4,
|
||||
0x8,0x3,0x0,0x0,0x0,0xa0,0x45,0x18,0x0,0x0,0x0,0x0,0x31,0xc2,0x0,0x0,
|
||||
0x0,0x0,0x68,0x11,0x6,0x0,0x0,0x0,0x40,0x8b,0x30,0x0,0x0,0x0,0x0,0x5a,
|
||||
0x84,0x1,0x0,0x0,0x0,0x10,0x23,0xc,0x0,0x0,0x0,0x80,0x16,0x61,0x0,0x0,
|
||||
0x0,0x0,0xb4,0x8,0x3,0x0,0x0,0x0,0xa0,0x45,0x18,0x0,0x0,0x0,0x0,0x31,
|
||||
0xc2,0x0,0x0,0x0,0x0,0x68,0x11,0x6,0x0,0x0,0x0,0x40,0x8b,0x30,0x0,0x0,
|
||||
0x0,0x0,0x5a,0x84,0x1,0x0,0x0,0x0,0x10,0x23,0xc,0x0,0x0,0x0,0x80,0x16,
|
||||
0x61,0x0,0x0,0x0,0x0,0xb4,0x8,0x3,0x0,0x0,0x0,0xa0,0x45,0x18,0x0,0x0,
|
||||
0x0,0x0,0x31,0xc2,0x0,0x0,0x0,0x0,0x68,0x11,0x6,0x0,0x0,0x0,0x40,0x8b,
|
||||
0x30,0x0,0x0,0x0,0x0,0x5a,0x84,0x1,0x0,0x0,0x0,0x10,0x23,0xc,0x0,0x0,
|
||||
0x0,0x80,0x16,0x61,0x0,0x0,0x0,0x0,0xb4,0x8,0x3,0x0,0x0,0x0,0xa0,0x45,
|
||||
0x18,0x0,0x0,0x0,0x0,0x31,0xc2,0x0,0x0,0x0,0x0,0x68,0x11,0x6,0x0,0x0,
|
||||
0x0,0x40,0x8b,0x30,0x0,0x0,0x0,0x0,0x5a,0x84,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0xc,0x0,0x0,0x0,0x80,0x16,0x61,0x0,0x0,0x0,0x0,0xb4,0x8,0x3,0x0,0x0,
|
||||
0x0,0xa0,0x45,0x18,0x0,0x0,0x0,0x0,0x31,0xc2,0x0,0x0,0x0,0x0,0x68,0x11,
|
||||
0x6,0x0,0x0,0x0,0x40,0x8b,0x30,0x0,0x0,0x0,0x0,0x5a,0x84,0x1,0x0,0x0,
|
||||
0x0,0x10,0x23,0xc,0x0,0x0,0x0,0x80,0x16,0x61,0x0,0x0,0x0,0x0,0xb4,0x8,
|
||||
0x3,0x0,0x0,0x0,0xa0,0x45,0x18,0x0,0x0,0x0,0x0,0x31,0xc2,0x0,0x0,0x0,
|
||||
0x0,0x68,0x11,0x6,0x0,0x0,0x0,0x40,0x8b,0x30,0x0,0x0,0x0,0x0,0x5a,0x84,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0xc,0x0,0x0,0x0,0x80,0x16,0x61,0x0,0x0,0x0,
|
||||
0x0,0xb4,0x8,0x3,0x0,0x0,0x0,0xa0,0x45,0x18,0x0,0x0,0x0,0x0,0x31,0xc2,
|
||||
0x0,0x0,0x0,0x0,0x68,0x11,0x6,0x0,0x0,0x0,0x40,0x8b,0x30,0x0,0x0,0x0,
|
||||
0x0,0x5a,0x84,0x1,0x0,0x0,0x0,0x10,0x23,0xc,0x0,0x0,0x0,0x80,0x16,0x61,
|
||||
0x0,0x0,0x0,0x0,0xb4,0x8,0x3,0x0,0x0,0x0,0xa0,0x45,0x18,0x0,0x0,0x0,
|
||||
0x0,0x31,0xc2,0x0,0x0,0x0,0x0,0x68,0x11,0x6,0x0,0x0,0x0,0x40,0x8b,0x30,
|
||||
0x0,0x0,0x0,0x0,0x5a,0x84,0x1,0x0,0x0,0x0,0x10,0x23,0xc,0x0,0x0,0x0,
|
||||
0x80,0x16,0x61,0x0,0x0,0x0,0x0,0xb4,0x8,0x3,0x0,0x0,0x0,0xa0,0x45,0x18,
|
||||
0x0,0x0,0x0,0x0,0x31,0xc2,0x0,0x0,0x0,0x0,0x68,0x11,0x6,0x0,0x0,0x0,
|
||||
0x40,0x8b,0x30,0x0,0x0,0x0,0x0,0x5a,0x84,0x1,0x0,0x0,0x0,0x10,0x23,0xc,
|
||||
0x0,0x0,0x0,0x80,0x16,0x61,0x0,0x0,0x0,0x0,0xb4,0x8,0x3,0x0,0x0,0x0,
|
||||
0xa0,0x45,0x18,0x0,0x0,0x0,0x0,0x31,0xc2,0x0,0x0,0x0,0x0,0x68,0x11,0x6,
|
||||
0x0,0x0,0x0,0x40,0x8b,0x30,0x0,0x0,0x0,0x0,0x5a,0x84,0x1,0x0,0x0,0x0,
|
||||
0x10,0x23,0xc,0x0,0x0,0x0,0x80,0x16,0x61,0x0,0x0,0x0,0x0,0xb4,0x8,0x3,
|
||||
0x0,0x0,0x0,0xa0,0x45,0x18,0x0,0x0,0x0,0x0,0x31,0xc2,0x0,0x0,0x0,0x0,
|
||||
0x68,0x11,0x6,0x0,0x0,0x0,0x40,0x8b,0x30,0x0,0x0,0x0,0x0,0x5a,0x84,0x1,
|
||||
0x0,0x0,0x0,0x10,0x23,0xc,0x0,0x0,0x0,0x80,0x16,0x61,0x0,0x0,0x0,0x0,
|
||||
0xb4,0x8,0x3,0x0,0x0,0x0,0xa0,0x45,0x18,0x0,0x0,0x0,0x0,0x31,0xc2,0x0,
|
||||
0x0,0x0,0x0,0x68,0x11,0x6,0x0,0x0,0x0,0x40,0x8b,0x30,0x0,0x0,0x0,0x0,
|
||||
0x5a,0x84,0x1,0x0,0x0,0x0,0x10,0x23,0xc,0x0,0x0,0x0,0x80,0x16,0x61,0x0,
|
||||
0x0,0x0,0x0,0xb4,0x8,0x3,0x0,0x0,0x0,0xa0,0x45,0x18,0x0,0x0,0x0,0x0,
|
||||
0x31,0xc2,0x0,0x0,0x0,0x0,0x68,0x11,0x6,0x0,0x0,0x0,0x40,0x8b,0x30,0x0,
|
||||
0x0,0x0,0x0,0x5a,0x84,0x1,0x0,0x0,0x0,0x10,0x23,0xc,0x0,0x0,0x0,0x80,
|
||||
0x16,0x61,0x0,0x0,0x0,0x0,0xb4,0x8,0x3,0x0,0x0,0x0,0xa0,0x45,0x18,0x0,
|
||||
0x0,0x0,0x0,0x31,0xc2,0x0,0x0,0x0,0x0,0x68,0x11,0x6,0x0,0x0,0x0,0x40,
|
||||
0x8b,0x30,0x0,0x0,0x0,0x0,0x5a,0x84,0x1,0x0,0x0,0x0,0x10,0x23,0xc,0x0,
|
||||
0x0,0x0,0x80,0x16,0x61,0x0,0x0,0x0,0x0,0xb4,0x8,0x3,0x0,0x0,0x0,0xa0,
|
||||
0x45,0x18,0x0,0x0,0x0,0x0,0x31,0xc2,0x0,0x0,0x0,0x0,0x68,0x11,0x6,0x0,
|
||||
0x0,0x0,0x40,0x8b,0x30,0x0,0x0,0x0,0x0,0x5a,0x84,0x1,0x0,0x0,0x0,0x10,
|
||||
0x23,0xc,0x0,0x0,0x0,0x80,0x16,0x61,0x0,0x0,0x0,0x0,0xb4,0x8,0x3,0x0,
|
||||
0x0,0x0,0xa0,0x45,0x18,0x0,0x0,0x0,0x0,0x31,0xc2,0x0,0x0,0x0,0x0,0x68,
|
||||
0x11,0x6,0x0,0x0,0x0,0x40,0x8b,0x30,0x0,0x0,0x0,0x0,0x5a,0x84,0x1,0x0,
|
||||
0x0,0x0,0x10,0x23,0xc,0x0,0x0,0x0,0x80,0x16,0x61,0x0,0x0,0x0,0x0,0xb4,
|
||||
0x8,0x3,0x0,0x0,0x0,0xa0,0x45,0x18,0x0,0x0,0x0,0x0,0x31,0xc2,0x0,0x0,
|
||||
0x0,0x0,0x68,0x11,0x6,0x0,0x0,0x0,0x40,0x8b,0x30,0x0,0x0,0x0,0x0,0x5a,
|
||||
0x84,0x1,0x0,0x0,0x0,0x10,0x23,0xc,0x0,0x0,0x0,0x80,0x16,0x61,0x0,0x0,
|
||||
0x0,0x0,0xb4,0x8,0x3,0x0,0x0,0x0,0xa0,0x45,0x18,0x0,0x0,0x0,0x0,0x31,
|
||||
0xc2,0x0,0x0,0x0,0x0,0x68,0x11,0x6,0x0,0x0,0x0,0x40,0x8b,0x30,0x0,0x0,
|
||||
0x0,0x0,0x5a,0x84,0x1,0x0,0x0,0x0,0x10,0x23,0xc,0x0,0x0,0x0,0x80,0x16,
|
||||
0x61,0x0,0x0,0x0,0x0,0xb4,0x8,0x3,0x0,0x0,0x0,0xa0,0x45,0x18,0x0,0x0,
|
||||
0x0,0x0,0x31,0xc2,0x0,0x0,0x0,0x0,0x68,0x11,0x6,0x0,0x0,0x0,0x40,0x8b,
|
||||
0x30,0x0,0x0,0x0,0x0,0x5a,0x84,0x1,0x0,0x0,0x0,0x10,0x23,0xc,0x0,0x0,
|
||||
0x0,0x80,0x16,0x61,0x0,0x0,0x0,0x0,0xb4,0x8,0x3,0x0,0x0,0x0,0xa0,0x45,
|
||||
0x18,0x0,0x0,0x0,0x0,0x31,0xc2,0x0,0x0,0x0,0x0,0x68,0x11,0x6,0x0,0x0,
|
||||
0x0,0x40,0x8b,0x30,0x0,0x0,0x0,0x0,0x5a,0x84,0x1,0x0,0x0,0x0,0x10,0x23,
|
||||
0xc,0x0,0x0,0x0,0x80,0x16,0x61,0x0,0x0,0x0,0x0,0xb4,0x8,0x3,0x0,0x0,
|
||||
0x0,0xa0,0x45,0x18,0x0,0x0,0x0,0x0,0x31,0xc2,0x0,0x0,0x0,0x0,0x68,0x11,
|
||||
0x6,0x0,0x0,0x0,0x40,0x8b,0x30,0x0,0x0,0x0,0x0,0x5a,0x84,0x1,0x0,0x0,
|
||||
0x0,0x10,0x23,0xc,0x0,0x0,0x0,0x80,0x16,0x61,0x0,0x0,0x0,0x0,0xb4,0x8,
|
||||
0x3,0x0,0x0,0x0,0xa0,0x45,0x18,0x0,0x0,0x0,0x0,0x31,0xc2,0x0,0x0,0x0,
|
||||
0x0,0x68,0x11,0x6,0x0,0x0,0x0,0x40,0x8b,0x30,0x0,0x0,0x0,0x0,0x5a,0x84,
|
||||
0x1,0x0,0x0,0x0,0x10,0x23,0xc,0x0,0x0,0x0,0x80,0x16,0x61,0x0,0x0,0x0,
|
||||
0x0,0xb4,0x8,0x3,0x0,0x0,0x0,0xa0,0x45,0x18,0x0,0x0,0x0,0x0,0x31,0xc2,
|
||||
0x0,0x0,0x0,0x0,0x68,0x11,0x6,0x0,0x0,0x0,0x40,0x8b,0x30,0x0,0x0,0x0,
|
||||
0x0,0x5a,0x84,0x1,0x0,0x0,0x0,0x10,0x23,0xc,0x0,0x0,0x0,0x80,0x16,0x61,
|
||||
0x0,0x0,0x0,0x0,0xb4,0x8,0x3,0x0,0x0,0x0,0xa0,0x45,0x18,0x0,0x0,0x0,
|
||||
0x0,0x31,0xc2,0x0,0x0,0x0,0x0,0x68,0x11,0x6,0x0,0x0,0x0,0x40,0x8b,0x30,
|
||||
0x0,0x0,0x0,0x0,0x5a,0x84,0x1,0x0,0x0,0x0,0x10,0x23,0xc,0x0,0x0,0x0,
|
||||
0x80,0x16,0x61,0x0,0x0,0x0,0x0,0xb4,0x8,0x3,0x0,0x0,0x0,0xa0,0x45,0x18,
|
||||
0x0,0x0,0x0,0x0,0x31,0xc2,0x0,0x0,0x0,0x0,0x68,0x11,0x6,0x0,0x0,0x0,
|
||||
0x40,0x8b,0x30,0x0,0x0,0x0,0x0,0x5a,0x84,0x1,0x0,0x0,0x0,0x10,0x23,0xc,
|
||||
0x0,0x0,0x0,0x80,0x96,0xf3,0xb,0x3,0xa6,0xe7,0x58,0xaf,0x7d,0x1e,0xe0,0x1f,
|
||||
0xdb,0xbd,0xc,0xe3,0xb,0x89,0xb2,0x9c,0x98,
|
||||
|
||||
};
|
||||
|
||||
static const unsigned char qt_resource_name[] = {
|
||||
// 5GiB.txt.inflate
|
||||
0x0,0x10,
|
||||
0x6,0xf4,0xb1,0x65,
|
||||
0x0,0x35,
|
||||
0x0,0x47,0x0,0x69,0x0,0x42,0x0,0x2e,0x0,0x74,0x0,0x78,0x0,0x74,0x0,0x2e,0x0,0x69,0x0,0x6e,0x0,0x66,0x0,0x6c,0x0,0x61,0x0,0x74,0x0,0x65,
|
||||
|
||||
};
|
||||
|
||||
static const unsigned char qt_resource_struct[] = {
|
||||
// :
|
||||
0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
|
||||
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
|
||||
// :/5GiB.txt.inflate
|
||||
0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,
|
||||
0x0,0x0,0x1,0x72,0x1c,0xae,0x5,0x93,
|
||||
|
||||
};
|
||||
|
||||
#ifdef QT_NAMESPACE
|
||||
# define QT_RCC_PREPEND_NAMESPACE(name) ::QT_NAMESPACE::name
|
||||
# define QT_RCC_MANGLE_NAMESPACE0(x) x
|
||||
# define QT_RCC_MANGLE_NAMESPACE1(a, b) a##_##b
|
||||
# define QT_RCC_MANGLE_NAMESPACE2(a, b) QT_RCC_MANGLE_NAMESPACE1(a,b)
|
||||
# define QT_RCC_MANGLE_NAMESPACE(name) QT_RCC_MANGLE_NAMESPACE2( \
|
||||
QT_RCC_MANGLE_NAMESPACE0(name), QT_RCC_MANGLE_NAMESPACE0(QT_NAMESPACE))
|
||||
#else
|
||||
# define QT_RCC_PREPEND_NAMESPACE(name) name
|
||||
# define QT_RCC_MANGLE_NAMESPACE(name) name
|
||||
#endif
|
||||
|
||||
#ifdef QT_NAMESPACE
|
||||
namespace QT_NAMESPACE {
|
||||
#endif
|
||||
|
||||
bool qRegisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *);
|
||||
bool qUnregisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *);
|
||||
|
||||
#if defined(__ELF__) || defined(__APPLE__)
|
||||
static inline unsigned char qResourceFeatureZlib()
|
||||
{
|
||||
extern const unsigned char qt_resourceFeatureZlib;
|
||||
return qt_resourceFeatureZlib;
|
||||
}
|
||||
#else
|
||||
unsigned char qResourceFeatureZlib();
|
||||
#endif
|
||||
|
||||
#ifdef QT_NAMESPACE
|
||||
}
|
||||
#endif
|
||||
|
||||
int QT_RCC_MANGLE_NAMESPACE(qInitResources_inflate)();
|
||||
int QT_RCC_MANGLE_NAMESPACE(qInitResources_inflate)()
|
||||
{
|
||||
int version = 3;
|
||||
QT_RCC_PREPEND_NAMESPACE(qRegisterResourceData)
|
||||
(version, qt_resource_struct, qt_resource_name, qt_resource_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int QT_RCC_MANGLE_NAMESPACE(qCleanupResources_inflate)();
|
||||
int QT_RCC_MANGLE_NAMESPACE(qCleanupResources_inflate)()
|
||||
{
|
||||
int version = 3;
|
||||
version += QT_RCC_PREPEND_NAMESPACE(qResourceFeatureZlib());
|
||||
QT_RCC_PREPEND_NAMESPACE(qUnregisterResourceData)
|
||||
(version, qt_resource_struct, qt_resource_name, qt_resource_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct initializer {
|
||||
initializer() { QT_RCC_MANGLE_NAMESPACE(qInitResources_inflate)(); }
|
||||
~initializer() { QT_RCC_MANGLE_NAMESPACE(qCleanupResources_inflate)(); }
|
||||
} dummy;
|
||||
}
|
@ -0,0 +1,461 @@
|
||||
// Copyright (C) 2020 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include <QtNetwork/private/qdecompresshelper_p.h>
|
||||
|
||||
#include <QtCore/qbytearray.h>
|
||||
|
||||
const QString srcDir = QStringLiteral(QT_STRINGIFY(SRC_DIR));
|
||||
|
||||
class tst_QDecompressHelper : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
void sharedDecompress_data();
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
|
||||
void encodingSupported();
|
||||
|
||||
void decompress_data();
|
||||
void decompress();
|
||||
|
||||
void partialDecompress_data();
|
||||
void partialDecompress();
|
||||
|
||||
void countAhead_data();
|
||||
void countAhead();
|
||||
void countAheadByteDataBuffer_data();
|
||||
void countAheadByteDataBuffer();
|
||||
|
||||
void countAheadPartialRead_data();
|
||||
void countAheadPartialRead();
|
||||
|
||||
void decompressBigData_data();
|
||||
void decompressBigData();
|
||||
|
||||
void archiveBomb_data();
|
||||
void archiveBomb();
|
||||
|
||||
void bigZlib();
|
||||
};
|
||||
|
||||
void tst_QDecompressHelper::initTestCase()
|
||||
{
|
||||
Q_INIT_RESOURCE(gzip);
|
||||
Q_INIT_RESOURCE(inflate);
|
||||
#if QT_CONFIG(zstd)
|
||||
Q_INIT_RESOURCE(zstandard);
|
||||
#endif
|
||||
}
|
||||
void tst_QDecompressHelper::cleanupTestCase()
|
||||
{
|
||||
#if QT_CONFIG(zstd)
|
||||
Q_CLEANUP_RESOURCE(zstandard);
|
||||
#endif
|
||||
Q_CLEANUP_RESOURCE(inflate);
|
||||
Q_CLEANUP_RESOURCE(gzip);
|
||||
}
|
||||
|
||||
void tst_QDecompressHelper::encodingSupported()
|
||||
{
|
||||
const QByteArrayList &accepted = QDecompressHelper::acceptedEncoding();
|
||||
|
||||
QVERIFY(QDecompressHelper::isSupportedEncoding("deflate"));
|
||||
QVERIFY(accepted.contains("deflate"));
|
||||
QVERIFY(QDecompressHelper::isSupportedEncoding("gzip"));
|
||||
QVERIFY(accepted.contains("gzip"));
|
||||
int expected = 2;
|
||||
|
||||
QVERIFY(accepted.indexOf("gzip") < accepted.indexOf("deflate"));
|
||||
|
||||
#if QT_CONFIG(brotli)
|
||||
QVERIFY(QDecompressHelper::isSupportedEncoding("br"));
|
||||
QVERIFY(accepted.contains("br"));
|
||||
++expected;
|
||||
#endif
|
||||
|
||||
#if QT_CONFIG(zstd)
|
||||
QVERIFY(QDecompressHelper::isSupportedEncoding("zstd"));
|
||||
QVERIFY(accepted.contains("zstd"));
|
||||
++expected;
|
||||
#endif
|
||||
QCOMPARE(expected, accepted.size());
|
||||
}
|
||||
|
||||
void tst_QDecompressHelper::sharedDecompress_data()
|
||||
{
|
||||
QTest::addColumn<QByteArray>("encoding");
|
||||
QTest::addColumn<QByteArray>("data");
|
||||
QTest::addColumn<QByteArray>("expected");
|
||||
|
||||
QTest::newRow("gzip-hello-world")
|
||||
<< QByteArray("gzip")
|
||||
<< QByteArray::fromBase64("H4sIAAAAAAAAA8tIzcnJVyjPL8pJAQCFEUoNCwAAAA==")
|
||||
<< QByteArray("hello world");
|
||||
|
||||
// Has two streams. ZLib reports end of stream after the first one, but we need to decompress
|
||||
// all of the streams to get the full file.
|
||||
QTest::newRow("gzip-multistream-hello-world")
|
||||
<< QByteArray("gzip")
|
||||
<< QByteArray::fromBase64(
|
||||
"H4sIAAAAAAAAA8tIzcnJBwCGphA2BQAAAB+LCAAAAAAAAANTKM8vykkBAMtCO0oGAAAA")
|
||||
<< QByteArray("hello world");
|
||||
|
||||
QTest::newRow("deflate-hello-world")
|
||||
<< QByteArray("deflate") << QByteArray::fromBase64("eJzLSM3JyVcozy/KSQEAGgsEXQ==")
|
||||
<< QByteArray("hello world");
|
||||
|
||||
#if QT_CONFIG(brotli)
|
||||
QTest::newRow("brotli-hello-world")
|
||||
<< QByteArray("br") << QByteArray::fromBase64("DwWAaGVsbG8gd29ybGQD")
|
||||
<< QByteArray("hello world");
|
||||
#endif
|
||||
|
||||
#if QT_CONFIG(zstd)
|
||||
QTest::newRow("zstandard-hello-world")
|
||||
<< QByteArray("zstd") << QByteArray::fromBase64("KLUv/QRYWQAAaGVsbG8gd29ybGRoaR6y")
|
||||
<< QByteArray("hello world");
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QDecompressHelper::decompress_data()
|
||||
{
|
||||
sharedDecompress_data();
|
||||
}
|
||||
|
||||
void tst_QDecompressHelper::decompress()
|
||||
{
|
||||
QDecompressHelper helper;
|
||||
|
||||
QFETCH(QByteArray, encoding);
|
||||
QVERIFY(helper.setEncoding(encoding));
|
||||
|
||||
QFETCH(QByteArray, data);
|
||||
helper.feed(data);
|
||||
|
||||
QFETCH(QByteArray, expected);
|
||||
QByteArray actual(expected.size(), Qt::Uninitialized);
|
||||
qsizetype read = helper.read(actual.data(), actual.size());
|
||||
|
||||
QCOMPARE(read, expected.size());
|
||||
QCOMPARE(actual, expected);
|
||||
}
|
||||
|
||||
void tst_QDecompressHelper::partialDecompress_data()
|
||||
{
|
||||
sharedDecompress_data();
|
||||
}
|
||||
|
||||
// Test that even though we read 1 byte at a time we
|
||||
// don't lose data from the decoder's internal storage
|
||||
void tst_QDecompressHelper::partialDecompress()
|
||||
{
|
||||
QDecompressHelper helper;
|
||||
|
||||
QFETCH(QByteArray, encoding);
|
||||
QVERIFY(helper.setEncoding(encoding));
|
||||
|
||||
QFETCH(QByteArray, data);
|
||||
helper.feed(data);
|
||||
|
||||
QFETCH(QByteArray, expected);
|
||||
QByteArray actual(expected.size(), Qt::Uninitialized);
|
||||
qsizetype readTotal = 0;
|
||||
while (helper.hasData()) {
|
||||
qsizetype read = helper.read(actual.data() + readTotal, 1);
|
||||
if (read != 0) // last read might return 0
|
||||
QCOMPARE(read, 1); // Make sure we don't suddenly read too much
|
||||
readTotal += read;
|
||||
}
|
||||
|
||||
QCOMPARE(readTotal, expected.size());
|
||||
QCOMPARE(actual, expected);
|
||||
}
|
||||
|
||||
void tst_QDecompressHelper::countAhead_data()
|
||||
{
|
||||
sharedDecompress_data();
|
||||
}
|
||||
|
||||
// Test the double-decompress / count uncompressed size feature.
|
||||
// We expect that after it has been fed data it will be able to
|
||||
// tell us the full size of the data when uncompressed.
|
||||
void tst_QDecompressHelper::countAhead()
|
||||
{
|
||||
QDecompressHelper helper;
|
||||
helper.setCountingBytesEnabled(true);
|
||||
|
||||
QFETCH(QByteArray, encoding);
|
||||
QVERIFY(helper.setEncoding(encoding));
|
||||
|
||||
QFETCH(QByteArray, data);
|
||||
QByteArray firstPart = data.left(data.size() - data.size() / 6);
|
||||
QVERIFY(firstPart.size() < data.size()); // sanity check
|
||||
QByteArray secondPart = data.mid(firstPart.size());
|
||||
helper.feed(firstPart); // feed by copy
|
||||
|
||||
// it's a reasonable assumption that after feeding it the first part
|
||||
// should have decompressed something
|
||||
QVERIFY(helper.uncompressedSize() > 0);
|
||||
|
||||
helper.feed(std::move(secondPart)); // feed by move
|
||||
|
||||
QFETCH(QByteArray, expected);
|
||||
QCOMPARE(helper.uncompressedSize(), expected.size());
|
||||
|
||||
QByteArray actual(helper.uncompressedSize(), Qt::Uninitialized);
|
||||
qsizetype read = helper.read(actual.data(), actual.size());
|
||||
|
||||
QCOMPARE(read, expected.size());
|
||||
QCOMPARE(actual, expected);
|
||||
}
|
||||
|
||||
void tst_QDecompressHelper::countAheadByteDataBuffer_data()
|
||||
{
|
||||
sharedDecompress_data();
|
||||
}
|
||||
|
||||
void tst_QDecompressHelper::countAheadByteDataBuffer()
|
||||
{
|
||||
QFETCH(QByteArray, encoding);
|
||||
QFETCH(QByteArray, data);
|
||||
QFETCH(QByteArray, expected);
|
||||
{ // feed buffer by const-ref
|
||||
QDecompressHelper helper;
|
||||
helper.setCountingBytesEnabled(true);
|
||||
QVERIFY(helper.setEncoding(encoding));
|
||||
|
||||
QByteArray firstPart = data.left(data.size() - data.size() / 6);
|
||||
QVERIFY(firstPart.size() < data.size()); // sanity check
|
||||
QByteArray secondPart = data.mid(firstPart.size());
|
||||
|
||||
QByteDataBuffer buffer;
|
||||
buffer.append(firstPart);
|
||||
buffer.append(secondPart);
|
||||
|
||||
helper.feed(buffer);
|
||||
|
||||
QCOMPARE(helper.uncompressedSize(), expected.size());
|
||||
|
||||
QByteArray actual(helper.uncompressedSize(), Qt::Uninitialized);
|
||||
qsizetype read = helper.read(actual.data(), actual.size());
|
||||
|
||||
QCOMPARE(read, expected.size());
|
||||
QCOMPARE(actual, expected);
|
||||
}
|
||||
{ // Feed buffer by move
|
||||
QDecompressHelper helper;
|
||||
helper.setCountingBytesEnabled(true);
|
||||
QVERIFY(helper.setEncoding(encoding));
|
||||
|
||||
QByteArray firstPart = data.left(data.size() - data.size() / 6);
|
||||
QVERIFY(firstPart.size() < data.size()); // sanity check
|
||||
QByteArray secondPart = data.mid(firstPart.size());
|
||||
|
||||
QByteDataBuffer buffer;
|
||||
buffer.append(firstPart);
|
||||
buffer.append(secondPart);
|
||||
|
||||
helper.feed(std::move(buffer));
|
||||
|
||||
QCOMPARE(helper.uncompressedSize(), expected.size());
|
||||
|
||||
QByteArray actual(helper.uncompressedSize(), Qt::Uninitialized);
|
||||
qsizetype read = helper.read(actual.data(), actual.size());
|
||||
|
||||
QCOMPARE(read, expected.size());
|
||||
QCOMPARE(actual, expected);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QDecompressHelper::countAheadPartialRead_data()
|
||||
{
|
||||
sharedDecompress_data();
|
||||
}
|
||||
|
||||
// Make sure that the size is adjusted as we read data
|
||||
void tst_QDecompressHelper::countAheadPartialRead()
|
||||
{
|
||||
QDecompressHelper helper;
|
||||
helper.setCountingBytesEnabled(true);
|
||||
|
||||
QFETCH(QByteArray, encoding);
|
||||
QVERIFY(helper.setEncoding(encoding));
|
||||
|
||||
QFETCH(QByteArray, data);
|
||||
QByteArray firstPart = data.left(data.size() - data.size() / 6);
|
||||
QVERIFY(firstPart.size() < data.size()); // sanity check
|
||||
QByteArray secondPart = data.mid(firstPart.size());
|
||||
helper.feed(firstPart);
|
||||
|
||||
// it's a reasonable assumption that after feeding it half the data it
|
||||
// should have decompressed something
|
||||
QVERIFY(helper.uncompressedSize() > 0);
|
||||
|
||||
helper.feed(secondPart);
|
||||
|
||||
QFETCH(QByteArray, expected);
|
||||
QCOMPARE(helper.uncompressedSize(), expected.size());
|
||||
|
||||
QByteArray actual(helper.uncompressedSize(), Qt::Uninitialized);
|
||||
qsizetype read = helper.read(actual.data(), 5);
|
||||
QCOMPARE(read, 5);
|
||||
QCOMPARE(helper.uncompressedSize(), expected.size() - read);
|
||||
read += helper.read(actual.data() + read, 1);
|
||||
QCOMPARE(read, 6);
|
||||
QCOMPARE(helper.uncompressedSize(), expected.size() - read);
|
||||
|
||||
read += helper.read(actual.data() + read, expected.size() - read);
|
||||
|
||||
QCOMPARE(read, expected.size());
|
||||
QCOMPARE(actual, expected);
|
||||
}
|
||||
|
||||
void tst_QDecompressHelper::decompressBigData_data()
|
||||
{
|
||||
#if defined(QT_ASAN_ENABLED)
|
||||
QSKIP("Tests are too slow with asan enabled");
|
||||
#endif
|
||||
QTest::addColumn<QByteArray>("encoding");
|
||||
QTest::addColumn<QString>("path");
|
||||
QTest::addColumn<qint64>("size");
|
||||
QTest::addColumn<bool>("countAhead");
|
||||
|
||||
qint64 fourGiB = 4ll * 1024ll * 1024ll * 1024ll;
|
||||
qint64 fiveGiB = 5ll * 1024ll * 1024ll * 1024ll;
|
||||
|
||||
// Only use countAhead on one of these since they share codepath anyway
|
||||
QTest::newRow("gzip-counted-4G") << QByteArray("gzip") << QString(":/4G.gz") << fourGiB << true;
|
||||
QTest::newRow("deflate-5G") << QByteArray("deflate") << QString(":/5GiB.txt.inflate")
|
||||
<< fiveGiB << false;
|
||||
|
||||
#if QT_CONFIG(brotli)
|
||||
QTest::newRow("brotli-4G") << QByteArray("br") << (srcDir + "/4G.br") << fourGiB << false;
|
||||
QTest::newRow("brotli-counted-4G") << QByteArray("br") << (srcDir + "/4G.br") << fourGiB << true;
|
||||
#endif
|
||||
|
||||
#if QT_CONFIG(zstd)
|
||||
QTest::newRow("zstandard-4G") << QByteArray("zstd") << (":/4G.zst") << fourGiB << false;
|
||||
QTest::newRow("zstandard-counted-4G") << QByteArray("zstd") << (":/4G.zst") << fourGiB << true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QDecompressHelper::decompressBigData()
|
||||
{
|
||||
QFETCH(QString, path);
|
||||
QFile file(path);
|
||||
QVERIFY(file.open(QIODevice::ReadOnly));
|
||||
|
||||
const qint64 third = file.bytesAvailable() / 3;
|
||||
|
||||
QDecompressHelper helper;
|
||||
QFETCH(bool, countAhead);
|
||||
helper.setCountingBytesEnabled(countAhead);
|
||||
helper.setDecompressedSafetyCheckThreshold(-1);
|
||||
QFETCH(QByteArray, encoding);
|
||||
helper.setEncoding(encoding);
|
||||
|
||||
// The size of 'output' should be at least QDecompressHelper::MaxDecompressedDataBufferSize + 1
|
||||
QByteArray output(10 * 1024 * 1024 + 1, Qt::Uninitialized);
|
||||
qint64 totalSize = 0;
|
||||
while (!file.atEnd()) {
|
||||
helper.feed(file.read(third));
|
||||
while (helper.hasData()) {
|
||||
qsizetype bytesRead = helper.read(output.data(), output.size());
|
||||
QVERIFY(bytesRead >= 0);
|
||||
QVERIFY(bytesRead <= output.size());
|
||||
totalSize += bytesRead;
|
||||
const auto isZero = [](char c) { return c == '\0'; };
|
||||
bool allZero = std::all_of(output.cbegin(), output.cbegin() + bytesRead, isZero);
|
||||
QVERIFY(allZero);
|
||||
}
|
||||
}
|
||||
QTEST(totalSize, "size");
|
||||
}
|
||||
|
||||
void tst_QDecompressHelper::archiveBomb_data()
|
||||
{
|
||||
QTest::addColumn<QByteArray>("encoding");
|
||||
QTest::addColumn<QString>("path");
|
||||
QTest::addColumn<bool>("shouldFail");
|
||||
|
||||
QTest::newRow("gzip-10K") << QByteArray("gzip") << (srcDir + "/10K.gz") << false;
|
||||
QTest::newRow("gzip-4G") << QByteArray("gzip") << QString(":/4G.gz") << true;
|
||||
}
|
||||
|
||||
void tst_QDecompressHelper::archiveBomb()
|
||||
{
|
||||
QFETCH(bool, shouldFail);
|
||||
QFETCH(QString, path);
|
||||
QFile file(path);
|
||||
QVERIFY(file.open(QIODevice::ReadOnly));
|
||||
|
||||
QDecompressHelper helper;
|
||||
QFETCH(QByteArray, encoding);
|
||||
helper.setEncoding(encoding);
|
||||
QVERIFY(helper.isValid());
|
||||
|
||||
constexpr qint64 SafeSizeLimit = 10 * 1024 * 1024;
|
||||
constexpr qint64 RatioLimit = 40;
|
||||
qint64 bytesToRead = std::min(SafeSizeLimit / RatioLimit, file.bytesAvailable());
|
||||
QByteArray output(1 + bytesToRead * RatioLimit, Qt::Uninitialized);
|
||||
helper.feed(file.read(bytesToRead));
|
||||
qsizetype bytesRead = helper.read(output.data(), output.size());
|
||||
QVERIFY(bytesRead <= output.size());
|
||||
QVERIFY(helper.isValid());
|
||||
|
||||
if (shouldFail) {
|
||||
QCOMPARE(bytesRead, -1);
|
||||
QVERIFY(!helper.errorString().isEmpty());
|
||||
} else {
|
||||
QVERIFY(bytesRead > 0);
|
||||
QVERIFY(helper.errorString().isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QDecompressHelper::bigZlib()
|
||||
{
|
||||
#if QT_POINTER_SIZE < 8
|
||||
QSKIP("This cannot be tested on 32-bit systems");
|
||||
#elif defined(QT_ASAN_ENABLED)
|
||||
QSKIP("Test is too slow with asan enabled");
|
||||
#else
|
||||
# ifndef QT_NO_EXCEPTIONS
|
||||
try {
|
||||
# endif
|
||||
// ZLib uses unsigned integers as their size type internally which creates some special
|
||||
// cases in the internal code that should be tested!
|
||||
QFile file(":/5GiB.txt.inflate");
|
||||
QVERIFY(file.open(QIODevice::ReadOnly));
|
||||
QByteArray compressedData = file.readAll();
|
||||
|
||||
QDecompressHelper helper;
|
||||
helper.setDecompressedSafetyCheckThreshold(-1);
|
||||
helper.setEncoding("deflate");
|
||||
auto firstHalf = compressedData.left(compressedData.size() - 2);
|
||||
helper.feed(firstHalf);
|
||||
helper.feed(compressedData.mid(firstHalf.size()));
|
||||
|
||||
// We need the whole thing in one go... which is why this test is not available for 32-bit
|
||||
const qint64 expected = 5ll * 1024ll * 1024ll * 1024ll;
|
||||
// Request a few more byte than what is available, to verify exact size
|
||||
QByteArray output(expected + 42, Qt::Uninitialized);
|
||||
const qsizetype size = helper.read(output.data(), output.size());
|
||||
QCOMPARE(size, expected);
|
||||
# ifndef QT_NO_EXCEPTIONS
|
||||
} catch (const std::bad_alloc &) {
|
||||
QSKIP("Encountered most likely OOM.");
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QDecompressHelper)
|
||||
|
||||
#include "tst_qdecompresshelper.moc"
|
116
tests/auto/network/access/qdecompresshelper/zstandard.rcc.cpp
Normal file
116
tests/auto/network/access/qdecompresshelper/zstandard.rcc.cpp
Normal file
@ -0,0 +1,116 @@
|
||||
/****************************************************************************
|
||||
** Resource object code
|
||||
**
|
||||
** Created by: The Resource Compiler for Qt version 6.0.0
|
||||
**
|
||||
** WARNING! All changes made in this file will be lost!
|
||||
*****************************************************************************/
|
||||
|
||||
static const unsigned char qt_resource_data[] = {
|
||||
// D:/projects/qt/dev/src/qtbase/tests/auto/network/access/decompresshelper/4G.zst
|
||||
0x0,0x0,0x1,0x75,
|
||||
0x0,
|
||||
0x2,0x3,0x93,0x78,0xda,0xed,0xd4,0x21,0xe,0x83,0x40,0x10,0x86,0xd1,0x29,0x98,
|
||||
0x26,0x98,0x3d,0x46,0x1d,0x1a,0x8f,0xec,0x29,0x50,0xdc,0x84,0x13,0xe1,0x2b,0x90,
|
||||
0x1c,0x89,0xcd,0x32,0xe9,0x25,0x2a,0xfa,0x26,0x79,0xc9,0xe8,0x5f,0x7c,0xaf,0x7d,
|
||||
0xac,0xc7,0x1a,0x79,0x8f,0xf4,0x8e,0x78,0xe6,0x73,0xb5,0xa9,0x74,0x5d,0x94,0x0,
|
||||
0xfe,0xcf,0xfc,0xed,0x41,0x6d,0xe7,0x50,0xcc,0x1,0x32,0x60,0xe,0x90,0x1,0x73,
|
||||
0x80,0xc,0x98,0x4,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,
|
||||
0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,
|
||||
0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,
|
||||
0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,
|
||||
0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,
|
||||
0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,
|
||||
0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,
|
||||
0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,
|
||||
0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,
|
||||
0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,
|
||||
0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,
|
||||
0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,
|
||||
0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,
|
||||
0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,
|
||||
0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,
|
||||
0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,
|
||||
0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,
|
||||
0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x80,0x5f,0xe8,0xd3,0xf2,0x69,0xdb,0xd,
|
||||
0xcd,0x15,0x90,0xe9,
|
||||
|
||||
};
|
||||
|
||||
static const unsigned char qt_resource_name[] = {
|
||||
// 4G.zst
|
||||
0x0,0x6,
|
||||
0x3,0x8a,0x61,0xa4,
|
||||
0x0,0x34,
|
||||
0x0,0x47,0x0,0x2e,0x0,0x7a,0x0,0x73,0x0,0x74,
|
||||
|
||||
};
|
||||
|
||||
static const unsigned char qt_resource_struct[] = {
|
||||
// :
|
||||
0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
|
||||
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
|
||||
// :/4G.zst
|
||||
0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,
|
||||
0x0,0x0,0x1,0x72,0x1c,0x8d,0x7,0xac,
|
||||
|
||||
};
|
||||
|
||||
#ifdef QT_NAMESPACE
|
||||
# define QT_RCC_PREPEND_NAMESPACE(name) ::QT_NAMESPACE::name
|
||||
# define QT_RCC_MANGLE_NAMESPACE0(x) x
|
||||
# define QT_RCC_MANGLE_NAMESPACE1(a, b) a##_##b
|
||||
# define QT_RCC_MANGLE_NAMESPACE2(a, b) QT_RCC_MANGLE_NAMESPACE1(a,b)
|
||||
# define QT_RCC_MANGLE_NAMESPACE(name) QT_RCC_MANGLE_NAMESPACE2( \
|
||||
QT_RCC_MANGLE_NAMESPACE0(name), QT_RCC_MANGLE_NAMESPACE0(QT_NAMESPACE))
|
||||
#else
|
||||
# define QT_RCC_PREPEND_NAMESPACE(name) name
|
||||
# define QT_RCC_MANGLE_NAMESPACE(name) name
|
||||
#endif
|
||||
|
||||
#ifdef QT_NAMESPACE
|
||||
namespace QT_NAMESPACE {
|
||||
#endif
|
||||
|
||||
bool qRegisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *);
|
||||
bool qUnregisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *);
|
||||
|
||||
#if defined(__ELF__) || defined(__APPLE__)
|
||||
static inline unsigned char qResourceFeatureZlib()
|
||||
{
|
||||
extern const unsigned char qt_resourceFeatureZlib;
|
||||
return qt_resourceFeatureZlib;
|
||||
}
|
||||
#else
|
||||
unsigned char qResourceFeatureZlib();
|
||||
#endif
|
||||
|
||||
#ifdef QT_NAMESPACE
|
||||
}
|
||||
#endif
|
||||
|
||||
int QT_RCC_MANGLE_NAMESPACE(qInitResources_zstandard)();
|
||||
int QT_RCC_MANGLE_NAMESPACE(qInitResources_zstandard)()
|
||||
{
|
||||
int version = 3;
|
||||
QT_RCC_PREPEND_NAMESPACE(qRegisterResourceData)
|
||||
(version, qt_resource_struct, qt_resource_name, qt_resource_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int QT_RCC_MANGLE_NAMESPACE(qCleanupResources_zstandard)();
|
||||
int QT_RCC_MANGLE_NAMESPACE(qCleanupResources_zstandard)()
|
||||
{
|
||||
int version = 3;
|
||||
version += QT_RCC_PREPEND_NAMESPACE(qResourceFeatureZlib());
|
||||
QT_RCC_PREPEND_NAMESPACE(qUnregisterResourceData)
|
||||
(version, qt_resource_struct, qt_resource_name, qt_resource_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct initializer {
|
||||
initializer() { QT_RCC_MANGLE_NAMESPACE(qInitResources_zstandard)(); }
|
||||
~initializer() { QT_RCC_MANGLE_NAMESPACE(qCleanupResources_zstandard)(); }
|
||||
} dummy;
|
||||
}
|
13
tests/auto/network/access/qhttpheaderparser/CMakeLists.txt
Normal file
13
tests/auto/network/access/qhttpheaderparser/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
if(NOT QT_FEATURE_private_tests)
|
||||
return()
|
||||
endif()
|
||||
|
||||
qt_internal_add_test(tst_qhttpheaderparser
|
||||
SOURCES
|
||||
tst_qhttpheaderparser.cpp
|
||||
LIBRARIES
|
||||
Qt::NetworkPrivate
|
||||
)
|
@ -0,0 +1,94 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QtTest/qtest.h>
|
||||
#include <QObject>
|
||||
#include <QtNetwork/private/qhttpheaderparser_p.h>
|
||||
|
||||
class tst_QHttpHeaderParser : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void constructor();
|
||||
void limitsSetters();
|
||||
|
||||
void adjustableLimits_data();
|
||||
void adjustableLimits();
|
||||
|
||||
// general parsing tests can be found in tst_QHttpNetworkReply
|
||||
};
|
||||
|
||||
void tst_QHttpHeaderParser::constructor()
|
||||
{
|
||||
QHttpHeaderParser parser;
|
||||
QCOMPARE(parser.getStatusCode(), 100);
|
||||
QCOMPARE(parser.getMajorVersion(), 0);
|
||||
QCOMPARE(parser.getMinorVersion(), 0);
|
||||
QCOMPARE(parser.getReasonPhrase(), QByteArray());
|
||||
QCOMPARE(parser.combinedHeaderValue("Location"), QByteArray());
|
||||
QCOMPARE(parser.maxHeaderFields(), HeaderConstants::MAX_HEADER_FIELDS);
|
||||
QCOMPARE(parser.maxHeaderFieldSize(), HeaderConstants::MAX_HEADER_FIELD_SIZE);
|
||||
QCOMPARE(parser.maxTotalHeaderSize(), HeaderConstants::MAX_TOTAL_HEADER_SIZE);
|
||||
}
|
||||
|
||||
void tst_QHttpHeaderParser::limitsSetters()
|
||||
{
|
||||
QHttpHeaderParser parser;
|
||||
parser.setMaxHeaderFields(10);
|
||||
QCOMPARE(parser.maxHeaderFields(), 10);
|
||||
parser.setMaxHeaderFieldSize(10);
|
||||
QCOMPARE(parser.maxHeaderFieldSize(), 10);
|
||||
parser.setMaxTotalHeaderSize(10);
|
||||
QCOMPARE(parser.maxTotalHeaderSize(), 10);
|
||||
}
|
||||
|
||||
void tst_QHttpHeaderParser::adjustableLimits_data()
|
||||
{
|
||||
QTest::addColumn<qsizetype>("maxFieldCount");
|
||||
QTest::addColumn<qsizetype>("maxFieldSize");
|
||||
QTest::addColumn<qsizetype>("maxTotalSize");
|
||||
QTest::addColumn<QByteArray>("headers");
|
||||
QTest::addColumn<bool>("success");
|
||||
|
||||
// We pretend -1 means to not set a new limit.
|
||||
|
||||
QTest::newRow("maxFieldCount-pass") << qsizetype(10) << qsizetype(-1) << qsizetype(-1)
|
||||
<< QByteArray("Location: hi\r\n\r\n") << true;
|
||||
QTest::newRow("maxFieldCount-fail") << qsizetype(1) << qsizetype(-1) << qsizetype(-1)
|
||||
<< QByteArray("Location: hi\r\nCookie: a\r\n\r\n") << false;
|
||||
|
||||
QTest::newRow("maxFieldSize-pass") << qsizetype(-1) << qsizetype(50) << qsizetype(-1)
|
||||
<< QByteArray("Location: hi\r\n\r\n") << true;
|
||||
constexpr char cookieHeader[] = "Cookie: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
|
||||
static_assert(sizeof(cookieHeader) - 1 == 51);
|
||||
QByteArray fullHeader = QByteArray("Location: hi\r\n") + cookieHeader;
|
||||
QTest::newRow("maxFieldSize-fail") << qsizetype(-1) << qsizetype(50) << qsizetype(-1)
|
||||
<< (fullHeader + "\r\n\r\n") << false;
|
||||
|
||||
QTest::newRow("maxTotalSize-pass") << qsizetype(-1) << qsizetype(-1) << qsizetype(50)
|
||||
<< QByteArray("Location: hi\r\n\r\n") << true;
|
||||
QTest::newRow("maxTotalSize-fail") << qsizetype(-1) << qsizetype(-1) << qsizetype(10)
|
||||
<< QByteArray("Location: hi\r\n\r\n") << false;
|
||||
}
|
||||
|
||||
void tst_QHttpHeaderParser::adjustableLimits()
|
||||
{
|
||||
QFETCH(qsizetype, maxFieldCount);
|
||||
QFETCH(qsizetype, maxFieldSize);
|
||||
QFETCH(qsizetype, maxTotalSize);
|
||||
QFETCH(QByteArray, headers);
|
||||
QFETCH(bool, success);
|
||||
|
||||
QHttpHeaderParser parser;
|
||||
if (maxFieldCount != qsizetype(-1))
|
||||
parser.setMaxHeaderFields(maxFieldCount);
|
||||
if (maxFieldSize != qsizetype(-1))
|
||||
parser.setMaxHeaderFieldSize(maxFieldSize);
|
||||
if (maxTotalSize != qsizetype(-1))
|
||||
parser.setMaxTotalHeaderSize(maxTotalSize);
|
||||
|
||||
QCOMPARE(parser.parseHeaders(headers), success);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QHttpHeaderParser)
|
||||
#include "tst_qhttpheaderparser.moc"
|
@ -0,0 +1,19 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
if(NOT QT_FEATURE_private_tests)
|
||||
return()
|
||||
endif()
|
||||
|
||||
#####################################################################
|
||||
## tst_qhttpnetworkconnection Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qhttpnetworkconnection
|
||||
SOURCES
|
||||
tst_qhttpnetworkconnection.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::NetworkPrivate
|
||||
QT_TEST_SERVER_LIST "apache2"
|
||||
)
|
@ -0,0 +1,994 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
|
||||
#include <QTest>
|
||||
#include <QTestEventLoop>
|
||||
#include <QAuthenticator>
|
||||
#include <QTcpServer>
|
||||
|
||||
#include "private/qhttpnetworkconnection_p.h"
|
||||
#include "private/qnoncontiguousbytedevice_p.h"
|
||||
|
||||
#include "../../../network-settings.h"
|
||||
|
||||
class tst_QHttpNetworkConnection: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public Q_SLOTS:
|
||||
void finishedReply();
|
||||
void finishedWithError(QNetworkReply::NetworkError errorCode, const QString &detail);
|
||||
void challenge401(const QHttpNetworkRequest &request, QAuthenticator *authenticator);
|
||||
#ifndef QT_NO_SSL
|
||||
void sslErrors(const QList<QSslError> &errors);
|
||||
#endif
|
||||
private:
|
||||
bool finishedCalled;
|
||||
bool finishedWithErrorCalled;
|
||||
QNetworkReply::NetworkError netErrorCode;
|
||||
QString (*httpServerName)() = QtNetworkSettings::httpServerName;
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void options_data();
|
||||
void options();
|
||||
void get_data();
|
||||
void get();
|
||||
void head_data();
|
||||
void head();
|
||||
void post_data();
|
||||
void post();
|
||||
void put_data();
|
||||
void put();
|
||||
void _delete_data();
|
||||
void _delete();
|
||||
void trace_data();
|
||||
void trace();
|
||||
void _connect_data();
|
||||
void _connect();
|
||||
#ifndef QT_NO_COMPRESS
|
||||
void compression_data();
|
||||
void compression();
|
||||
#endif
|
||||
#ifndef QT_NO_SSL
|
||||
void ignoresslerror_data();
|
||||
void ignoresslerror();
|
||||
#endif
|
||||
#ifdef QT_NO_SSL
|
||||
void nossl_data();
|
||||
void nossl();
|
||||
#endif
|
||||
void get401_data();
|
||||
void get401();
|
||||
|
||||
void getMultiple_data();
|
||||
void getMultiple();
|
||||
void getMultipleWithPipeliningAndMultiplePriorities();
|
||||
void getMultipleWithPriorities();
|
||||
|
||||
void getEmptyWithPipelining();
|
||||
|
||||
void getAndEverythingShouldBePipelined();
|
||||
|
||||
void getAndThenDeleteObject();
|
||||
void getAndThenDeleteObject_data();
|
||||
|
||||
void overlappingCloseAndWrite();
|
||||
};
|
||||
|
||||
void tst_QHttpNetworkConnection::initTestCase()
|
||||
{
|
||||
#if defined(QT_TEST_SERVER)
|
||||
QVERIFY(QtNetworkSettings::verifyConnection(httpServerName(), 80));
|
||||
#else
|
||||
if (!QtNetworkSettings::verifyTestNetworkSettings())
|
||||
QSKIP("No network test server available");
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::options_data()
|
||||
{
|
||||
// not tested yet
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::options()
|
||||
{
|
||||
QEXPECT_FAIL("", "not tested yet", Continue);
|
||||
QVERIFY(false);
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::head_data()
|
||||
{
|
||||
QTest::addColumn<QString>("protocol");
|
||||
QTest::addColumn<QString>("host");
|
||||
QTest::addColumn<QString>("path");
|
||||
QTest::addColumn<ushort>("port");
|
||||
QTest::addColumn<bool>("encrypt");
|
||||
QTest::addColumn<int>("statusCode");
|
||||
QTest::addColumn<QString>("statusString");
|
||||
QTest::addColumn<int>("contentLength");
|
||||
|
||||
QTest::newRow("success-internal") << "http://" << httpServerName() << "/qtest/rfc3252.txt" << ushort(80) << false << 200 << "OK" << 25962;
|
||||
QTest::newRow("failure-path") << "http://" << httpServerName() << "/t" << ushort(80) << false << 404 << "Not Found" << -1;
|
||||
QTest::newRow("failure-protocol") << "" << httpServerName() << "/qtest/rfc3252.txt" << ushort(80) << false << 400 << "Bad Request" << -1;
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::head()
|
||||
{
|
||||
QFETCH(QString, protocol);
|
||||
QFETCH(QString, host);
|
||||
QFETCH(QString, path);
|
||||
QFETCH(ushort, port);
|
||||
QFETCH(bool, encrypt);
|
||||
QFETCH(int, statusCode);
|
||||
QFETCH(QString, statusString);
|
||||
QFETCH(int, contentLength);
|
||||
|
||||
QHttpNetworkConnection connection(host, port, encrypt);
|
||||
QCOMPARE(connection.port(), port);
|
||||
QCOMPARE(connection.hostName(), host);
|
||||
QCOMPARE(connection.isSsl(), encrypt);
|
||||
|
||||
QHttpNetworkRequest request(protocol + host + path, QHttpNetworkRequest::Head);
|
||||
QHttpNetworkReply *reply = connection.sendRequest(request);
|
||||
|
||||
QTRY_VERIFY_WITH_TIMEOUT(reply->isFinished(), 30000);
|
||||
QCOMPARE(reply->statusCode(), statusCode);
|
||||
QCOMPARE(reply->reasonPhrase(), statusString);
|
||||
// only check it if it is set and expected
|
||||
if (reply->contentLength() != -1 && contentLength != -1)
|
||||
QCOMPARE(reply->contentLength(), qint64(contentLength));
|
||||
|
||||
QVERIFY(reply->isFinished());
|
||||
|
||||
delete reply;
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::get_data()
|
||||
{
|
||||
QTest::addColumn<QString>("protocol");
|
||||
QTest::addColumn<QString>("host");
|
||||
QTest::addColumn<QString>("path");
|
||||
QTest::addColumn<ushort>("port");
|
||||
QTest::addColumn<bool>("encrypt");
|
||||
QTest::addColumn<int>("statusCode");
|
||||
QTest::addColumn<QString>("statusString");
|
||||
QTest::addColumn<int>("contentLength");
|
||||
QTest::addColumn<int>("downloadSize");
|
||||
|
||||
QTest::newRow("success-internal") << "http://" << httpServerName() << "/qtest/rfc3252.txt" << ushort(80) << false << 200 << "OK" << 25962 << 25962;
|
||||
|
||||
QTest::newRow("failure-path") << "http://" << httpServerName() << "/t" << ushort(80) << false << 404 << "Not Found" << -1 << -1;
|
||||
QTest::newRow("failure-protocol") << "" << httpServerName() << "/qtest/rfc3252.txt" << ushort(80) << false << 400 << "Bad Request" << -1 << -1;
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::get()
|
||||
{
|
||||
QFETCH(QString, protocol);
|
||||
QFETCH(QString, host);
|
||||
QFETCH(QString, path);
|
||||
QFETCH(ushort, port);
|
||||
QFETCH(bool, encrypt);
|
||||
QFETCH(int, statusCode);
|
||||
QFETCH(QString, statusString);
|
||||
QFETCH(int, contentLength);
|
||||
QFETCH(int, downloadSize);
|
||||
|
||||
QHttpNetworkConnection connection(host, port, encrypt);
|
||||
QCOMPARE(connection.port(), port);
|
||||
QCOMPARE(connection.hostName(), host);
|
||||
QCOMPARE(connection.isSsl(), encrypt);
|
||||
|
||||
QHttpNetworkRequest request(protocol + host + path);
|
||||
QHttpNetworkReply *reply = connection.sendRequest(request);
|
||||
|
||||
QTRY_VERIFY_WITH_TIMEOUT(reply->bytesAvailable(), 30000);
|
||||
|
||||
QCOMPARE(reply->statusCode(), statusCode);
|
||||
QCOMPARE(reply->reasonPhrase(), statusString);
|
||||
// only check it if it is set and expected
|
||||
if (reply->contentLength() != -1 && contentLength != -1)
|
||||
QCOMPARE(reply->contentLength(), qint64(contentLength));
|
||||
|
||||
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);
|
||||
//but check against content length if it was sent
|
||||
if (reply->contentLength() != -1)
|
||||
QCOMPARE(ba.size(), (int)reply->contentLength());
|
||||
|
||||
delete reply;
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::finishedReply()
|
||||
{
|
||||
finishedCalled = true;
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::finishedWithError(QNetworkReply::NetworkError errorCode, const QString &detail)
|
||||
{
|
||||
Q_UNUSED(detail);
|
||||
finishedWithErrorCalled = true;
|
||||
netErrorCode = errorCode;
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::put_data()
|
||||
{
|
||||
|
||||
QTest::addColumn<QString>("protocol");
|
||||
QTest::addColumn<QString>("host");
|
||||
QTest::addColumn<QString>("path");
|
||||
QTest::addColumn<ushort>("port");
|
||||
QTest::addColumn<bool>("encrypt");
|
||||
QTest::addColumn<QString>("data");
|
||||
QTest::addColumn<bool>("succeed");
|
||||
|
||||
QTest::newRow("success-internal") << "http://" << httpServerName() << "/dav/file1.txt" << ushort(80) << false << "Hello World\nEnd of file\n"<<true;
|
||||
QTest::newRow("fail-internal") << "http://" << httpServerName() << "/dav2/file1.txt" << ushort(80) << false << "Hello World\nEnd of file\n"<<false;
|
||||
QTest::newRow("fail-host") << "http://" << "invalid.test.qt-project.org" << "/dav2/file1.txt" << ushort(80) << false << "Hello World\nEnd of file\n"<<false;
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::put()
|
||||
{
|
||||
QFETCH(QString, protocol);
|
||||
QFETCH(QString, host);
|
||||
QFETCH(QString, path);
|
||||
QFETCH(ushort, port);
|
||||
QFETCH(bool, encrypt);
|
||||
QFETCH(QString, data);
|
||||
QFETCH(bool, succeed);
|
||||
|
||||
QHttpNetworkConnection connection(host, port, encrypt);
|
||||
QCOMPARE(connection.port(), port);
|
||||
QCOMPARE(connection.hostName(), host);
|
||||
QCOMPARE(connection.isSsl(), encrypt);
|
||||
|
||||
QHttpNetworkRequest request(protocol + host + path, QHttpNetworkRequest::Put);
|
||||
|
||||
QByteArray array = data.toLatin1();
|
||||
QNonContiguousByteDevice *bd = QNonContiguousByteDeviceFactory::create(&array);
|
||||
bd->setParent(this);
|
||||
request.setUploadByteDevice(bd);
|
||||
|
||||
finishedCalled = false;
|
||||
finishedWithErrorCalled = false;
|
||||
|
||||
QHttpNetworkReply *reply = connection.sendRequest(request);
|
||||
connect(reply, SIGNAL(finished()), SLOT(finishedReply()));
|
||||
connect(reply, SIGNAL(finishedWithError(QNetworkReply::NetworkError,QString)),
|
||||
SLOT(finishedWithError(QNetworkReply::NetworkError,QString)));
|
||||
|
||||
QTRY_VERIFY_WITH_TIMEOUT(reply->isFinished() || finishedCalled || finishedWithErrorCalled, 30000);
|
||||
|
||||
if (reply->isFinished()) {
|
||||
QByteArray ba;
|
||||
while (reply->bytesAvailable())
|
||||
ba += reply->readAny();
|
||||
} else if(finishedWithErrorCalled) {
|
||||
if(!succeed) {
|
||||
delete reply;
|
||||
return;
|
||||
} else {
|
||||
QFAIL("Error in PUT");
|
||||
}
|
||||
} else {
|
||||
QFAIL("PUT timed out");
|
||||
}
|
||||
|
||||
int status = reply->statusCode();
|
||||
if (status != 200 && status != 201 && status != 204) {
|
||||
if (succeed) {
|
||||
qDebug()<<"PUT failed, Status Code:" <<status;
|
||||
QFAIL("Error in PUT");
|
||||
}
|
||||
} else {
|
||||
if (!succeed) {
|
||||
qDebug()<<"PUT Should fail, Status Code:" <<status;
|
||||
QFAIL("Error in PUT");
|
||||
}
|
||||
}
|
||||
delete reply;
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::post_data()
|
||||
{
|
||||
QTest::addColumn<QString>("protocol");
|
||||
QTest::addColumn<QString>("host");
|
||||
QTest::addColumn<QString>("path");
|
||||
QTest::addColumn<ushort>("port");
|
||||
QTest::addColumn<bool>("encrypt");
|
||||
QTest::addColumn<QString>("data");
|
||||
QTest::addColumn<int>("statusCode");
|
||||
QTest::addColumn<QString>("statusString");
|
||||
QTest::addColumn<int>("contentLength");
|
||||
QTest::addColumn<int>("downloadSize");
|
||||
|
||||
QTest::newRow("success-internal") << "http://" << httpServerName() << "/qtest/cgi-bin/echo.cgi" << ushort(80) << false << "7 bytes" << 200 << "OK" << 7 << 7;
|
||||
QTest::newRow("failure-internal") << "http://" << httpServerName() << "/t" << ushort(80) << false << "Hello World" << 404 << "Not Found" << -1 << -1;
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::post()
|
||||
{
|
||||
QFETCH(QString, protocol);
|
||||
QFETCH(QString, host);
|
||||
QFETCH(QString, path);
|
||||
QFETCH(ushort, port);
|
||||
QFETCH(bool, encrypt);
|
||||
QFETCH(QString, data);
|
||||
QFETCH(int, statusCode);
|
||||
QFETCH(QString, statusString);
|
||||
QFETCH(int, contentLength);
|
||||
QFETCH(int, downloadSize);
|
||||
|
||||
QHttpNetworkConnection connection(host, port, encrypt);
|
||||
QCOMPARE(connection.port(), port);
|
||||
QCOMPARE(connection.hostName(), host);
|
||||
QCOMPARE(connection.isSsl(), encrypt);
|
||||
|
||||
QHttpNetworkRequest request(protocol + host + path, QHttpNetworkRequest::Post);
|
||||
|
||||
QByteArray array = data.toLatin1();
|
||||
QNonContiguousByteDevice *bd = QNonContiguousByteDeviceFactory::create(&array);
|
||||
bd->setParent(this);
|
||||
request.setUploadByteDevice(bd);
|
||||
|
||||
QHttpNetworkReply *reply = connection.sendRequest(request);
|
||||
|
||||
QTRY_VERIFY_WITH_TIMEOUT(reply->bytesAvailable(), 30000);
|
||||
QCOMPARE(reply->statusCode(), statusCode);
|
||||
QCOMPARE(reply->reasonPhrase(), statusString);
|
||||
|
||||
qint64 cLen = reply->contentLength();
|
||||
if (contentLength != -1) {
|
||||
// only check the content length if test expected it to be set
|
||||
if (cLen==-1) {
|
||||
// HTTP 1.1 server may respond with chunked encoding and in that
|
||||
// case contentLength is not present in reply -> verify that it is the case
|
||||
QByteArray transferEnc = reply->headerField("Transfer-Encoding");
|
||||
QCOMPARE(transferEnc, QByteArray("chunked"));
|
||||
} else {
|
||||
QCOMPARE(cLen, qint64(contentLength));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
//but do compare with content length if possible
|
||||
if (cLen != -1)
|
||||
QCOMPARE(ba.size(), (int)cLen);
|
||||
|
||||
delete reply;
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::_delete_data()
|
||||
{
|
||||
// not tested yet
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::_delete()
|
||||
{
|
||||
QEXPECT_FAIL("", "not tested yet", Continue);
|
||||
QVERIFY(false);
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::trace_data()
|
||||
{
|
||||
// not tested yet
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::trace()
|
||||
{
|
||||
QEXPECT_FAIL("", "not tested yet", Continue);
|
||||
QVERIFY(false);
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::_connect_data()
|
||||
{
|
||||
// not tested yet
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::_connect()
|
||||
{
|
||||
QEXPECT_FAIL("", "not tested yet", Continue);
|
||||
QVERIFY(false);
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::challenge401(const QHttpNetworkRequest &request,
|
||||
QAuthenticator *authenticator)
|
||||
{
|
||||
Q_UNUSED(request);
|
||||
|
||||
QHttpNetworkReply *reply = qobject_cast<QHttpNetworkReply*>(sender());
|
||||
if (reply) {
|
||||
QHttpNetworkConnection *c = reply->connection();
|
||||
|
||||
QVariant val = c->property("setCredentials");
|
||||
if (val.toBool()) {
|
||||
QVariant user = c->property("username");
|
||||
QVariant password = c->property("password");
|
||||
authenticator->setUser(user.toString());
|
||||
authenticator->setPassword(password.toString());
|
||||
c->setProperty("setCredentials", false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::get401_data()
|
||||
{
|
||||
QTest::addColumn<QString>("protocol");
|
||||
QTest::addColumn<QString>("host");
|
||||
QTest::addColumn<QString>("path");
|
||||
QTest::addColumn<ushort>("port");
|
||||
QTest::addColumn<bool>("encrypt");
|
||||
QTest::addColumn<bool>("setCredentials");
|
||||
QTest::addColumn<QString>("username");
|
||||
QTest::addColumn<QString>("password");
|
||||
QTest::addColumn<int>("statusCode");
|
||||
|
||||
QTest::newRow("no-credentials") << "http://" << httpServerName() << "/qtest/rfcs-auth/index.html" << ushort(80) << false << false << "" << ""<<401;
|
||||
QTest::newRow("invalid-credentials") << "http://" << httpServerName() << "/qtest/rfcs-auth/index.html" << ushort(80) << false << true << "test" << "test"<<401;
|
||||
QTest::newRow("valid-credentials") << "http://" << httpServerName() << "/qtest/rfcs-auth/index.html" << ushort(80) << false << true << "httptest" << "httptest"<<200;
|
||||
QTest::newRow("digest-authentication-invalid") << "http://" << httpServerName() << "/qtest/auth-digest/index.html" << ushort(80) << false << true << "wrong" << "wrong"<<401;
|
||||
QTest::newRow("digest-authentication-valid") << "http://" << httpServerName() << "/qtest/auth-digest/index.html" << ushort(80) << false << true << "httptest" << "httptest"<<200;
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::get401()
|
||||
{
|
||||
QFETCH(QString, protocol);
|
||||
QFETCH(QString, host);
|
||||
QFETCH(QString, path);
|
||||
QFETCH(ushort, port);
|
||||
QFETCH(bool, encrypt);
|
||||
QFETCH(bool, setCredentials);
|
||||
QFETCH(QString, username);
|
||||
QFETCH(QString, password);
|
||||
QFETCH(int, statusCode);
|
||||
|
||||
QHttpNetworkConnection connection(host, port, encrypt);
|
||||
QCOMPARE(connection.port(), port);
|
||||
QCOMPARE(connection.hostName(), host);
|
||||
QCOMPARE(connection.isSsl(), encrypt);
|
||||
connection.setProperty("setCredentials", setCredentials);
|
||||
connection.setProperty("username", username);
|
||||
connection.setProperty("password", password);
|
||||
|
||||
QHttpNetworkRequest request(protocol + host + path);
|
||||
QHttpNetworkReply *reply = connection.sendRequest(request);
|
||||
connect(reply, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
|
||||
SLOT(challenge401(QHttpNetworkRequest,QAuthenticator*)));
|
||||
|
||||
finishedCalled = false;
|
||||
finishedWithErrorCalled = false;
|
||||
|
||||
connect(reply, SIGNAL(finished()), SLOT(finishedReply()));
|
||||
connect(reply, SIGNAL(finishedWithError(QNetworkReply::NetworkError,QString)),
|
||||
SLOT(finishedWithError(QNetworkReply::NetworkError,QString)));
|
||||
|
||||
QTRY_VERIFY_WITH_TIMEOUT(finishedCalled || finishedWithErrorCalled, 30000);
|
||||
QCOMPARE(reply->statusCode(), statusCode);
|
||||
delete reply;
|
||||
}
|
||||
|
||||
#ifndef QT_NO_COMPRESS
|
||||
void tst_QHttpNetworkConnection::compression_data()
|
||||
{
|
||||
QTest::addColumn<QString>("protocol");
|
||||
QTest::addColumn<QString>("host");
|
||||
QTest::addColumn<QString>("path");
|
||||
QTest::addColumn<ushort>("port");
|
||||
QTest::addColumn<bool>("encrypt");
|
||||
QTest::addColumn<int>("statusCode");
|
||||
QTest::addColumn<QString>("statusString");
|
||||
QTest::addColumn<int>("contentLength");
|
||||
QTest::addColumn<int>("downloadSize");
|
||||
QTest::addColumn<bool>("autoCompress");
|
||||
QTest::addColumn<QString>("contentCoding");
|
||||
|
||||
QTest::newRow("success-autogzip-temp") << "http://" << httpServerName() << "/qtest/rfcs/rfc2616.html" << ushort(80) << false << 200 << "OK" << -1 << 418321 << true << "";
|
||||
QTest::newRow("success-nogzip-temp") << "http://" << httpServerName() << "/qtest/rfcs/rfc2616.html" << ushort(80) << false << 200 << "OK" << 418321 << 418321 << false << "identity";
|
||||
QTest::newRow("success-manualgzip-temp") << "http://" << httpServerName() << "/qtest/deflate/rfc2616.html" << ushort(80) << false << 200 << "OK" << 119124 << 119124 << false << "gzip";
|
||||
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::compression()
|
||||
{
|
||||
QFETCH(QString, protocol);
|
||||
QFETCH(QString, host);
|
||||
QFETCH(QString, path);
|
||||
QFETCH(ushort, port);
|
||||
QFETCH(bool, encrypt);
|
||||
QFETCH(int, statusCode);
|
||||
QFETCH(QString, statusString);
|
||||
QFETCH(int, contentLength);
|
||||
QFETCH(int, downloadSize);
|
||||
QFETCH(bool, autoCompress);
|
||||
QFETCH(QString, contentCoding);
|
||||
|
||||
QHttpNetworkConnection connection(host, port, encrypt);
|
||||
QCOMPARE(connection.port(), port);
|
||||
QCOMPARE(connection.hostName(), host);
|
||||
QCOMPARE(connection.isSsl(), encrypt);
|
||||
|
||||
QHttpNetworkRequest request(protocol + host + path);
|
||||
if (!autoCompress)
|
||||
request.setHeaderField("Accept-Encoding", contentCoding.toLatin1());
|
||||
QHttpNetworkReply *reply = connection.sendRequest(request);
|
||||
|
||||
QTRY_VERIFY_WITH_TIMEOUT(reply->bytesAvailable(), 30000);
|
||||
QCOMPARE(reply->statusCode(), statusCode);
|
||||
QCOMPARE(reply->reasonPhrase(), statusString);
|
||||
bool isLengthOk = (reply->contentLength() == qint64(contentLength)
|
||||
|| reply->contentLength() == qint64(downloadSize)
|
||||
|| reply->contentLength() == -1); //apache2 does not send content-length for compressed pages
|
||||
|
||||
QVERIFY(isLengthOk);
|
||||
|
||||
QTRY_VERIFY_WITH_TIMEOUT(reply->isFinished(), 30000);
|
||||
QByteArray ba = reply->readAll();
|
||||
QCOMPARE(ba.size(), downloadSize);
|
||||
|
||||
delete reply;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef QT_NO_SSL
|
||||
void tst_QHttpNetworkConnection::sslErrors(const QList<QSslError> &errors)
|
||||
{
|
||||
Q_UNUSED(errors);
|
||||
|
||||
QHttpNetworkReply *reply = qobject_cast<QHttpNetworkReply*>(sender());
|
||||
if (reply) {
|
||||
QHttpNetworkConnection *connection = reply->connection();
|
||||
|
||||
QVariant val = connection->property("ignoreFromSignal");
|
||||
if (val.toBool())
|
||||
connection->ignoreSslErrors();
|
||||
finishedWithErrorCalled = true;
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::ignoresslerror_data()
|
||||
{
|
||||
QTest::addColumn<QString>("protocol");
|
||||
QTest::addColumn<QString>("host");
|
||||
QTest::addColumn<QString>("path");
|
||||
QTest::addColumn<ushort>("port");
|
||||
QTest::addColumn<bool>("encrypt");
|
||||
QTest::addColumn<bool>("ignoreInit");
|
||||
QTest::addColumn<bool>("ignoreFromSignal");
|
||||
QTest::addColumn<int>("statusCode");
|
||||
|
||||
// This test will work only if the website has ssl errors.
|
||||
// fluke's certificate is signed by a non-standard authority.
|
||||
// Since we don't introduce that CA into the SSL verification chain,
|
||||
// connecting should fail.
|
||||
QTest::newRow("success-init") << "https://" << httpServerName() << "/" << ushort(443) << true << true << false << 200;
|
||||
QTest::newRow("success-fromSignal") << "https://" << httpServerName() << "/" << ushort(443) << true << false << true << 200;
|
||||
QTest::newRow("failure") << "https://" << httpServerName() << "/" << ushort(443) << true << false << false << 100;
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::ignoresslerror()
|
||||
{
|
||||
QFETCH(QString, protocol);
|
||||
QFETCH(QString, host);
|
||||
QFETCH(QString, path);
|
||||
QFETCH(ushort, port);
|
||||
QFETCH(bool, encrypt);
|
||||
QFETCH(bool, ignoreInit);
|
||||
QFETCH(bool, ignoreFromSignal);
|
||||
QFETCH(int, statusCode);
|
||||
|
||||
QHttpNetworkConnection connection(host, port, encrypt);
|
||||
QCOMPARE(connection.port(), port);
|
||||
QCOMPARE(connection.hostName(), host);
|
||||
if (ignoreInit)
|
||||
connection.ignoreSslErrors();
|
||||
QCOMPARE(connection.isSsl(), encrypt);
|
||||
connection.setProperty("ignoreFromSignal", ignoreFromSignal);
|
||||
|
||||
QHttpNetworkRequest request(protocol + host + path);
|
||||
QHttpNetworkReply *reply = connection.sendRequest(request);
|
||||
connect(reply, SIGNAL(sslErrors(QList<QSslError>)),
|
||||
SLOT(sslErrors(QList<QSslError>)));
|
||||
|
||||
finishedWithErrorCalled = false;
|
||||
|
||||
connect(reply, SIGNAL(finished()), SLOT(finishedReply()));
|
||||
|
||||
QTRY_VERIFY_WITH_TIMEOUT(reply->bytesAvailable() || (statusCode == 100 && finishedWithErrorCalled), 30000);
|
||||
QCOMPARE(reply->statusCode(), statusCode);
|
||||
delete reply;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef QT_NO_SSL
|
||||
void tst_QHttpNetworkConnection::nossl_data()
|
||||
{
|
||||
QTest::addColumn<QString>("protocol");
|
||||
QTest::addColumn<QString>("host");
|
||||
QTest::addColumn<QString>("path");
|
||||
QTest::addColumn<ushort>("port");
|
||||
QTest::addColumn<bool>("encrypt");
|
||||
QTest::addColumn<QNetworkReply::NetworkError>("networkError");
|
||||
|
||||
QTest::newRow("protocol-error") << "https://" << httpServerName() << "/" << ushort(443) << true <<QNetworkReply::ProtocolUnknownError;
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::nossl()
|
||||
{
|
||||
QFETCH(QString, protocol);
|
||||
QFETCH(QString, host);
|
||||
QFETCH(QString, path);
|
||||
QFETCH(ushort, port);
|
||||
QFETCH(bool, encrypt);
|
||||
QFETCH(QNetworkReply::NetworkError, networkError);
|
||||
|
||||
QHttpNetworkConnection connection(host, port, encrypt);
|
||||
QCOMPARE(connection.port(), port);
|
||||
QCOMPARE(connection.hostName(), host);
|
||||
|
||||
QHttpNetworkRequest request(protocol + host + path);
|
||||
QHttpNetworkReply *reply = connection.sendRequest(request);
|
||||
|
||||
finishedWithErrorCalled = false;
|
||||
netErrorCode = QNetworkReply::NoError;
|
||||
|
||||
connect(reply, SIGNAL(finished()), SLOT(finishedReply()));
|
||||
connect(reply, SIGNAL(finishedWithError(QNetworkReply::NetworkError,QString)),
|
||||
SLOT(finishedWithError(QNetworkReply::NetworkError,QString)));
|
||||
|
||||
QTRY_VERIFY_WITH_TIMEOUT(finishedWithErrorCalled, 30000);
|
||||
QCOMPARE(netErrorCode, networkError);
|
||||
delete reply;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void tst_QHttpNetworkConnection::getMultiple_data()
|
||||
{
|
||||
QTest::addColumn<quint16>("connectionCount");
|
||||
QTest::addColumn<bool>("pipeliningAllowed");
|
||||
// send 100 requests. apache will usually force-close after 100 requests in a single tcp connection
|
||||
QTest::addColumn<int>("requestCount");
|
||||
|
||||
QTest::newRow("6 connections, no pipelining, 100 requests") << quint16(6) << false << 100;
|
||||
QTest::newRow("1 connection, no pipelining, 100 requests") << quint16(1) << false << 100;
|
||||
QTest::newRow("6 connections, pipelining allowed, 100 requests") << quint16(6) << true << 100;
|
||||
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.size(); i++)
|
||||
if (!replies.at(i)->isFinished())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::getMultiple()
|
||||
{
|
||||
QFETCH(quint16, connectionCount);
|
||||
QFETCH(bool, pipeliningAllowed);
|
||||
QFETCH(int, requestCount);
|
||||
|
||||
QHttpNetworkConnection connection(connectionCount, httpServerName());
|
||||
|
||||
QList<QHttpNetworkRequest*> requests;
|
||||
QList<QHttpNetworkReply*> replies;
|
||||
|
||||
for (int i = 0; i < requestCount; i++) {
|
||||
// depending on what you use the results will vary.
|
||||
// for the "real" results, use a URL that has "internet latency" for you. Then (6 connections, pipelining) will win.
|
||||
// for LAN latency, you will possibly get that (1 connection, no pipelining) is the fastest
|
||||
QHttpNetworkRequest *request = new QHttpNetworkRequest("http://" + httpServerName() + "/qtest/rfc3252.txt");
|
||||
if (pipeliningAllowed)
|
||||
request->setPipeliningAllowed(true);
|
||||
requests.append(request);
|
||||
QHttpNetworkReply *reply = connection.sendRequest(*request);
|
||||
replies.append(reply);
|
||||
}
|
||||
|
||||
QTRY_VERIFY_WITH_TIMEOUT(allRepliesFinished(&replies), 60000);
|
||||
qDeleteAll(requests);
|
||||
qDeleteAll(replies);
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::getMultipleWithPipeliningAndMultiplePriorities()
|
||||
{
|
||||
quint16 requestCount = 100;
|
||||
|
||||
// use 2 connections.
|
||||
QHttpNetworkConnection connection(2, httpServerName());
|
||||
|
||||
QList<QHttpNetworkRequest*> requests;
|
||||
QList<QHttpNetworkReply*> replies;
|
||||
|
||||
for (int i = 0; i < requestCount; i++) {
|
||||
QHttpNetworkRequest *request = nullptr;
|
||||
if (i % 3)
|
||||
request = new QHttpNetworkRequest("http://" + httpServerName() + "/qtest/rfc3252.txt", QHttpNetworkRequest::Get);
|
||||
else
|
||||
request = new QHttpNetworkRequest("http://" + httpServerName() + "/qtest/rfc3252.txt", QHttpNetworkRequest::Head);
|
||||
|
||||
if (i % 2 || i % 3)
|
||||
request->setPipeliningAllowed(true);
|
||||
|
||||
if (i % 3)
|
||||
request->setPriority(QHttpNetworkRequest::HighPriority);
|
||||
else if (i % 5)
|
||||
request->setPriority(QHttpNetworkRequest::NormalPriority);
|
||||
else if (i % 7)
|
||||
request->setPriority(QHttpNetworkRequest::LowPriority);
|
||||
|
||||
requests.append(request);
|
||||
QHttpNetworkReply *reply = connection.sendRequest(*request);
|
||||
replies.append(reply);
|
||||
}
|
||||
|
||||
QTRY_VERIFY_WITH_TIMEOUT(allRepliesFinished(&replies), 60000);
|
||||
|
||||
int pipelinedCount = 0;
|
||||
for (int i = 0; i < replies.size(); i++) {
|
||||
QVERIFY (!(replies.at(i)->request().isPipeliningAllowed() == false
|
||||
&& replies.at(i)->isPipeliningUsed()));
|
||||
|
||||
if (replies.at(i)->isPipeliningUsed())
|
||||
pipelinedCount++;
|
||||
}
|
||||
|
||||
// We allow pipelining for every 2nd,3rd,4th,6th,8th,9th,10th etc request.
|
||||
// Assume that half of the requests had been pipelined.
|
||||
// (this is a very relaxed condition, when last measured 79 of 100
|
||||
// requests had been pipelined)
|
||||
QVERIFY(pipelinedCount >= requestCount / 2);
|
||||
|
||||
qDeleteAll(requests);
|
||||
qDeleteAll(replies);
|
||||
}
|
||||
|
||||
class GetMultipleWithPrioritiesReceiver : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
int highPrioReceived;
|
||||
int lowPrioReceived;
|
||||
int requestCount;
|
||||
GetMultipleWithPrioritiesReceiver(int rq) : highPrioReceived(0), lowPrioReceived(0), requestCount(rq) { }
|
||||
public Q_SLOTS:
|
||||
void finishedSlot() {
|
||||
QHttpNetworkReply *reply = (QHttpNetworkReply*) sender();
|
||||
if (reply->request().priority() == QHttpNetworkRequest::HighPriority)
|
||||
highPrioReceived++;
|
||||
else if (reply->request().priority() == QHttpNetworkRequest::LowPriority)
|
||||
lowPrioReceived++;
|
||||
else
|
||||
QFAIL("Wrong priority!?");
|
||||
|
||||
QVERIFY(highPrioReceived + 7 >= lowPrioReceived);
|
||||
|
||||
if (highPrioReceived + lowPrioReceived == requestCount)
|
||||
QTestEventLoop::instance().exitLoop();
|
||||
}
|
||||
};
|
||||
|
||||
void tst_QHttpNetworkConnection::getMultipleWithPriorities()
|
||||
{
|
||||
quint16 requestCount = 100;
|
||||
// use 2 connections.
|
||||
QHttpNetworkConnection connection(2, httpServerName());
|
||||
GetMultipleWithPrioritiesReceiver receiver(requestCount);
|
||||
QUrl url("http://" + httpServerName() + "/qtest/rfc3252.txt");
|
||||
QList<QHttpNetworkRequest*> requests;
|
||||
QList<QHttpNetworkReply*> replies;
|
||||
|
||||
for (int i = 0; i < requestCount; i++) {
|
||||
QHttpNetworkRequest *request = nullptr;
|
||||
if (i % 3)
|
||||
request = new QHttpNetworkRequest(url, QHttpNetworkRequest::Get);
|
||||
else
|
||||
request = new QHttpNetworkRequest(url, QHttpNetworkRequest::Head);
|
||||
|
||||
if (i % 2)
|
||||
request->setPriority(QHttpNetworkRequest::HighPriority);
|
||||
else
|
||||
request->setPriority(QHttpNetworkRequest::LowPriority);
|
||||
|
||||
requests.append(request);
|
||||
QHttpNetworkReply *reply = connection.sendRequest(*request);
|
||||
connect(reply, SIGNAL(finished()), &receiver, SLOT(finishedSlot()));
|
||||
replies.append(reply);
|
||||
}
|
||||
|
||||
QTestEventLoop::instance().enterLoop(40);
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
|
||||
qDeleteAll(requests);
|
||||
qDeleteAll(replies);
|
||||
}
|
||||
|
||||
|
||||
class GetEmptyWithPipeliningReceiver : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
int receivedCount;
|
||||
int requestCount;
|
||||
GetEmptyWithPipeliningReceiver(int rq) : receivedCount(0),requestCount(rq) { }
|
||||
public Q_SLOTS:
|
||||
void finishedSlot() {
|
||||
QHttpNetworkReply *reply = (QHttpNetworkReply*) sender();
|
||||
Q_UNUSED(reply);
|
||||
receivedCount++;
|
||||
|
||||
if (receivedCount == requestCount)
|
||||
QTestEventLoop::instance().exitLoop();
|
||||
}
|
||||
};
|
||||
|
||||
void tst_QHttpNetworkConnection::getEmptyWithPipelining()
|
||||
{
|
||||
quint16 requestCount = 50;
|
||||
// use 2 connections.
|
||||
QHttpNetworkConnection connection(2, httpServerName());
|
||||
GetEmptyWithPipeliningReceiver receiver(requestCount);
|
||||
|
||||
QUrl url("http://" + httpServerName() + "/cgi-bin/echo.cgi"); // a get on this = getting an empty file
|
||||
QList<QHttpNetworkRequest*> requests;
|
||||
QList<QHttpNetworkReply*> replies;
|
||||
|
||||
for (int i = 0; i < requestCount; i++) {
|
||||
QHttpNetworkRequest *request = nullptr;
|
||||
request = new QHttpNetworkRequest(url, QHttpNetworkRequest::Get);
|
||||
request->setPipeliningAllowed(true);
|
||||
|
||||
requests.append(request);
|
||||
QHttpNetworkReply *reply = connection.sendRequest(*request);
|
||||
connect(reply, SIGNAL(finished()), &receiver, SLOT(finishedSlot()));
|
||||
replies.append(reply);
|
||||
}
|
||||
|
||||
QTestEventLoop::instance().enterLoop(20);
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
|
||||
qDeleteAll(requests);
|
||||
qDeleteAll(replies);
|
||||
}
|
||||
|
||||
class GetAndEverythingShouldBePipelinedReceiver : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
int receivedCount;
|
||||
int requestCount;
|
||||
GetAndEverythingShouldBePipelinedReceiver(int rq) : receivedCount(0),requestCount(rq) { }
|
||||
public Q_SLOTS:
|
||||
void finishedSlot() {
|
||||
QHttpNetworkReply *reply = (QHttpNetworkReply*) sender();
|
||||
Q_UNUSED(reply);
|
||||
receivedCount++;
|
||||
|
||||
if (receivedCount == requestCount)
|
||||
QTestEventLoop::instance().exitLoop();
|
||||
}
|
||||
};
|
||||
|
||||
void tst_QHttpNetworkConnection::getAndEverythingShouldBePipelined()
|
||||
{
|
||||
quint16 requestCount = 100;
|
||||
// use 1 connection.
|
||||
QHttpNetworkConnection connection(1, httpServerName());
|
||||
QUrl url("http://" + httpServerName() + "/qtest/rfc3252.txt");
|
||||
QList<QHttpNetworkRequest*> requests;
|
||||
QList<QHttpNetworkReply*> replies;
|
||||
|
||||
GetAndEverythingShouldBePipelinedReceiver receiver(requestCount);
|
||||
|
||||
for (int i = 0; i < requestCount; i++) {
|
||||
QHttpNetworkRequest *request = nullptr;
|
||||
request = new QHttpNetworkRequest(url, QHttpNetworkRequest::Get);
|
||||
request->setPipeliningAllowed(true);
|
||||
requests.append(request);
|
||||
QHttpNetworkReply *reply = connection.sendRequest(*request);
|
||||
connect(reply, SIGNAL(finished()), &receiver, SLOT(finishedSlot()));
|
||||
replies.append(reply);
|
||||
}
|
||||
QTestEventLoop::instance().enterLoop(40);
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
|
||||
qDeleteAll(requests);
|
||||
qDeleteAll(replies);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void tst_QHttpNetworkConnection::getAndThenDeleteObject_data()
|
||||
{
|
||||
QTest::addColumn<bool>("replyFirst");
|
||||
|
||||
QTest::newRow("delete-reply-first") << true;
|
||||
QTest::newRow("delete-connection-first") << false;
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkConnection::getAndThenDeleteObject()
|
||||
{
|
||||
// yes, this will leak if the testcase fails. I don't care. It must not fail then :P
|
||||
QHttpNetworkConnection *connection = new QHttpNetworkConnection(httpServerName());
|
||||
QHttpNetworkRequest request("http://" + httpServerName() + "/qtest/bigfile");
|
||||
QHttpNetworkReply *reply = connection->sendRequest(request);
|
||||
reply->setDownstreamLimited(true);
|
||||
|
||||
QTRY_VERIFY_WITH_TIMEOUT(reply->bytesAvailable(), 30000);
|
||||
QCOMPARE(reply->statusCode() ,200);
|
||||
QVERIFY(!reply->isFinished()); // must not be finished
|
||||
|
||||
QFETCH(bool, replyFirst);
|
||||
|
||||
if (replyFirst) {
|
||||
delete reply;
|
||||
delete connection;
|
||||
} else {
|
||||
delete connection;
|
||||
delete reply;
|
||||
}
|
||||
}
|
||||
|
||||
class TestTcpServer : public QTcpServer
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TestTcpServer() : errorCodeReports(0)
|
||||
{
|
||||
connect(this, &QTcpServer::newConnection, this, &TestTcpServer::onNewConnection);
|
||||
QVERIFY(listen(QHostAddress::LocalHost));
|
||||
}
|
||||
|
||||
int errorCodeReports;
|
||||
|
||||
public slots:
|
||||
void onNewConnection()
|
||||
{
|
||||
QTcpSocket *socket = nextPendingConnection();
|
||||
if (!socket)
|
||||
return;
|
||||
// close socket instantly!
|
||||
connect(socket, &QTcpSocket::readyRead, socket, &QTcpSocket::close);
|
||||
}
|
||||
|
||||
void onReply(QNetworkReply::NetworkError code)
|
||||
{
|
||||
QCOMPARE(code, QNetworkReply::RemoteHostClosedError);
|
||||
++errorCodeReports;
|
||||
}
|
||||
};
|
||||
|
||||
void tst_QHttpNetworkConnection::overlappingCloseAndWrite()
|
||||
{
|
||||
// server accepts connections, but closes the socket instantly
|
||||
TestTcpServer server;
|
||||
QNetworkAccessManager accessManager;
|
||||
|
||||
// ten requests are scheduled. All should result in an RemoteHostClosed...
|
||||
QUrl url;
|
||||
url.setScheme(QStringLiteral("http"));
|
||||
url.setHost(server.serverAddress().toString());
|
||||
url.setPort(server.serverPort());
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
QNetworkRequest request(url);
|
||||
QNetworkReply *reply = accessManager.get(request);
|
||||
QObject::connect(reply, &QNetworkReply::errorOccurred,
|
||||
&server, &TestTcpServer::onReply);
|
||||
}
|
||||
|
||||
QTRY_COMPARE(server.errorCodeReports, 10);
|
||||
}
|
||||
|
||||
|
||||
QTEST_MAIN(tst_QHttpNetworkConnection)
|
||||
#include "tst_qhttpnetworkconnection.moc"
|
18
tests/auto/network/access/qhttpnetworkreply/CMakeLists.txt
Normal file
18
tests/auto/network/access/qhttpnetworkreply/CMakeLists.txt
Normal file
@ -0,0 +1,18 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
if(NOT QT_FEATURE_private_tests)
|
||||
return()
|
||||
endif()
|
||||
|
||||
#####################################################################
|
||||
## tst_qhttpnetworkreply Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qhttpnetworkreply
|
||||
SOURCES
|
||||
tst_qhttpnetworkreply.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::NetworkPrivate
|
||||
)
|
@ -0,0 +1,236 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
|
||||
#include <QTest>
|
||||
#include <QtCore/QBuffer>
|
||||
#include <QtCore/QByteArray>
|
||||
#include <QtCore/QStringBuilder>
|
||||
|
||||
#include "private/qhttpnetworkconnection_p.h"
|
||||
|
||||
class tst_QHttpNetworkReply: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private Q_SLOTS:
|
||||
void parseHeader_data();
|
||||
void parseHeader();
|
||||
|
||||
void parseHeaderVerification_data();
|
||||
void parseHeaderVerification();
|
||||
|
||||
void parseEndOfHeader_data();
|
||||
void parseEndOfHeader();
|
||||
};
|
||||
|
||||
void tst_QHttpNetworkReply::parseHeader_data()
|
||||
{
|
||||
QTest::addColumn<QByteArray>("headers");
|
||||
QTest::addColumn<QStringList>("fields");
|
||||
QTest::addColumn<QStringList>("values");
|
||||
|
||||
QTest::newRow("no-fields") << QByteArray("\r\n") << QStringList() << QStringList();
|
||||
QTest::newRow("empty-field") << QByteArray("Set-Cookie: \r\n")
|
||||
<< (QStringList() << "Set-Cookie")
|
||||
<< (QStringList() << "");
|
||||
QTest::newRow("single-field") << QByteArray("Content-Type: text/html; charset=utf-8\r\n")
|
||||
<< (QStringList() << "Content-Type")
|
||||
<< (QStringList() << "text/html; charset=utf-8");
|
||||
QTest::newRow("single-field-continued") << QByteArray("Content-Type: text/html;\r\n"
|
||||
" charset=utf-8\r\n")
|
||||
<< (QStringList() << "Content-Type")
|
||||
<< (QStringList() << "text/html; charset=utf-8");
|
||||
QTest::newRow("single-field-on-five-lines")
|
||||
<< QByteArray("Name:\r\n first\r\n \r\n \r\n last\r\n") << (QStringList() << "Name")
|
||||
<< (QStringList() << "first last");
|
||||
|
||||
QTest::newRow("multi-field") << QByteArray("Content-Type: text/html; charset=utf-8\r\n"
|
||||
"Content-Length: 1024\r\n"
|
||||
"Content-Encoding: gzip\r\n")
|
||||
<< (QStringList() << "Content-Type" << "Content-Length" << "Content-Encoding")
|
||||
<< (QStringList() << "text/html; charset=utf-8" << "1024" << "gzip");
|
||||
QTest::newRow("multi-field-with-emtpy") << QByteArray("Content-Type: text/html; charset=utf-8\r\n"
|
||||
"Content-Length: 1024\r\n"
|
||||
"Set-Cookie: \r\n"
|
||||
"Content-Encoding: gzip\r\n")
|
||||
<< (QStringList() << "Content-Type" << "Content-Length" << "Set-Cookie" << "Content-Encoding")
|
||||
<< (QStringList() << "text/html; charset=utf-8" << "1024" << "" << "gzip");
|
||||
|
||||
QTest::newRow("lws-field") << QByteArray("Content-Type: text/html; charset=utf-8\r\n"
|
||||
"Content-Length:\r\n 1024\r\n"
|
||||
"Content-Encoding: gzip\r\n")
|
||||
<< (QStringList() << "Content-Type" << "Content-Length" << "Content-Encoding")
|
||||
<< (QStringList() << "text/html; charset=utf-8" << "1024" << "gzip");
|
||||
|
||||
QTest::newRow("duplicated-field") << QByteArray("Vary: Accept-Language\r\n"
|
||||
"Vary: Cookie\r\n"
|
||||
"Vary: User-Agent\r\n")
|
||||
<< (QStringList() << "Vary")
|
||||
<< (QStringList() << "Accept-Language, Cookie, User-Agent");
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkReply::parseHeader()
|
||||
{
|
||||
QFETCH(QByteArray, headers);
|
||||
QFETCH(QStringList, fields);
|
||||
QFETCH(QStringList, values);
|
||||
|
||||
QHttpNetworkReply reply;
|
||||
reply.parseHeader(headers);
|
||||
for (int i = 0; i < fields.size(); ++i) {
|
||||
//qDebug() << "field" << fields.at(i) << "value" << reply.headerField(fields.at(i)) << "expected" << values.at(i);
|
||||
QString field = reply.headerField(fields.at(i).toLatin1());
|
||||
QCOMPARE(field, values.at(i));
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkReply::parseHeaderVerification_data()
|
||||
{
|
||||
QTest::addColumn<QByteArray>("headers");
|
||||
QTest::addColumn<bool>("success");
|
||||
|
||||
QTest::newRow("no-header-fields") << QByteArray("\r\n") << true;
|
||||
QTest::newRow("starting-with-space") << QByteArray(" Content-Encoding: gzip\r\n") << false;
|
||||
QTest::newRow("starting-with-tab") << QByteArray("\tContent-Encoding: gzip\r\n") << false;
|
||||
QTest::newRow("only-colon") << QByteArray(":\r\n") << false;
|
||||
QTest::newRow("colon-and-value") << QByteArray(": only-value\r\n") << false;
|
||||
QTest::newRow("name-with-space") << QByteArray("Content Length: 10\r\n") << false;
|
||||
QTest::newRow("missing-colon-1") << QByteArray("Content-Encoding\r\n") << false;
|
||||
QTest::newRow("missing-colon-2")
|
||||
<< QByteArray("Content-Encoding\r\nContent-Length: 10\r\n") << false;
|
||||
QTest::newRow("missing-colon-3")
|
||||
<< QByteArray("Content-Encoding: gzip\r\nContent-Length\r\n") << false;
|
||||
QTest::newRow("header-field-too-long")
|
||||
<< (QByteArray("Content-Type: ")
|
||||
+ QByteArray(HeaderConstants::MAX_HEADER_FIELD_SIZE, 'a') + QByteArray("\r\n"))
|
||||
<< false;
|
||||
|
||||
QByteArray name = "Content-Type: ";
|
||||
QTest::newRow("max-header-field-size")
|
||||
<< (name + QByteArray(HeaderConstants::MAX_HEADER_FIELD_SIZE - name.size(), 'a')
|
||||
+ QByteArray("\r\n"))
|
||||
<< true;
|
||||
|
||||
QByteArray tooManyHeaders = QByteArray("Content-Type: text/html; charset=utf-8\r\n")
|
||||
.repeated(HeaderConstants::MAX_HEADER_FIELDS + 1);
|
||||
QTest::newRow("too-many-headers") << tooManyHeaders << false;
|
||||
|
||||
QByteArray maxHeaders = QByteArray("Content-Type: text/html; charset=utf-8\r\n")
|
||||
.repeated(HeaderConstants::MAX_HEADER_FIELDS);
|
||||
QTest::newRow("max-headers") << maxHeaders << true;
|
||||
|
||||
QByteArray firstValue(HeaderConstants::MAX_HEADER_FIELD_SIZE / 2, 'a');
|
||||
constexpr int obsFold = 1;
|
||||
QTest::newRow("max-continuation-size")
|
||||
<< (name + firstValue + QByteArray("\r\n ")
|
||||
+ QByteArray(HeaderConstants::MAX_HEADER_FIELD_SIZE - name.size()
|
||||
- firstValue.size() - obsFold,
|
||||
'b')
|
||||
+ QByteArray("\r\n"))
|
||||
<< true;
|
||||
QTest::newRow("too-long-continuation-size")
|
||||
<< (name + firstValue + QByteArray("\r\n ")
|
||||
+ QByteArray(HeaderConstants::MAX_HEADER_FIELD_SIZE - name.size()
|
||||
- firstValue.size() - obsFold + 1,
|
||||
'b')
|
||||
+ QByteArray("\r\n"))
|
||||
<< false;
|
||||
|
||||
auto appendLongHeaderElement = [](QByteArray &result, QByteArrayView name) {
|
||||
const qsizetype size = result.size();
|
||||
result += name;
|
||||
result += ": ";
|
||||
result.resize(size + HeaderConstants::MAX_HEADER_FIELD_SIZE, 'a');
|
||||
};
|
||||
QByteArray longHeader;
|
||||
constexpr qsizetype TrailerLength = sizeof("\r\n\r\n") - 1; // we ignore the trailing newlines
|
||||
longHeader.reserve(HeaderConstants::MAX_TOTAL_HEADER_SIZE + TrailerLength + 1);
|
||||
appendLongHeaderElement(longHeader, "Location");
|
||||
longHeader += "\r\n";
|
||||
appendLongHeaderElement(longHeader, "WWW-Authenticate");
|
||||
longHeader += "\r\nProxy-Authenticate: ";
|
||||
longHeader.resize(HeaderConstants::MAX_TOTAL_HEADER_SIZE, 'a');
|
||||
longHeader += "\r\n\r\n";
|
||||
|
||||
// Test with headers which are just large enough to fit our MAX_TOTAL_HEADER_SIZE limit:
|
||||
QTest::newRow("total-header-close-to-max-size") << longHeader << true;
|
||||
// Now add another character to make the total header size exceed the limit:
|
||||
longHeader.insert(HeaderConstants::MAX_TOTAL_HEADER_SIZE - TrailerLength, 'a');
|
||||
QTest::newRow("total-header-too-large") << longHeader << false;
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkReply::parseHeaderVerification()
|
||||
{
|
||||
QFETCH(QByteArray, headers);
|
||||
QFETCH(bool, success);
|
||||
QHttpNetworkReply reply;
|
||||
reply.parseHeader(headers);
|
||||
if (success && QByteArrayView(headers).trimmed().size())
|
||||
QVERIFY(reply.header().size() > 0);
|
||||
else
|
||||
QCOMPARE(reply.header().size(), 0);
|
||||
}
|
||||
|
||||
class TestHeaderSocket : public QAbstractSocket
|
||||
{
|
||||
public:
|
||||
explicit TestHeaderSocket(const QByteArray &input) : QAbstractSocket(QAbstractSocket::TcpSocket, nullptr)
|
||||
{
|
||||
inputBuffer.setData(input);
|
||||
inputBuffer.open(QIODevice::ReadOnly | QIODevice::Unbuffered);
|
||||
open(QIODevice::ReadOnly | QIODevice::Unbuffered);
|
||||
}
|
||||
|
||||
qint64 readData(char *data, qint64 maxlen) override { return inputBuffer.read(data, maxlen); }
|
||||
|
||||
QBuffer inputBuffer;
|
||||
};
|
||||
|
||||
class TestHeaderReply : public QHttpNetworkReply
|
||||
{
|
||||
public:
|
||||
QHttpNetworkReplyPrivate *replyPrivate() { return static_cast<QHttpNetworkReplyPrivate *>(d_ptr.data()); }
|
||||
};
|
||||
|
||||
void tst_QHttpNetworkReply::parseEndOfHeader_data()
|
||||
{
|
||||
QTest::addColumn<QByteArray>("headers");
|
||||
QTest::addColumn<qint64>("lengths");
|
||||
|
||||
QTest::newRow("CRLFCRLF") << QByteArray("Content-Type: text/html; charset=utf-8\r\n"
|
||||
"Content-Length:\r\n 1024\r\n"
|
||||
"Content-Encoding: gzip\r\n\r\nHTTPBODY")
|
||||
<< qint64(90);
|
||||
|
||||
QTest::newRow("CRLFLF") << QByteArray("Content-Type: text/html; charset=utf-8\r\n"
|
||||
"Content-Length:\r\n 1024\r\n"
|
||||
"Content-Encoding: gzip\r\n\nHTTPBODY")
|
||||
<< qint64(89);
|
||||
|
||||
QTest::newRow("LFCRLF") << QByteArray("Content-Type: text/html; charset=utf-8\r\n"
|
||||
"Content-Length:\r\n 1024\r\n"
|
||||
"Content-Encoding: gzip\n\r\nHTTPBODY")
|
||||
<< qint64(89);
|
||||
|
||||
QTest::newRow("LFLF") << QByteArray("Content-Type: text/html; charset=utf-8\r\n"
|
||||
"Content-Length:\r\n 1024\r\n"
|
||||
"Content-Encoding: gzip\n\nHTTPBODY")
|
||||
<< qint64(88);
|
||||
}
|
||||
|
||||
void tst_QHttpNetworkReply::parseEndOfHeader()
|
||||
{
|
||||
QFETCH(QByteArray, headers);
|
||||
QFETCH(qint64, lengths);
|
||||
|
||||
TestHeaderSocket socket(headers);
|
||||
|
||||
TestHeaderReply reply;
|
||||
|
||||
QHttpNetworkReplyPrivate *replyPrivate = reply.replyPrivate();
|
||||
qint64 headerBytes = replyPrivate->readHeader(&socket);
|
||||
QCOMPARE(headerBytes, lengths);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QHttpNetworkReply)
|
||||
#include "tst_qhttpnetworkreply.moc"
|
@ -0,0 +1,13 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qnetworkaccessmanager Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qnetworkaccessmanager
|
||||
SOURCES
|
||||
tst_qnetworkaccessmanager.cpp
|
||||
LIBRARIES
|
||||
Qt::Network
|
||||
)
|
@ -0,0 +1,38 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include <QtNetwork/QNetworkAccessManager>
|
||||
#include <QtNetwork/QNetworkReply>
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
|
||||
class tst_QNetworkAccessManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
tst_QNetworkAccessManager();
|
||||
|
||||
private slots:
|
||||
void alwaysCacheRequest();
|
||||
};
|
||||
|
||||
tst_QNetworkAccessManager::tst_QNetworkAccessManager()
|
||||
{
|
||||
}
|
||||
|
||||
void tst_QNetworkAccessManager::alwaysCacheRequest()
|
||||
{
|
||||
QNetworkAccessManager manager;
|
||||
|
||||
QNetworkRequest req;
|
||||
req.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysCache);
|
||||
QNetworkReply *reply = manager.get(req);
|
||||
reply->close();
|
||||
delete reply;
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QNetworkAccessManager)
|
||||
#include "tst_qnetworkaccessmanager.moc"
|
@ -0,0 +1,13 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qnetworkcachemetadata Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qnetworkcachemetadata
|
||||
SOURCES
|
||||
tst_qnetworkcachemetadata.cpp
|
||||
LIBRARIES
|
||||
Qt::Network
|
||||
)
|
@ -0,0 +1,309 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
|
||||
#include <QTest>
|
||||
#include <QBuffer>
|
||||
|
||||
#include <qabstractnetworkcache.h>
|
||||
|
||||
#define EXAMPLE_URL "http://user:pass@www.example.com/#foo"
|
||||
|
||||
class tst_QNetworkCacheMetaData : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void qnetworkcachemetadata_data();
|
||||
void qnetworkcachemetadata();
|
||||
|
||||
void expirationDate_data();
|
||||
void expirationDate();
|
||||
void isValid_data();
|
||||
void isValid();
|
||||
void lastModified_data();
|
||||
void lastModified();
|
||||
void operatorEqual_data();
|
||||
void operatorEqual();
|
||||
void operatorEqualEqual_data();
|
||||
void operatorEqualEqual();
|
||||
void rawHeaders_data();
|
||||
void rawHeaders();
|
||||
void saveToDisk_data();
|
||||
void saveToDisk();
|
||||
void url_data();
|
||||
void url();
|
||||
|
||||
void stream();
|
||||
};
|
||||
|
||||
// Subclass that exposes the protected functions.
|
||||
class SubQNetworkCacheMetaData : public QNetworkCacheMetaData
|
||||
{
|
||||
public:};
|
||||
|
||||
void tst_QNetworkCacheMetaData::qnetworkcachemetadata_data()
|
||||
{
|
||||
}
|
||||
|
||||
void tst_QNetworkCacheMetaData::qnetworkcachemetadata()
|
||||
{
|
||||
QNetworkCacheMetaData data;
|
||||
QCOMPARE(data.expirationDate(), QDateTime());
|
||||
QCOMPARE(data.isValid(), false);
|
||||
QCOMPARE(data.lastModified(), QDateTime());
|
||||
QCOMPARE(data.operator!=(QNetworkCacheMetaData()), false);
|
||||
QNetworkCacheMetaData metaData;
|
||||
QCOMPARE(data.operator=(metaData), QNetworkCacheMetaData());
|
||||
QCOMPARE(data.operator==(QNetworkCacheMetaData()), true);
|
||||
QCOMPARE(data.rawHeaders(), QNetworkCacheMetaData::RawHeaderList());
|
||||
QCOMPARE(data.saveToDisk(), true);
|
||||
QCOMPARE(data.url(), QUrl());
|
||||
data.setExpirationDate(QDateTime());
|
||||
data.setLastModified(QDateTime());
|
||||
data.setRawHeaders(QNetworkCacheMetaData::RawHeaderList());
|
||||
data.setSaveToDisk(false);
|
||||
data.setUrl(QUrl());
|
||||
}
|
||||
|
||||
void tst_QNetworkCacheMetaData::expirationDate_data()
|
||||
{
|
||||
QTest::addColumn<QDateTime>("expirationDate");
|
||||
QTest::newRow("null") << QDateTime();
|
||||
QTest::newRow("now") << QDateTime::currentDateTime();
|
||||
}
|
||||
|
||||
// public QDateTime expirationDate() const
|
||||
void tst_QNetworkCacheMetaData::expirationDate()
|
||||
{
|
||||
QFETCH(QDateTime, expirationDate);
|
||||
|
||||
SubQNetworkCacheMetaData data;
|
||||
|
||||
data.setExpirationDate(expirationDate);
|
||||
QCOMPARE(data.expirationDate(), expirationDate);
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(QNetworkCacheMetaData)
|
||||
void tst_QNetworkCacheMetaData::isValid_data()
|
||||
{
|
||||
QTest::addColumn<QNetworkCacheMetaData>("data");
|
||||
QTest::addColumn<bool>("isValid");
|
||||
|
||||
QNetworkCacheMetaData metaData;
|
||||
QTest::newRow("null") << metaData << false;
|
||||
|
||||
QNetworkCacheMetaData data1;
|
||||
data1.setUrl(QUrl(EXAMPLE_URL));
|
||||
QTest::newRow("valid-1") << data1 << true;
|
||||
|
||||
QNetworkCacheMetaData data2;
|
||||
QNetworkCacheMetaData::RawHeaderList headers;
|
||||
headers.append(QNetworkCacheMetaData::RawHeader("foo", "Bar"));
|
||||
data2.setRawHeaders(headers);
|
||||
QTest::newRow("valid-2") << data2 << true;
|
||||
|
||||
QNetworkCacheMetaData data3;
|
||||
data3.setLastModified(QDateTime::currentDateTime());
|
||||
QTest::newRow("valid-3") << data3 << true;
|
||||
|
||||
QNetworkCacheMetaData data4;
|
||||
data4.setExpirationDate(QDateTime::currentDateTime());
|
||||
QTest::newRow("valid-4") << data4 << true;
|
||||
|
||||
QNetworkCacheMetaData data5;
|
||||
data5.setSaveToDisk(false);
|
||||
QTest::newRow("valid-5") << data5 << true;
|
||||
}
|
||||
|
||||
// public bool isValid() const
|
||||
void tst_QNetworkCacheMetaData::isValid()
|
||||
{
|
||||
QFETCH(QNetworkCacheMetaData, data);
|
||||
QFETCH(bool, isValid);
|
||||
|
||||
QCOMPARE(data.isValid(), isValid);
|
||||
}
|
||||
|
||||
void tst_QNetworkCacheMetaData::lastModified_data()
|
||||
{
|
||||
QTest::addColumn<QDateTime>("lastModified");
|
||||
QTest::newRow("null") << QDateTime();
|
||||
QTest::newRow("now") << QDateTime::currentDateTime();
|
||||
}
|
||||
|
||||
// public QDateTime lastModified() const
|
||||
void tst_QNetworkCacheMetaData::lastModified()
|
||||
{
|
||||
QFETCH(QDateTime, lastModified);
|
||||
|
||||
SubQNetworkCacheMetaData data;
|
||||
|
||||
data.setLastModified(lastModified);
|
||||
QCOMPARE(data.lastModified(), lastModified);
|
||||
}
|
||||
|
||||
void tst_QNetworkCacheMetaData::operatorEqual_data()
|
||||
{
|
||||
QTest::addColumn<QNetworkCacheMetaData>("other");
|
||||
QTest::newRow("null") << QNetworkCacheMetaData();
|
||||
|
||||
QNetworkCacheMetaData data;
|
||||
data.setUrl(QUrl(EXAMPLE_URL));
|
||||
QNetworkCacheMetaData::RawHeaderList headers;
|
||||
headers.append(QNetworkCacheMetaData::RawHeader("foo", "Bar"));
|
||||
data.setRawHeaders(headers);
|
||||
data.setLastModified(QDateTime::currentDateTime());
|
||||
data.setExpirationDate(QDateTime::currentDateTime());
|
||||
data.setSaveToDisk(false);
|
||||
QTest::newRow("valid") << data;
|
||||
}
|
||||
|
||||
// public QNetworkCacheMetaData& operator=(QNetworkCacheMetaData const& other)
|
||||
void tst_QNetworkCacheMetaData::operatorEqual()
|
||||
{
|
||||
QFETCH(QNetworkCacheMetaData, other);
|
||||
|
||||
QNetworkCacheMetaData data = other;
|
||||
|
||||
QCOMPARE(data, other);
|
||||
}
|
||||
|
||||
void tst_QNetworkCacheMetaData::operatorEqualEqual_data()
|
||||
{
|
||||
QTest::addColumn<QNetworkCacheMetaData>("a");
|
||||
QTest::addColumn<QNetworkCacheMetaData>("b");
|
||||
QTest::addColumn<bool>("operatorEqualEqual");
|
||||
QTest::newRow("null") << QNetworkCacheMetaData() << QNetworkCacheMetaData() << true;
|
||||
|
||||
QNetworkCacheMetaData data1;
|
||||
data1.setUrl(QUrl(EXAMPLE_URL));
|
||||
QTest::newRow("valid-1-1") << data1 << QNetworkCacheMetaData() << false;
|
||||
QTest::newRow("valid-1-2") << data1 << data1 << true;
|
||||
|
||||
QNetworkCacheMetaData data2;
|
||||
QNetworkCacheMetaData::RawHeaderList headers;
|
||||
headers.append(QNetworkCacheMetaData::RawHeader("foo", "Bar"));
|
||||
data2.setRawHeaders(headers);
|
||||
QTest::newRow("valid-2-1") << data2 << QNetworkCacheMetaData() << false;
|
||||
QTest::newRow("valid-2-2") << data2 << data2 << true;
|
||||
QTest::newRow("valid-2-3") << data2 << data1 << false;
|
||||
|
||||
QNetworkCacheMetaData data3;
|
||||
data3.setLastModified(QDateTime::currentDateTime());
|
||||
QTest::newRow("valid-3-1") << data3 << QNetworkCacheMetaData() << false;
|
||||
QTest::newRow("valid-3-2") << data3 << data3 << true;
|
||||
QTest::newRow("valid-3-3") << data3 << data1 << false;
|
||||
QTest::newRow("valid-3-4") << data3 << data2 << false;
|
||||
|
||||
QNetworkCacheMetaData data4;
|
||||
data4.setExpirationDate(QDateTime::currentDateTime());
|
||||
QTest::newRow("valid-4-1") << data4 << QNetworkCacheMetaData() << false;
|
||||
QTest::newRow("valid-4-2") << data4 << data4 << true;
|
||||
QTest::newRow("valid-4-3") << data4 << data1 << false;
|
||||
QTest::newRow("valid-4-4") << data4 << data2 << false;
|
||||
QTest::newRow("valid-4-5") << data4 << data3 << false;
|
||||
|
||||
QNetworkCacheMetaData data5;
|
||||
data5.setSaveToDisk(false);
|
||||
QTest::newRow("valid-5-1") << data5 << QNetworkCacheMetaData() << false;
|
||||
QTest::newRow("valid-5-2") << data5 << data5 << true;
|
||||
QTest::newRow("valid-5-3") << data5 << data1 << false;
|
||||
QTest::newRow("valid-5-4") << data5 << data2 << false;
|
||||
QTest::newRow("valid-5-5") << data5 << data3 << false;
|
||||
QTest::newRow("valid-5-6") << data5 << data4 << false;
|
||||
}
|
||||
|
||||
// public bool operator==(QNetworkCacheMetaData const& other) const
|
||||
void tst_QNetworkCacheMetaData::operatorEqualEqual()
|
||||
{
|
||||
QFETCH(QNetworkCacheMetaData, a);
|
||||
QFETCH(QNetworkCacheMetaData, b);
|
||||
QFETCH(bool, operatorEqualEqual);
|
||||
|
||||
QCOMPARE(a == b, operatorEqualEqual);
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(QNetworkCacheMetaData::RawHeaderList)
|
||||
void tst_QNetworkCacheMetaData::rawHeaders_data()
|
||||
{
|
||||
QTest::addColumn<QNetworkCacheMetaData::RawHeaderList>("rawHeaders");
|
||||
QTest::newRow("null") << QNetworkCacheMetaData::RawHeaderList();
|
||||
QNetworkCacheMetaData::RawHeaderList headers;
|
||||
headers.append(QNetworkCacheMetaData::RawHeader("foo", "Bar"));
|
||||
QTest::newRow("valie") << headers;
|
||||
}
|
||||
|
||||
// public QNetworkCacheMetaData::RawHeaderList rawHeaders() const
|
||||
void tst_QNetworkCacheMetaData::rawHeaders()
|
||||
{
|
||||
QFETCH(QNetworkCacheMetaData::RawHeaderList, rawHeaders);
|
||||
|
||||
SubQNetworkCacheMetaData data;
|
||||
|
||||
data.setRawHeaders(rawHeaders);
|
||||
QCOMPARE(data.rawHeaders(), rawHeaders);
|
||||
}
|
||||
|
||||
void tst_QNetworkCacheMetaData::saveToDisk_data()
|
||||
{
|
||||
QTest::addColumn<bool>("saveToDisk");
|
||||
QTest::newRow("false") << false;
|
||||
QTest::newRow("true") << true;
|
||||
}
|
||||
|
||||
// public bool saveToDisk() const
|
||||
void tst_QNetworkCacheMetaData::saveToDisk()
|
||||
{
|
||||
QFETCH(bool, saveToDisk);
|
||||
|
||||
SubQNetworkCacheMetaData data;
|
||||
|
||||
data.setSaveToDisk(saveToDisk);
|
||||
QCOMPARE(data.saveToDisk(), saveToDisk);
|
||||
}
|
||||
|
||||
void tst_QNetworkCacheMetaData::url_data()
|
||||
{
|
||||
QTest::addColumn<QUrl>("url");
|
||||
QTest::addColumn<QUrl>("expected");
|
||||
QTest::newRow("null") << QUrl() << QUrl();
|
||||
QTest::newRow("valid") << QUrl(EXAMPLE_URL) << QUrl("http://user@www.example.com/");
|
||||
}
|
||||
|
||||
// public QUrl url() const
|
||||
void tst_QNetworkCacheMetaData::url()
|
||||
{
|
||||
QFETCH(QUrl, url);
|
||||
QFETCH(QUrl, expected);
|
||||
|
||||
SubQNetworkCacheMetaData data;
|
||||
data.setUrl(url);
|
||||
QCOMPARE(data.url(), expected);
|
||||
}
|
||||
|
||||
void tst_QNetworkCacheMetaData::stream()
|
||||
{
|
||||
QNetworkCacheMetaData data;
|
||||
data.setUrl(QUrl(EXAMPLE_URL));
|
||||
QNetworkCacheMetaData::RawHeaderList headers;
|
||||
headers.append(QNetworkCacheMetaData::RawHeader("foo", "Bar"));
|
||||
data.setRawHeaders(headers);
|
||||
data.setLastModified(QDateTime::currentDateTime());
|
||||
data.setExpirationDate(QDateTime::currentDateTime());
|
||||
data.setSaveToDisk(false);
|
||||
|
||||
QBuffer buffer;
|
||||
buffer.open(QIODevice::ReadWrite);
|
||||
QDataStream stream(&buffer);
|
||||
stream << data;
|
||||
|
||||
buffer.seek(0);
|
||||
QNetworkCacheMetaData data2;
|
||||
stream >> data2;
|
||||
QCOMPARE(data2, data);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QNetworkCacheMetaData)
|
||||
#include "tst_qnetworkcachemetadata.moc"
|
||||
|
13
tests/auto/network/access/qnetworkcookie/CMakeLists.txt
Normal file
13
tests/auto/network/access/qnetworkcookie/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qnetworkcookie Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qnetworkcookie
|
||||
SOURCES
|
||||
tst_qnetworkcookie.cpp
|
||||
LIBRARIES
|
||||
Qt::Network
|
||||
)
|
683
tests/auto/network/access/qnetworkcookie/tst_qnetworkcookie.cpp
Normal file
683
tests/auto/network/access/qnetworkcookie/tst_qnetworkcookie.cpp
Normal file
@ -0,0 +1,683 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
|
||||
#include <QTest>
|
||||
#include <QtNetwork/QNetworkCookie>
|
||||
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QTimeZone>
|
||||
#include <QtCore/QUrl>
|
||||
|
||||
class tst_QNetworkCookie: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void getterSetter();
|
||||
|
||||
void parseSingleCookie_data();
|
||||
void parseSingleCookie();
|
||||
|
||||
void parseMultipleCookies_data();
|
||||
void parseMultipleCookies();
|
||||
|
||||
void sameSite();
|
||||
};
|
||||
|
||||
void tst_QNetworkCookie::getterSetter()
|
||||
{
|
||||
QNetworkCookie cookie;
|
||||
QNetworkCookie otherCookie;
|
||||
|
||||
QCOMPARE(cookie, otherCookie);
|
||||
QCOMPARE(cookie, otherCookie);
|
||||
QVERIFY(!(cookie != otherCookie));
|
||||
|
||||
QVERIFY(!cookie.isSecure());
|
||||
QVERIFY(cookie.isSessionCookie());
|
||||
QVERIFY(!cookie.expirationDate().isValid());
|
||||
QVERIFY(cookie.domain().isEmpty());
|
||||
QVERIFY(cookie.path().isEmpty());
|
||||
QVERIFY(cookie.name().isEmpty());
|
||||
QVERIFY(cookie.value().isEmpty());
|
||||
|
||||
// change something
|
||||
cookie.setName("foo");
|
||||
QVERIFY(!(cookie == otherCookie));
|
||||
QVERIFY(cookie != otherCookie);
|
||||
|
||||
// test getters and setters:
|
||||
QCOMPARE(cookie.name(), QByteArray("foo"));
|
||||
cookie.setName(0);
|
||||
QVERIFY(cookie.name().isEmpty());
|
||||
|
||||
cookie.setValue("bar");
|
||||
QCOMPARE(cookie.value(), QByteArray("bar"));
|
||||
cookie.setValue(0);
|
||||
QVERIFY(cookie.value().isEmpty());
|
||||
|
||||
cookie.setPath("/");
|
||||
QCOMPARE(cookie.path(), QString("/"));
|
||||
cookie.setPath(QString());
|
||||
QVERIFY(cookie.path().isEmpty());
|
||||
|
||||
cookie.setDomain(".tld");
|
||||
QCOMPARE(cookie.domain(), QString(".tld"));
|
||||
cookie.setDomain(QString());
|
||||
QVERIFY(cookie.domain().isEmpty());
|
||||
|
||||
QDateTime now = QDateTime::currentDateTime();
|
||||
cookie.setExpirationDate(now);
|
||||
QCOMPARE(cookie.expirationDate(), now);
|
||||
QVERIFY(!cookie.isSessionCookie());
|
||||
cookie.setExpirationDate(QDateTime());
|
||||
QVERIFY(!cookie.expirationDate().isValid());
|
||||
QVERIFY(cookie.isSessionCookie());
|
||||
|
||||
cookie.setSecure(true);
|
||||
QVERIFY(cookie.isSecure());
|
||||
cookie.setSecure(false);
|
||||
QVERIFY(!cookie.isSecure());
|
||||
|
||||
QCOMPARE(cookie, otherCookie);
|
||||
}
|
||||
|
||||
void tst_QNetworkCookie::parseSingleCookie_data()
|
||||
{
|
||||
QTest::addColumn<QString>("cookieString");
|
||||
QTest::addColumn<QNetworkCookie>("expectedCookie");
|
||||
const auto utc = [](int year, int month, int day,
|
||||
int hour = 0, int minute = 0, int second = 0, int millis = 0) {
|
||||
return QDateTime(QDate(year, month, day),
|
||||
QTime(hour, minute, second, millis),
|
||||
QTimeZone::UTC);
|
||||
};
|
||||
|
||||
QNetworkCookie cookie;
|
||||
cookie.setName("a");
|
||||
QTest::newRow("basic") << "a=" << cookie;
|
||||
QTest::newRow("basic2") << " a=" << cookie;
|
||||
QTest::newRow("basic3") << "a= " << cookie;
|
||||
QTest::newRow("basic4") << " a= " << cookie;
|
||||
QTest::newRow("basic5") << " a= ;" << cookie;
|
||||
QTest::newRow("basic6") << " a=; " << cookie;
|
||||
QTest::newRow("basic7") << " a =" << cookie;
|
||||
QTest::newRow("basic8") << " a = " << cookie;
|
||||
|
||||
cookie.setValue("b");
|
||||
QTest::newRow("with-value") << "a=b" << cookie;
|
||||
QTest::newRow("with-value2") << " a=b" << cookie;
|
||||
QTest::newRow("with-value3") << "a=b " << cookie;
|
||||
QTest::newRow("with-value4") << " a=b " << cookie;
|
||||
QTest::newRow("with-value5") << " a=b ;" << cookie;
|
||||
QTest::newRow("with-value6") << "a =b" << cookie;
|
||||
QTest::newRow("with-value7") << "a= b" << cookie;
|
||||
QTest::newRow("with-value8") << "a = b" << cookie;
|
||||
QTest::newRow("with-value9") << "a = b " << cookie;
|
||||
|
||||
cookie.setValue("\",\"");
|
||||
QTest::newRow("with-value-with-special1") << "a = \",\" " << cookie;
|
||||
cookie.setValue("\"");
|
||||
QTest::newRow("with-value-with-special2") << "a = \";\" " << cookie;
|
||||
cookie.setValue("\" \"");
|
||||
QTest::newRow("with-value-with-special3") << "a = \" \" " << cookie;
|
||||
cookie.setValue("\"\\\"\"");
|
||||
QTest::newRow("with-value-with-special4") << "a = \"\\\"\" " << cookie;
|
||||
cookie.setValue("\"\\\"a, b");
|
||||
QTest::newRow("with-value-with-special5") << "a = \"\\\"a, b; c\\\"\"" << cookie;
|
||||
// RFC6265 states that cookie values shouldn't contain commas, but we still allow them
|
||||
// since they are only reserved for future compatibility and other browsers do the same.
|
||||
cookie.setValue(",");
|
||||
QTest::newRow("with-value-with-special6") << "a = ," << cookie;
|
||||
cookie.setValue(",b");
|
||||
QTest::newRow("with-value-with-special7") << "a = ,b" << cookie;
|
||||
cookie.setValue("b,");
|
||||
QTest::newRow("with-value-with-special8") << "a = b," << cookie;
|
||||
|
||||
cookie.setValue("b c");
|
||||
QTest::newRow("with-value-with-whitespace") << "a = b c" << cookie;
|
||||
|
||||
cookie.setValue("\"b\"");
|
||||
QTest::newRow("quoted-value") << "a = \"b\"" << cookie;
|
||||
cookie.setValue("\"b c\"");
|
||||
QTest::newRow("quoted-value-with-whitespace") << "a = \"b c\"" << cookie;
|
||||
|
||||
cookie.setValue("b");
|
||||
cookie.setSecure(true);
|
||||
QTest::newRow("secure") << "a=b;secure" << cookie;
|
||||
QTest::newRow("secure2") << "a=b;secure " << cookie;
|
||||
QTest::newRow("secure3") << "a=b; secure" << cookie;
|
||||
QTest::newRow("secure4") << "a=b; secure " << cookie;
|
||||
QTest::newRow("secure5") << "a=b ;secure" << cookie;
|
||||
QTest::newRow("secure6") << "a=b ;secure " << cookie;
|
||||
QTest::newRow("secure7") << "a=b ; secure " << cookie;
|
||||
QTest::newRow("secure8") << "a=b; Secure" << cookie;
|
||||
|
||||
cookie.setSecure(false);
|
||||
cookie.setHttpOnly(true);
|
||||
QTest::newRow("httponly") << "a=b;httponly" << cookie;
|
||||
QTest::newRow("httponly2") << "a=b;HttpOnly " << cookie;
|
||||
QTest::newRow("httponly3") << "a=b; httpOnly" << cookie;
|
||||
QTest::newRow("httponly4") << "a=b; HttpOnly " << cookie;
|
||||
QTest::newRow("httponly5") << "a=b ;HttpOnly" << cookie;
|
||||
QTest::newRow("httponly6") << "a=b ;httponly " << cookie;
|
||||
QTest::newRow("httponly7") << "a=b ; HttpOnly " << cookie;
|
||||
QTest::newRow("httponly8") << "a=b; Httponly" << cookie;
|
||||
|
||||
cookie.setHttpOnly(false);
|
||||
cookie.setPath("/");
|
||||
QTest::newRow("path1") << "a=b;path=/" << cookie;
|
||||
QTest::newRow("path2") << "a=b; path=/" << cookie;
|
||||
QTest::newRow("path3") << "a=b;path=/ " << cookie;
|
||||
QTest::newRow("path4") << "a=b;path =/ " << cookie;
|
||||
QTest::newRow("path5") << "a=b;path= / " << cookie;
|
||||
QTest::newRow("path6") << "a=b;path = / " << cookie;
|
||||
QTest::newRow("path7") << "a=b;Path = / " << cookie;
|
||||
QTest::newRow("path8") << "a=b; PATH = / " << cookie;
|
||||
|
||||
cookie.setPath("/foo");
|
||||
QTest::newRow("path9") << "a=b;path=/foo" << cookie;
|
||||
|
||||
// some weird paths:
|
||||
cookie.setPath("/with%20spaces");
|
||||
QTest::newRow("path-with-spaces") << "a=b;path=/with%20spaces" << cookie;
|
||||
QTest::newRow("path-with-spaces2") << "a=b; path=/with%20spaces " << cookie;
|
||||
cookie.setPath(QString());
|
||||
QTest::newRow("invalid-path-with-spaces3") << "a=b; path=\"/with spaces\"" << cookie;
|
||||
QTest::newRow("invalid-path-with-spaces4") << "a=b; path = \"/with spaces\" " << cookie;
|
||||
cookie.setPath("/with spaces");
|
||||
QTest::newRow("path-with-spaces5") << "a=b; path=/with spaces" << cookie;
|
||||
QTest::newRow("path-with-spaces6") << "a=b; path = /with spaces " << cookie;
|
||||
|
||||
cookie.setPath("/with\"Quotes");
|
||||
QTest::newRow("path-with-quotes") << "a=b; path = /with\"Quotes" << cookie;
|
||||
cookie.setPath(QString());
|
||||
QTest::newRow("invalid-path-with-quotes2") << "a=b; path = \"/with\\\"Quotes\"" << cookie;
|
||||
|
||||
cookie.setPath(QString::fromUtf8("/R\303\251sum\303\251"));
|
||||
QTest::newRow("path-with-utf8") << QString::fromUtf8("a=b;path=/R\303\251sum\303\251") << cookie;
|
||||
cookie.setPath("/R%C3%A9sum%C3%A9");
|
||||
QTest::newRow("path-with-utf8-2") << "a=b;path=/R%C3%A9sum%C3%A9" << cookie;
|
||||
|
||||
cookie.setPath(QString());
|
||||
cookie.setDomain("qt-project.org");
|
||||
QTest::newRow("plain-domain1") << "a=b;domain=qt-project.org" << cookie;
|
||||
QTest::newRow("plain-domain2") << "a=b; domain=qt-project.org " << cookie;
|
||||
QTest::newRow("plain-domain3") << "a=b;domain=QT-PROJECT.ORG" << cookie;
|
||||
QTest::newRow("plain-domain4") << "a=b;DOMAIN = QT-PROJECT.ORG" << cookie;
|
||||
|
||||
cookie.setDomain(".qt-project.org");
|
||||
QTest::newRow("dot-domain1") << "a=b;domain=.qt-project.org" << cookie;
|
||||
QTest::newRow("dot-domain2") << "a=b; domain=.qt-project.org" << cookie;
|
||||
QTest::newRow("dot-domain3") << "a=b; domain=.QT-PROJECT.ORG" << cookie;
|
||||
QTest::newRow("dot-domain4") << "a=b; Domain = .QT-PROJECT.ORG" << cookie;
|
||||
|
||||
cookie.setDomain(QString::fromUtf8(".d\303\270gn\303\245pent.troll.no"));
|
||||
QTest::newRow("idn-domain1") << "a=b;domain=.xn--dgnpent-gxa2o.troll.no" << cookie;
|
||||
QTest::newRow("idn-domain2") << QString::fromUtf8("a=b;domain=.d\303\270gn\303\245pent.troll.no") << cookie;
|
||||
QTest::newRow("idn-domain3") << "a=b;domain=.XN--DGNPENT-GXA2O.TROLL.NO" << cookie;
|
||||
QTest::newRow("idn-domain4") << QString::fromUtf8("a=b;domain=.D\303\230GN\303\205PENT.troll.NO") << cookie;
|
||||
|
||||
cookie.setDomain(".qt-project.org");
|
||||
cookie.setPath("/");
|
||||
QTest::newRow("two-fields") << "a=b;domain=.qt-project.org;path=/" << cookie;
|
||||
QTest::newRow("two-fields2") << "a=b; domain=.qt-project.org; path=/" << cookie;
|
||||
QTest::newRow("two-fields3") << "a=b; domain=.qt-project.org ; path=/ " << cookie;
|
||||
QTest::newRow("two-fields4") << "a=b;path=/; domain=.qt-project.org" << cookie;
|
||||
QTest::newRow("two-fields5") << "a=b; path=/ ; domain=.qt-project.org" << cookie;
|
||||
QTest::newRow("two-fields6") << "a=b; path= / ; domain =.qt-project.org" << cookie;
|
||||
|
||||
cookie.setSecure(true);
|
||||
QTest::newRow("three-fields") << "a=b;domain=.qt-project.org;path=/;secure" << cookie;
|
||||
QTest::newRow("three-fields2") << "a=b;secure;path=/;domain=.qt-project.org" << cookie;
|
||||
QTest::newRow("three-fields3") << "a=b;secure;domain=.qt-project.org; path=/" << cookie;
|
||||
QTest::newRow("three-fields4") << "a = b;secure;domain=.qt-project.org; path=/" << cookie;
|
||||
|
||||
cookie = QNetworkCookie();
|
||||
cookie.setName("a");
|
||||
cookie.setValue("b");
|
||||
cookie.setExpirationDate(utc(2012, 1, 29, 23, 59, 59));
|
||||
QTest::newRow("broken-expiration1") << "a=b; expires=Sun, 29-Jan-2012 23:59:59;" << cookie;
|
||||
|
||||
cookie.setExpirationDate(utc(1999, 11, 9, 23, 12, 40));
|
||||
QTest::newRow("expiration1") << "a=b;expires=Wednesday, 09-Nov-1999 23:12:40 GMT" << cookie;
|
||||
QTest::newRow("expiration2") << "a=b;expires=Wed, 09-Nov-1999 23:12:40 GMT" << cookie;
|
||||
QTest::newRow("expiration3") << "a=b; expires=Wednesday, 09-Nov-1999 23:12:40 GMT " << cookie;
|
||||
QTest::newRow("expiration-utc") << "a=b;expires=Wednesday, 09-Nov-1999 23:12:40 UTC" << cookie;
|
||||
|
||||
cookie.setExpirationDate(utc(1989, 4, 14, 3, 20));
|
||||
QTest::newRow("time-0") << "a=b;expires=14 Apr 89 03:20" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 4, 14, 3, 20, 12));
|
||||
QTest::newRow("time-1") << "a=b;expires=14 Apr 89 03:20:12" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 4, 14, 3, 20, 12, 88));
|
||||
QTest::newRow("time-2") << "a=b;expires=14 Apr 89 03:20:12.88" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 4, 14, 3, 20, 12, 88));
|
||||
QTest::newRow("time-3") << "a=b;expires=14 Apr 89 03:20:12.88am" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 4, 14, 15, 20, 12, 88));
|
||||
QTest::newRow("time-4") << "a=b;expires=14 Apr 89 03:20:12.88pm" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 4, 14, 3, 20, 12, 88));
|
||||
QTest::newRow("time-5") << "a=b;expires=14 Apr 89 03:20:12.88 Am" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 4, 14, 15, 20, 12, 88));
|
||||
QTest::newRow("time-6") << "a=b;expires=14 Apr 89 03:20:12.88 PM" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 4, 14, 15, 20, 12, 88));
|
||||
QTest::newRow("time-7") << "a=b;expires=14 Apr 89 3:20:12.88 PM" << cookie;
|
||||
|
||||
// normal months
|
||||
cookie.setExpirationDate(utc(1989, 1, 1, 1, 1));
|
||||
QTest::newRow("months-1") << "a=b;expires=Jan 1 89 1:1" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 2, 1, 1, 1));
|
||||
QTest::newRow("months-2") << "a=b;expires=Feb 1 89 1:1" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 3, 1, 1, 1));
|
||||
QTest::newRow("months-3") << "a=b;expires=mar 1 89 1:1" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 4, 1, 1, 1));
|
||||
QTest::newRow("months-4") << "a=b;expires=Apr 1 89 1:1" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 5, 1, 1, 1));
|
||||
QTest::newRow("months-5") << "a=b;expires=May 1 89 1:1" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 6, 1, 1, 1));
|
||||
QTest::newRow("months-6") << "a=b;expires=Jun 1 89 1:1" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 7, 1, 1, 1));
|
||||
QTest::newRow("months-7") << "a=b;expires=Jul 1 89 1:1" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 8, 1, 1, 1));
|
||||
QTest::newRow("months-8") << "a=b;expires=Aug 1 89 1:1" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 9, 1, 1, 1));
|
||||
QTest::newRow("months-9") << "a=b;expires=Sep 1 89 1:1" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 10, 1, 1, 1));
|
||||
QTest::newRow("months-10") << "a=b;expires=Oct 1 89 1:1" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 11, 1, 1, 1));
|
||||
QTest::newRow("months-11") << "a=b;expires=Nov 1 89 1:1" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 12, 1, 1, 1));
|
||||
QTest::newRow("months-12") << "a=b;expires=Dec 1 89 1:1" << cookie;
|
||||
|
||||
// extra months
|
||||
cookie.setExpirationDate(utc(1989, 12, 1, 1, 1));
|
||||
QTest::newRow("months-13") << "a=b;expires=December 1 89 1:1" << cookie;
|
||||
QTest::newRow("months-14") << "a=b;expires=1 89 1:1 Dec" << cookie;
|
||||
//cookie.setExpirationDate(QDateTime());
|
||||
//QTest::newRow("months-15") << "a=b;expires=1 89 1:1 De" << cookie;
|
||||
cookie.setExpirationDate(utc(2024, 2, 29, 1, 1));
|
||||
QTest::newRow("months-16") << "a=b;expires=2024 29 Feb 1:1" << cookie;
|
||||
cookie.setExpirationDate(utc(2024, 2, 29, 1, 1));
|
||||
QTest::newRow("months-17") << "a=b;expires=Fri, 29-Feb-2024 01:01:00 GMT" << cookie;
|
||||
QTest::newRow("months-18") << "a=b;expires=2024 29 Feb 1:1 GMT" << cookie;
|
||||
|
||||
// normal offsets
|
||||
cookie.setExpirationDate(utc(1989, 1, 1));
|
||||
QTest::newRow("zoneoffset-0") << "a=b;expires=Jan 1 89 8:0 PST" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 1, 1));
|
||||
QTest::newRow("zoneoffset-1") << "a=b;expires=Jan 1 89 8:0 PDT" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 1, 1));
|
||||
QTest::newRow("zoneoffset-2") << "a=b;expires=Jan 1 89 7:0 MST" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 1, 1));
|
||||
QTest::newRow("zoneoffset-3") << "a=b;expires=Jan 1 89 7:0 MDT" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 1, 1));
|
||||
QTest::newRow("zoneoffset-4") << "a=b;expires=Jan 1 89 6:0 CST" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 1, 1));
|
||||
QTest::newRow("zoneoffset-5") << "a=b;expires=Jan 1 89 6:0 CDT" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 1, 1));
|
||||
QTest::newRow("zoneoffset-6") << "a=b;expires=Jan 1 89 5:0 EST" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 1, 1));
|
||||
QTest::newRow("zoneoffset-7") << "a=b;expires=Jan 1 89 5:0 EDT" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 1, 1));
|
||||
QTest::newRow("zoneoffset-8") << "a=b;expires=Jan 1 89 4:0 AST" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 1, 1));
|
||||
QTest::newRow("zoneoffset-9") << "a=b;expires=Jan 1 89 3:0 NST" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 1, 1));
|
||||
QTest::newRow("zoneoffset-10") << "a=b;expires=Jan 1 89 0:0 GMT" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 1, 1));
|
||||
QTest::newRow("zoneoffset-11") << "a=b;expires=Jan 1 89 0:0 BST" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 1, 2));
|
||||
QTest::newRow("zoneoffset-12") << "a=b;expires=Jan 1 89 23:0 MET" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 1, 2));
|
||||
QTest::newRow("zoneoffset-13") << "a=b;expires=Jan 1 89 22:0 EET" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 1, 2));
|
||||
QTest::newRow("zoneoffset-14") << "a=b;expires=Jan 1 89 15:0 JST" << cookie;
|
||||
|
||||
// extra offsets
|
||||
cookie.setExpirationDate(utc(1989, 1, 2));
|
||||
QTest::newRow("zoneoffset-15") << "a=b;expires=Jan 1 89 15:0 JST+1" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 1, 1, 1));
|
||||
QTest::newRow("zoneoffset-16") << "a=b;expires=Jan 1 89 0:0 GMT+1" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 1, 1));
|
||||
QTest::newRow("zoneoffset-17") << "a=b;expires=Jan 1 89 1:0 GMT-1" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 1, 1, 1));
|
||||
QTest::newRow("zoneoffset-18") << "a=b;expires=Jan 1 89 0:0 GMT+01" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 1, 1, 1, 5));
|
||||
QTest::newRow("zoneoffset-19") << "a=b;expires=Jan 1 89 0:0 GMT+0105" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 1, 1));
|
||||
QTest::newRow("zoneoffset-20") << "a=b;expires=Jan 1 89 0:0 GMT+015" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 1, 1));
|
||||
QTest::newRow("zoneoffset-21") << "a=b;expires=Jan 1 89 0:0 GM" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 1, 1));
|
||||
QTest::newRow("zoneoffset-22") << "a=b;expires=Jan 1 89 0:0 GMT" << cookie;
|
||||
|
||||
// offsets from gmt
|
||||
cookie.setExpirationDate(utc(1989, 1, 1, 1));
|
||||
QTest::newRow("zoneoffset-23") << "a=b;expires=Jan 1 89 0:0 +1" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 1, 1, 1));
|
||||
QTest::newRow("zoneoffset-24") << "a=b;expires=Jan 1 89 0:0 +01" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 1, 1, 1, 1));
|
||||
QTest::newRow("zoneoffset-25") << "a=b;expires=Jan 1 89 0:0 +0101" << cookie;
|
||||
cookie.setExpirationDate(utc(1989, 1, 1));
|
||||
QTest::newRow("zoneoffset-26") << "a=b;expires=Jan 1 89 1:0 -1" << cookie;
|
||||
|
||||
// Y2k
|
||||
cookie.setExpirationDate(utc(2000, 1, 1));
|
||||
QTest::newRow("year-0") << "a=b;expires=Jan 1 00 0:0" << cookie;
|
||||
cookie.setExpirationDate(utc(1970, 1, 1));
|
||||
QTest::newRow("year-1") << "a=b;expires=Jan 1 70 0:0" << cookie;
|
||||
cookie.setExpirationDate(utc(1971, 1, 1));
|
||||
QTest::newRow("year-2") << "a=b;expires=Jan 1 71 0:0" << cookie;
|
||||
|
||||
// Day, month, year
|
||||
cookie.setExpirationDate(utc(2013, 1, 2));
|
||||
QTest::newRow("date-0") << "a=b;expires=Jan 2 13 0:0" << cookie;
|
||||
QTest::newRow("date-1") << "a=b;expires=1-2-13 0:0" << cookie;
|
||||
QTest::newRow("date-2") << "a=b;expires=1/2/13 0:0" << cookie;
|
||||
QTest::newRow("date-3") << "a=b;expires=Jan 2 13 0:0" << cookie;
|
||||
QTest::newRow("date-4") << "a=b;expires=Jan 2, 13 0:0" << cookie;
|
||||
QTest::newRow("date-5") << "a=b;expires=1-2-13 0:0" << cookie;
|
||||
QTest::newRow("date-6") << "a=b;expires=1/2/13 0:0" << cookie;
|
||||
|
||||
// Known Year, determine month and day
|
||||
cookie.setExpirationDate(utc(1995, 1, 13));
|
||||
QTest::newRow("knownyear-0") << "a=b;expires=13/1/95 0:0" << cookie;
|
||||
QTest::newRow("knownyear-1") << "a=b;expires=95/13/1 0:0" << cookie;
|
||||
QTest::newRow("knownyear-2") << "a=b;expires=1995/1/13 0:0" << cookie;
|
||||
QTest::newRow("knownyear-3") << "a=b;expires=1995/13/1 0:0" << cookie;
|
||||
cookie.setExpirationDate(utc(1995, 1, 2));
|
||||
QTest::newRow("knownyear-4") << "a=b;expires=1/2/95 0:0" << cookie;
|
||||
QTest::newRow("knownyear-5") << "a=b;expires=95/1/2 0:0" << cookie;
|
||||
|
||||
// Known Year, Known day, determining month
|
||||
cookie.setExpirationDate(utc(1995, 1, 13));
|
||||
QTest::newRow("knownYD-0") << "a=b;expires=13/1/95 0:0" << cookie;
|
||||
QTest::newRow("knownYD-1") << "a=b;expires=1/13/95 0:0" << cookie;
|
||||
QTest::newRow("knownYD-2") << "a=b;expires=95/13/1 0:0" << cookie;
|
||||
QTest::newRow("knownYD-3") << "a=b;expires=95/1/13 0:0" << cookie;
|
||||
|
||||
// Month comes before Year
|
||||
cookie.setExpirationDate(utc(2021, 03, 26));
|
||||
QTest::newRow("month-0") << "a=b;expires=26/03/21 0:0" << cookie;
|
||||
cookie.setExpirationDate(utc(2015, 12, 30, 16, 25));
|
||||
QTest::newRow("month-1") << "a=b;expires=wed 16:25pm December 2015 30" << cookie;
|
||||
cookie.setExpirationDate(utc(2031, 11, 11, 16, 25));
|
||||
QTest::newRow("month-2") << "a=b;expires=16:25 11 31 11" << cookie;
|
||||
|
||||
// The very ambiguous cases
|
||||
// Matching Firefox's behavior of guessing month, day, year in those cases
|
||||
cookie.setExpirationDate(utc(2013, 10, 2));
|
||||
QTest::newRow("ambiguousd-0") << "a=b;expires=10/2/13 0:0" << cookie;
|
||||
cookie.setExpirationDate(utc(2013, 2, 10));
|
||||
QTest::newRow("ambiguousd-1") << "a=b;expires=2/10/13 0:0" << cookie;
|
||||
cookie.setExpirationDate(utc(2010, 2, 3));
|
||||
QTest::newRow("ambiguousd-2") << "a=b;expires=2/3/10 0:0" << cookie;
|
||||
|
||||
// FYI If you try these in Firefox it won't set a cookie for the following two string
|
||||
// because 03 is turned into the year at which point it is expired
|
||||
cookie.setExpirationDate(utc(2003, 2, 10));
|
||||
QTest::newRow("ambiguousd-3") << "a=b;expires=2/10/3 0:0" << cookie;
|
||||
cookie.setExpirationDate(utc(2003, 10, 2));
|
||||
QTest::newRow("ambiguousd-4") << "a=b;expires=10/2/3 0:0" << cookie;
|
||||
|
||||
// These are the cookies that firefox's source says it can parse
|
||||
cookie.setExpirationDate(utc(1989, 4, 14, 3, 20));
|
||||
QTest::newRow("firefox-0") << "a=b;expires=14 Apr 89 03:20" << cookie;
|
||||
|
||||
cookie.setExpirationDate(utc(1989, 4, 14, 3, 20));
|
||||
QTest::newRow("firefox-1") << "a=b;expires=14 Apr 89 03:20 GMT" << cookie;
|
||||
|
||||
cookie.setExpirationDate(utc(1989, 3, 17, 4, 1, 33));
|
||||
QTest::newRow("firefox-2") << "a=b;expires=Fri, 17 Mar 89 4:01:33" << cookie;
|
||||
|
||||
cookie.setExpirationDate(utc(1989, 3, 17, 4, 1));
|
||||
QTest::newRow("firefox-3") << "a=b;expires=Fri, 17 Mar 89 4:01 GMT" << cookie;
|
||||
|
||||
cookie.setExpirationDate(utc(1989, 1, 16, 16-8, 12));
|
||||
QTest::newRow("firefox-4") << "a=b;expires=Mon Jan 16 16:12 PDT 1989" << cookie;
|
||||
|
||||
cookie.setExpirationDate(utc(1989, 1, 16, 17, 42));
|
||||
QTest::newRow("firefox-5") << "a=b;expires=Mon Jan 16 16:12 +0130 1989" << cookie;
|
||||
|
||||
cookie.setExpirationDate(utc(1992, 5, 6, 16-9, 41));
|
||||
QTest::newRow("firefox-6") << "a=b;expires=6 May 1992 16:41-JST (Wednesday)" << cookie;
|
||||
|
||||
cookie.setExpirationDate(utc(1993, 8, 22, 10, 59, 12, 82));
|
||||
QTest::newRow("firefox-7") << "a=b;expires=22-AUG-1993 10:59:12.82" << cookie;
|
||||
|
||||
cookie.setExpirationDate(utc(1993, 8, 22, 22, 59));
|
||||
QTest::newRow("firefox-8") << "a=b;expires=22-AUG-1993 10:59pm" << cookie;
|
||||
|
||||
cookie.setExpirationDate(utc(1993, 8, 22, 12, 59));
|
||||
QTest::newRow("firefox-9") << "a=b;expires=22-AUG-1993 12:59am" << cookie;
|
||||
|
||||
cookie.setExpirationDate(utc(1993, 8, 22, 12, 59));
|
||||
QTest::newRow("firefox-10") << "a=b;expires=22-AUG-1993 12:59 PM" << cookie;
|
||||
|
||||
cookie.setExpirationDate(utc(1995, 8, 4, 15, 54));
|
||||
QTest::newRow("firefox-11") << "a=b;expires=Friday, August 04, 1995 3:54 PM" << cookie;
|
||||
|
||||
cookie.setExpirationDate(utc(1995, 6, 21, 16, 24, 34));
|
||||
QTest::newRow("firefox-12") << "a=b;expires=06/21/95 04:24:34 PM" << cookie;
|
||||
|
||||
cookie.setExpirationDate(utc(1995, 6, 20, 21, 7));
|
||||
QTest::newRow("firefox-13") << "a=b;expires=20/06/95 21:07" << cookie;
|
||||
|
||||
cookie.setExpirationDate(utc(1995, 6, 8, 19-5, 32, 48));
|
||||
QTest::newRow("firefox-14") << "a=b;expires=95-06-08 19:32:48 EDT" << cookie;
|
||||
|
||||
// Edge cases caught by fuzzing
|
||||
// These are about the default cause creates dates that don't exits
|
||||
cookie.setExpirationDate(utc(2030, 2, 25, 1, 1));
|
||||
QTest::newRow("fuzz-0") << "a=b; expires=30 -000002 1:1 25;" << cookie;
|
||||
|
||||
cookie.setExpirationDate(utc(2031, 11, 20, 1, 1));
|
||||
QTest::newRow("fuzz-1") << "a=b; expires=31 11 20 1:1;" << cookie;
|
||||
|
||||
// April only has 30 days
|
||||
cookie.setExpirationDate(utc(2031, 4, 30, 1, 1));
|
||||
QTest::newRow("fuzz-2") << "a=b; expires=31 30 4 1:1" << cookie;
|
||||
|
||||
// 9 must be the month so 31 can't be the day
|
||||
cookie.setExpirationDate(utc(2031, 9, 21, 1, 1));
|
||||
QTest::newRow("fuzz-3") << "a=b; expires=31 21 9 1:1" << cookie;
|
||||
|
||||
// Year is known, then fallback to defaults of filling in month and day
|
||||
cookie.setExpirationDate(utc(2031, 11, 1, 1, 1));
|
||||
QTest::newRow("fuzz-4") << "a=b; expires=31 11 01 1:1" << cookie;
|
||||
|
||||
// 2 must be the month so 30 can't be the day
|
||||
cookie.setExpirationDate(utc(2030, 2, 20, 1, 1));
|
||||
QTest::newRow("fuzz-5") << "a=b; expires=30 02 20 1:1" << cookie;
|
||||
|
||||
cookie.setExpirationDate(utc(2021, 12, 22, 1, 1));
|
||||
QTest::newRow("fuzz-6") << "a=b; expires=2021 12 22 1:1" << cookie;
|
||||
|
||||
cookie.setExpirationDate(utc(2029, 2, 23, 1, 1));
|
||||
QTest::newRow("fuzz-7") << "a=b; expires=29 23 Feb 1:1" << cookie;
|
||||
|
||||
// 11 and 6 don't have 31 days
|
||||
cookie.setExpirationDate(utc(2031, 11, 06, 1, 1));
|
||||
QTest::newRow("fuzz-8") << "a=b; expires=31 11 06 1:1" << cookie;
|
||||
|
||||
// two-digit years:
|
||||
// from 70 until 99, we assume 20th century
|
||||
cookie.setExpirationDate(utc(1999, 11, 9, 23, 12, 40));
|
||||
QTest::newRow("expiration-2digit1") << "a=b; expires=Wednesday, 09-Nov-99 23:12:40 GMT " << cookie;
|
||||
cookie.setExpirationDate(utc(1970, 1, 1, 23, 12, 40));
|
||||
QTest::newRow("expiration-2digit2") << "a=b; expires=Thursday, 01-Jan-70 23:12:40 GMT " << cookie;
|
||||
// from 00 until 69, we assume 21st century
|
||||
cookie.setExpirationDate(utc(2000, 1, 1, 23, 12, 40));
|
||||
QTest::newRow("expiration-2digit3") << "a=b; expires=Saturday, 01-Jan-00 23:12:40 GMT " << cookie;
|
||||
cookie.setExpirationDate(utc(2020, 1, 1, 23, 12, 40));
|
||||
QTest::newRow("expiration-2digit4") << "a=b; expires=Wednesday, 01-Jan-20 23:12:40 GMT " << cookie;
|
||||
cookie.setExpirationDate(utc(2069, 1, 1, 23, 12, 40));
|
||||
QTest::newRow("expiration-2digit5") << "a=b; expires=Wednesday, 01-Jan-69 23:12:40 GMT " << cookie;
|
||||
|
||||
cookie.setExpirationDate(utc(1999, 11, 9, 23, 12, 40));
|
||||
|
||||
cookie.setPath("/");
|
||||
QTest::newRow("expires+path") << "a=b; expires=Wed, 09-Nov-1999 23:12:40 GMT; path=/" << cookie;
|
||||
QTest::newRow("path+expires") << "a=b; path=/;expires=Wed, 09-Nov-1999 23:12:40 GMT " << cookie;
|
||||
|
||||
cookie.setDomain(".qt-project.org");
|
||||
QTest::newRow("full") << "a=b; domain=.qt-project.org;expires=Wed, 09-Nov-1999 23:12:40 GMT;path=/" << cookie;
|
||||
QTest::newRow("full2") << "a=b;path=/; expires=Wed, 09-Nov-1999 23:12:40 GMT ;domain=.qt-project.org" << cookie;
|
||||
|
||||
// cookies obtained from the network:
|
||||
cookie = QNetworkCookie("__siteid", "1");
|
||||
cookie.setPath("/");
|
||||
cookie.setExpirationDate(utc(9999, 12, 31, 23, 59, 59));
|
||||
QTest::newRow("network2") << "__siteid=1; expires=Fri, 31-Dec-9999 23:59:59 GMT; path=/" << cookie;
|
||||
|
||||
cookie = QNetworkCookie("YM.LC", "v=2&m=9993_262838_159_1558_1063_0_5649_4012_3776161073,9426_260205_549_1295_1336_0_5141_4738_3922731647,6733_258196_952_1364_643_0_3560_-1_0,3677_237633_1294_1294_19267_0_3244_29483_4102206176,1315_235149_1693_1541_941_0_3224_1691_1861378060,1858_214311_2100_1298_19538_0_2873_30900_716411652,6258_212007_2506_1285_1017_0_2868_3606_4288540264,3743_207884_2895_1362_2759_0_2545_7114_3388520216,2654_205253_3257_1297_1332_0_2504_4682_3048534803,1891_184881_3660_1291_19079_0_978_29178_2592538685&f=1&n=20&s=date&o=down&e=1196548712&b=Inbox&u=removed");
|
||||
cookie.setPath("/");
|
||||
cookie.setDomain("mail.yahoo.com");
|
||||
QTest::newRow("network3") << "YM.LC=v=2&m=9993_262838_159_1558_1063_0_5649_4012_3776161073,9426_260205_549_1295_1336_0_5141_4738_3922731647,6733_258196_952_1364_643_0_3560_-1_0,3677_237633_1294_1294_19267_0_3244_29483_4102206176,1315_235149_1693_1541_941_0_3224_1691_1861378060,1858_214311_2100_1298_19538_0_2873_30900_716411652,6258_212007_2506_1285_1017_0_2868_3606_4288540264,3743_207884_2895_1362_2759_0_2545_7114_3388520216,2654_205253_3257_1297_1332_0_2504_4682_3048534803,1891_184881_3660_1291_19079_0_978_29178_2592538685&f=1&n=20&s=date&o=down&e=1196548712&b=Inbox&u=removed; path=/; domain=mail.yahoo.com" << cookie;
|
||||
|
||||
cookie = QNetworkCookie("__ac", "\"c2hhdXNtYW46U2FTYW80Wm8%3D\"");
|
||||
cookie.setPath("/");
|
||||
cookie.setExpirationDate(utc(2008, 8, 30, 20, 21, 49));
|
||||
QTest::newRow("network4") << "__ac=\"c2hhdXNtYW46U2FTYW80Wm8%3D\"; Path=/; Expires=Sat, 30 Aug 2008 20:21:49 +0000" << cookie;
|
||||
|
||||
// linkedin.com sends cookies in quotes and expects the cookie in quotes
|
||||
cookie = QNetworkCookie("leo_auth_token", "\"GST:UroVXaxYA3sVSkoVjMNH9bj4dZxVzK2yekgrAUxMfUsyLTNyPjoP60:1298974875:b675566ae32ab36d7a708c0efbf446a5c22b9fca\"");
|
||||
cookie.setPath("/");
|
||||
cookie.setExpirationDate(utc(2011, 3, 1, 10, 51, 14));
|
||||
QTest::newRow("network5") << "leo_auth_token=\"GST:UroVXaxYA3sVSkoVjMNH9bj4dZxVzK2yekgrAUxMfUsyLTNyPjoP60:1298974875:b675566ae32ab36d7a708c0efbf446a5c22b9fca\"; Version=1; Max-Age=1799; Expires=Tue, 01-Mar-2011 10:51:14 GMT; Path=/" << cookie;
|
||||
|
||||
// cookie containing JSON data (illegal for server, client should accept) - QTBUG-26002
|
||||
cookie = QNetworkCookie("xploreCookies", "{\"desktopReportingUrl\":\"null\",\"userIds\":\"1938850\",\"contactEmail\":\"NA\",\"contactName\":\"NA\",\"enterpriseLicenseId\":\"0\",\"openUrlTxt\":\"NA\",\"customerSurvey\":\"NA\",\"standardsLicenseId\":\"0\",\"openUrl\":\"NA\",\"smallBusinessLicenseId\":\"0\", \"instImage\":\"1938850_univ skovde.gif\",\"isMember\":\"false\",\"products\":\"IEL|VDE|\",\"openUrlImgLoc\":\"NA\",\"isIp\":\"true\",\"instName\": \"University of XXXXXX\",\"oldSessionKey\":\"LmZ8hlXo5a9uZx2Fnyw1564T1ZOWMnf3Dk*oDx2FQHwbg6RYefyrhC8PL2wx3Dx3D-18x2d8723DyqXRnkILyGpmx2Fh9wgx3Dx3Dc2lAOhHqGSKT78xxGwXZxxCgx3Dx3D-XrL4FnIlW2OPkqtVJq0LkQx3Dx3D-tujOLwhFqtX7Pa7HGqmCXQx3Dx3D\", \"isChargebackUser\":\"false\",\"isInst\":\"true\"}");
|
||||
cookie.setDomain(".ieee.org");
|
||||
cookie.setPath("/");
|
||||
QTest::newRow("network6") << "xploreCookies={\"desktopReportingUrl\":\"null\",\"userIds\":\"1938850\",\"contactEmail\":\"NA\",\"contactName\":\"NA\",\"enterpriseLicenseId\":\"0\",\"openUrlTxt\":\"NA\",\"customerSurvey\":\"NA\",\"standardsLicenseId\":\"0\",\"openUrl\":\"NA\",\"smallBusinessLicenseId\":\"0\", \"instImage\":\"1938850_univ skovde.gif\",\"isMember\":\"false\",\"products\":\"IEL|VDE|\",\"openUrlImgLoc\":\"NA\",\"isIp\":\"true\",\"instName\": \"University of XXXXXX\",\"oldSessionKey\":\"LmZ8hlXo5a9uZx2Fnyw1564T1ZOWMnf3Dk*oDx2FQHwbg6RYefyrhC8PL2wx3Dx3D-18x2d8723DyqXRnkILyGpmx2Fh9wgx3Dx3Dc2lAOhHqGSKT78xxGwXZxxCgx3Dx3D-XrL4FnIlW2OPkqtVJq0LkQx3Dx3D-tujOLwhFqtX7Pa7HGqmCXQx3Dx3D\", \"isChargebackUser\":\"false\",\"isInst\":\"true\"}; domain=.ieee.org; path=/" << cookie;
|
||||
}
|
||||
|
||||
void tst_QNetworkCookie::parseSingleCookie()
|
||||
{
|
||||
QFETCH(QString, cookieString);
|
||||
QFETCH(QNetworkCookie, expectedCookie);
|
||||
|
||||
QList<QNetworkCookie> result = QNetworkCookie::parseCookies(cookieString.toUtf8());
|
||||
|
||||
//QEXPECT_FAIL("network2", "QDateTime parsing problem: the date is beyond year 8000", Abort);
|
||||
QCOMPARE(result.size(), 1);
|
||||
QCOMPARE(result.at(0), expectedCookie);
|
||||
|
||||
result = QNetworkCookie::parseCookies(result.at(0).toRawForm());
|
||||
QCOMPARE(result.size(), 1);
|
||||
|
||||
// Drop any millisecond information, if there's any
|
||||
QDateTime dt = expectedCookie.expirationDate();
|
||||
if (dt.isValid()) {
|
||||
QTime t = dt.time();
|
||||
dt.setTime(t.addMSecs(-t.msec()));
|
||||
expectedCookie.setExpirationDate(dt);
|
||||
}
|
||||
|
||||
QCOMPARE(result.at(0), expectedCookie);
|
||||
}
|
||||
|
||||
void tst_QNetworkCookie::parseMultipleCookies_data()
|
||||
{
|
||||
QTest::addColumn<QString>("cookieString");
|
||||
QTest::addColumn<QList<QNetworkCookie> >("expectedCookies");
|
||||
|
||||
QList<QNetworkCookie> list;
|
||||
QTest::newRow("empty") << "" << list;
|
||||
|
||||
// these are technically empty cookies:
|
||||
QTest::newRow("invalid-01") << ";" << list;
|
||||
QTest::newRow("invalid-02") << " " << list;
|
||||
QTest::newRow("invalid-03") << " ," << list;
|
||||
QTest::newRow("invalid-04") << ";;,, ; ; , , ; , ;" << list;
|
||||
|
||||
// these are really invalid:
|
||||
// reason: malformed NAME=VALUE pair
|
||||
QTest::newRow("invalid-05") << "foo" << list;
|
||||
QTest::newRow("invalid-06") << "=b" << list;
|
||||
QTest::newRow("invalid-07") << ";path=/" << list;
|
||||
|
||||
// these should be accepted by RFC6265 but ignoring the expires field
|
||||
// reason: malformed expiration date string
|
||||
QNetworkCookie datelessCookie;
|
||||
datelessCookie.setName("a");
|
||||
datelessCookie.setValue("b");
|
||||
list << datelessCookie;
|
||||
QTest::newRow("expiration-empty") << "a=b;expires=" << list;
|
||||
QTest::newRow("expiration-invalid-01") << "a=b;expires=foobar" << list;
|
||||
QTest::newRow("expiration-invalid-02") << "a=b;expires=foobar, abc" << list;
|
||||
QTest::newRow("expiration-invalid-03") << "a=b; expires=123" << list; // used to ASSERT
|
||||
datelessCookie.setPath("/");
|
||||
list.clear();
|
||||
list << datelessCookie;
|
||||
QTest::newRow("expiration-invalid-04") << "a=b;expires=foobar, dd-mmm-yyyy hh:mm:ss GMT; path=/" << list;
|
||||
QTest::newRow("expiration-invalid-05") << "a=b;expires=foobar, 32-Caz-1999 24:01:60 GMT; path=/" << list;
|
||||
|
||||
// cookies obtained from the network:
|
||||
QNetworkCookie cookie;
|
||||
cookie = QNetworkCookie("id", "51706646077999719");
|
||||
cookie.setDomain(".bluestreak.com");
|
||||
cookie.setPath("/");
|
||||
cookie.setExpirationDate(QDateTime(QDate(2017, 12, 05), QTime(9, 11, 7), QTimeZone::UTC));
|
||||
list << cookie;
|
||||
cookie.setName("bb");
|
||||
cookie.setValue("\\\"K14144t\\\"_AAQ\\\"ototrK_A_ttot44AQ4KwoRQtoto|");
|
||||
list << cookie;
|
||||
cookie.setName("adv");
|
||||
cookie.setValue(QByteArray());
|
||||
list << cookie;
|
||||
QTest::newRow("network1") << "id=51706646077999719 bb=\"K14144t\"_AAQ\"ototrK_A_ttot44AQ4KwoRQtoto| adv=; Domain=.bluestreak.com; expires=Tuesday 05-Dec-2017 09:11:07 GMT; path=/;" << list;
|
||||
|
||||
QNetworkCookie cookieA;
|
||||
cookieA.setName("a");
|
||||
cookieA.setValue("b");
|
||||
|
||||
QNetworkCookie cookieB;
|
||||
cookieB.setName("c");
|
||||
cookieB.setValue("d");
|
||||
|
||||
// NewLine
|
||||
cookieA.setExpirationDate(QDateTime(QDate(2009, 3, 10), QTime(7, 0), QTimeZone::UTC));
|
||||
cookieB.setExpirationDate(QDateTime(QDate(2009, 3, 20), QTime(7, 0), QTimeZone::UTC));
|
||||
list = QList<QNetworkCookie>() << cookieA << cookieB;
|
||||
QTest::newRow("real-0") << "a=b; expires=Tue Mar 10 07:00:00 2009 GMT\nc=d; expires=Fri Mar 20 07:00:00 2009 GMT" << list;
|
||||
QTest::newRow("real-1") << "a=b; expires=Tue Mar 10 07:00:00 2009 GMT\n\nc=d; expires=Fri Mar 20 07:00:00 2009 GMT" << list;
|
||||
QTest::newRow("real-2") << "a=b; expires=Mar 10 07:00:00 2009 GMT, Tue\nc=d; expires=Fri Mar 20 07:00:00 2009 GMT" << list;
|
||||
|
||||
// Match firefox's behavior
|
||||
cookieA.setPath("/foo");
|
||||
list = QList<QNetworkCookie>() << cookieA << cookieB;
|
||||
QTest::newRow("real-3") << "a=b; expires=Mar 10 07:00:00 2009 GMT, Tue; path=/foo\nc=d; expires=Fri Mar 20 07:00:00 2009 GMT" << list;
|
||||
|
||||
// do not accept cookies with non-alphanumeric characters in domain field (QTBUG-11029)
|
||||
cookie = QNetworkCookie("NonAlphNumDomName", "NonAlphNumDomValue");
|
||||
cookie.setDomain("!@#$%^&*();:."); // the ';' is actually problematic, because it is a separator
|
||||
list = QList<QNetworkCookie>();
|
||||
QTest::newRow("domain-non-alpha-numeric") << "NonAlphNumDomName=NonAlphNumDomValue; domain=!@#$%^&*()" << list;
|
||||
}
|
||||
|
||||
void tst_QNetworkCookie::parseMultipleCookies()
|
||||
{
|
||||
QFETCH(QString, cookieString);
|
||||
QFETCH(QList<QNetworkCookie>, expectedCookies);
|
||||
|
||||
QList<QNetworkCookie> result = QNetworkCookie::parseCookies(cookieString.toUtf8());
|
||||
|
||||
QEXPECT_FAIL("network1", "Apparently multiple cookies set in one request (and an invalid date)", Abort);
|
||||
QCOMPARE(result, expectedCookies);
|
||||
}
|
||||
|
||||
void tst_QNetworkCookie::sameSite()
|
||||
{
|
||||
QList<QNetworkCookie> result = QNetworkCookie::parseCookies(QByteArrayLiteral("a=b;domain=qt-project.org"));
|
||||
QCOMPARE(result.first().sameSitePolicy(), QNetworkCookie::SameSite::Default);
|
||||
result = QNetworkCookie::parseCookies(QByteArrayLiteral("a=b;domain=qt-project.org;samesite=strict"));
|
||||
QCOMPARE(result.first().sameSitePolicy(), QNetworkCookie::SameSite::Strict);
|
||||
result = QNetworkCookie::parseCookies(QByteArrayLiteral("a=b;domain=qt-project.org;samesite=none;secure"));
|
||||
QCOMPARE(result.first().sameSitePolicy(), QNetworkCookie::SameSite::None);
|
||||
QCOMPARE(result.first().toRawForm(), QByteArrayLiteral("a=b; secure; SameSite=None; domain=qt-project.org"));
|
||||
|
||||
}
|
||||
QTEST_MAIN(tst_QNetworkCookie)
|
||||
#include "tst_qnetworkcookie.moc"
|
19
tests/auto/network/access/qnetworkcookiejar/CMakeLists.txt
Normal file
19
tests/auto/network/access/qnetworkcookiejar/CMakeLists.txt
Normal file
@ -0,0 +1,19 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qnetworkcookiejar Test:
|
||||
#####################################################################
|
||||
|
||||
# Collect test data
|
||||
list(APPEND test_data "parser.json" "testdata/publicsuffix/public_suffix_list.dafsa")
|
||||
|
||||
qt_internal_add_test(tst_qnetworkcookiejar
|
||||
SOURCES
|
||||
tst_qnetworkcookiejar.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::Network
|
||||
Qt::NetworkPrivate
|
||||
TESTDATA ${test_data}
|
||||
)
|
1963
tests/auto/network/access/qnetworkcookiejar/parser.json
Normal file
1963
tests/auto/network/access/qnetworkcookiejar/parser.json
Normal file
File diff suppressed because it is too large
Load Diff
BIN
tests/auto/network/access/qnetworkcookiejar/testdata/publicsuffix/public_suffix_list.dafsa
vendored
Normal file
BIN
tests/auto/network/access/qnetworkcookiejar/testdata/publicsuffix/public_suffix_list.dafsa
vendored
Normal file
Binary file not shown.
@ -0,0 +1,562 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
|
||||
#include <QTest>
|
||||
#include <QtCore/QJsonArray>
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QJsonObject>
|
||||
#include <QtCore/QJsonValue>
|
||||
#include <QtNetwork/QNetworkCookieJar>
|
||||
#include <QtNetwork/QNetworkCookie>
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
#if QT_CONFIG(topleveldomain)
|
||||
#include "private/qtldurl_p.h"
|
||||
#endif
|
||||
|
||||
class tst_QNetworkCookieJar: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
|
||||
void getterSetter();
|
||||
void setCookiesFromUrl_data();
|
||||
void setCookiesFromUrl();
|
||||
void cookiesForUrl_data();
|
||||
void cookiesForUrl();
|
||||
#if defined(QT_BUILD_INTERNAL) && QT_CONFIG(topleveldomain)
|
||||
void effectiveTLDs_data();
|
||||
void effectiveTLDs();
|
||||
#endif
|
||||
void rfc6265_data();
|
||||
void rfc6265();
|
||||
private:
|
||||
QSharedPointer<QTemporaryDir> m_dataDir;
|
||||
};
|
||||
|
||||
class MyCookieJar: public QNetworkCookieJar
|
||||
{
|
||||
public:
|
||||
~MyCookieJar() override;
|
||||
using QNetworkCookieJar::allCookies;
|
||||
using QNetworkCookieJar::setAllCookies;
|
||||
};
|
||||
|
||||
MyCookieJar::~MyCookieJar() = default;
|
||||
|
||||
void tst_QNetworkCookieJar::getterSetter()
|
||||
{
|
||||
MyCookieJar jar;
|
||||
|
||||
QVERIFY(jar.allCookies().isEmpty());
|
||||
|
||||
QList<QNetworkCookie> list;
|
||||
QNetworkCookie cookie;
|
||||
cookie.setName("a");
|
||||
list << cookie;
|
||||
|
||||
jar.setAllCookies(list);
|
||||
QCOMPARE(jar.allCookies(), list);
|
||||
}
|
||||
|
||||
void tst_QNetworkCookieJar::initTestCase()
|
||||
{
|
||||
#if QT_CONFIG(topleveldomain) && QT_CONFIG(publicsuffix_system)
|
||||
QString testDataDir;
|
||||
#ifdef BUILTIN_TESTDATA
|
||||
m_dataDir = QEXTRACTTESTDATA("/testdata");
|
||||
QVERIFY(m_dataDir);
|
||||
testDataDir = m_dataDir->path() + "/testdata";
|
||||
#else
|
||||
testDataDir = QFINDTESTDATA("testdata");
|
||||
#endif
|
||||
qDebug() << "Test data dir:" << testDataDir;
|
||||
qputenv("XDG_DATA_DIRS", QFile::encodeName(testDataDir));
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QNetworkCookieJar::setCookiesFromUrl_data()
|
||||
{
|
||||
QTest::addColumn<QList<QNetworkCookie> >("preset");
|
||||
QTest::addColumn<QNetworkCookie>("newCookie");
|
||||
QTest::addColumn<QString>("referenceUrl");
|
||||
QTest::addColumn<QList<QNetworkCookie> >("expectedResult");
|
||||
QTest::addColumn<bool>("setCookies");
|
||||
|
||||
QList<QNetworkCookie> preset;
|
||||
QList<QNetworkCookie> result;
|
||||
QNetworkCookie cookie;
|
||||
|
||||
cookie.setName("a");
|
||||
cookie.setPath("/");
|
||||
cookie.setDomain(".foo.tld");
|
||||
result += cookie;
|
||||
QTest::newRow("just-add") << preset << cookie << "http://www.foo.tld" << result << true;
|
||||
|
||||
preset = result;
|
||||
QTest::newRow("replace-1") << preset << cookie << "http://www.foo.tld" << result << true;
|
||||
|
||||
cookie.setValue("bc");
|
||||
result.clear();
|
||||
result += cookie;
|
||||
QTest::newRow("replace-2") << preset << cookie << "http://www.foo.tld" << result << true;
|
||||
|
||||
preset = result;
|
||||
cookie.setName("d");
|
||||
result += cookie;
|
||||
QTest::newRow("append") << preset << cookie << "http://www.foo.tld" << result << true;
|
||||
|
||||
cookie = preset.at(0);
|
||||
result = preset;
|
||||
cookie.setPath("/something");
|
||||
result += cookie;
|
||||
QTest::newRow("diff-path") << preset << cookie << "http://www.foo.tld/something" << result << true;
|
||||
|
||||
preset.clear();
|
||||
preset += cookie;
|
||||
cookie.setPath("/");
|
||||
QTest::newRow("diff-path-order") << preset << cookie << "http://www.foo.tld" << result << true;
|
||||
|
||||
preset.clear();
|
||||
result.clear();
|
||||
QNetworkCookie finalCookie = cookie;
|
||||
cookie.setDomain("foo.tld");
|
||||
finalCookie.setDomain(".foo.tld");
|
||||
result += finalCookie;
|
||||
QTest::newRow("should-add-dot-prefix") << preset << cookie << "http://www.foo.tld" << result << true;
|
||||
|
||||
result.clear();
|
||||
cookie.setDomain("");
|
||||
finalCookie.setDomain("www.foo.tld");
|
||||
result += finalCookie;
|
||||
QTest::newRow("should-set-default-domain") << preset << cookie << "http://www.foo.tld" << result << true;
|
||||
|
||||
// security test:
|
||||
result.clear();
|
||||
preset.clear();
|
||||
cookie.setDomain("something.completely.different");
|
||||
QTest::newRow("security-domain-1") << preset << cookie << "http://www.foo.tld" << result << false;
|
||||
|
||||
// we want the cookie to be accepted although the path does not match, see QTBUG-5815
|
||||
cookie.setDomain(".foo.tld");
|
||||
cookie.setPath("/something");
|
||||
result += cookie;
|
||||
QTest::newRow("security-path-1") << preset << cookie << "http://www.foo.tld" << result << true;
|
||||
|
||||
// check effective TLDs
|
||||
// 1. co.uk is an effective TLD, should be denied
|
||||
result.clear();
|
||||
preset.clear();
|
||||
cookie.setPath("/");
|
||||
cookie.setDomain(".co.uk");
|
||||
QTest::newRow("effective-tld1-denied") << preset << cookie << "http://something.co.uk" << result << false;
|
||||
cookie.setDomain("co.uk");
|
||||
QTest::newRow("effective-tld1-denied2") << preset << cookie << "http://something.co.uk" << result << false;
|
||||
cookie.setDomain(".something.co.uk");
|
||||
result += cookie;
|
||||
QTest::newRow("effective-tld1-accepted") << preset << cookie << "http://something.co.uk" << result << true;
|
||||
|
||||
// 2. anything .ck is an effective TLD ('*.ck'), but 'www.ck' is an exception
|
||||
result.clear();
|
||||
preset.clear();
|
||||
cookie.setDomain(".ck");
|
||||
QTest::newRow("effective-tld.ck-denied") << preset << cookie << "http://foo.ck" << result << false;
|
||||
result.clear();
|
||||
preset.clear();
|
||||
cookie.setDomain(".foo.ck");
|
||||
result += cookie;
|
||||
QTest::newRow("effective-tld2-accepted2") << preset << cookie << "http://foo.ck" << result << true;
|
||||
result.clear();
|
||||
QTest::newRow("effective-tld2-denied2") << preset << cookie << "http://www.foo.ck" << result << false;
|
||||
QTest::newRow("effective-tld2-denied3") << preset << cookie << "http://www.anything.foo.ck" << result << false;
|
||||
cookie.setDomain(".www.ck");
|
||||
result += cookie;
|
||||
QTest::newRow("effective-tld2-accepted") << preset << cookie << "http://www.www.ck" << result << true;
|
||||
|
||||
result.clear();
|
||||
preset.clear();
|
||||
cookie.setDomain("127.0.0.1");
|
||||
result += cookie;
|
||||
QTest::newRow("IPv4-address-as-domain") << preset << cookie << "http://127.0.0.1/" << result << true;
|
||||
|
||||
result.clear();
|
||||
preset.clear();
|
||||
cookie.setDomain("fe80::250:56ff:fec0:1");
|
||||
result += cookie;
|
||||
QTest::newRow("IPv6-address-as-domain") << preset << cookie << "http://[fe80::250:56ff:fec0:1]/" << result << true;
|
||||
|
||||
// setting the defaults:
|
||||
finalCookie = cookie;
|
||||
finalCookie.setPath("/something/");
|
||||
finalCookie.setDomain("www.foo.tld");
|
||||
cookie.setPath("");
|
||||
cookie.setDomain("");
|
||||
result.clear();
|
||||
result += finalCookie;
|
||||
QTest::newRow("defaults-1") << preset << cookie << "http://www.foo.tld/something/" << result << true;
|
||||
|
||||
finalCookie.setPath("/");
|
||||
result.clear();
|
||||
result += finalCookie;
|
||||
QTest::newRow("defaults-2") << preset << cookie << "http://www.foo.tld" << result << true;
|
||||
|
||||
// security test: do not accept cookie domains like ".com" nor ".com." (see RFC 2109 section 4.3.2)
|
||||
result.clear();
|
||||
preset.clear();
|
||||
cookie.setDomain(".com");
|
||||
QTest::newRow("rfc2109-4.3.2-ex3") << preset << cookie << "http://x.foo.com" << result << false;
|
||||
|
||||
result.clear();
|
||||
preset.clear();
|
||||
cookie.setDomain(".com.");
|
||||
QTest::newRow("rfc2109-4.3.2-ex3-2") << preset << cookie << "http://x.foo.com" << result << false;
|
||||
|
||||
// When using a TLD as a hostname the hostname should still get cookies (QTBUG-52040)
|
||||
// ... and nothing else should get the cookies.
|
||||
result.clear();
|
||||
preset.clear();
|
||||
cookie.setPath("/");
|
||||
cookie.setDomain(".support");
|
||||
result += cookie;
|
||||
QTest::newRow("TLD-as-domain-accepted") << preset << cookie << "http://support" << result << true;
|
||||
result.clear();
|
||||
QTest::newRow("TLD-as-domain-rejected") << preset << cookie << "http://a.support" << result << false;
|
||||
// Now test with no domain in the cookie, use the domain from the url (matching TLD)
|
||||
cookie.setDomain("support");
|
||||
result += cookie;
|
||||
cookie.setDomain("");
|
||||
QTest::newRow("TLD-as-domain-accepted2") << preset << cookie << "http://support" << result << true;
|
||||
}
|
||||
|
||||
void tst_QNetworkCookieJar::setCookiesFromUrl()
|
||||
{
|
||||
QFETCH(QList<QNetworkCookie>, preset);
|
||||
QFETCH(QNetworkCookie, newCookie);
|
||||
QFETCH(QString, referenceUrl);
|
||||
QFETCH(QList<QNetworkCookie>, expectedResult);
|
||||
QFETCH(bool, setCookies);
|
||||
|
||||
QList<QNetworkCookie> cookieList;
|
||||
cookieList += newCookie;
|
||||
MyCookieJar jar;
|
||||
jar.setAllCookies(preset);
|
||||
QCOMPARE(jar.setCookiesFromUrl(cookieList, referenceUrl), setCookies);
|
||||
|
||||
QList<QNetworkCookie> result = jar.allCookies();
|
||||
foreach (QNetworkCookie cookie, expectedResult) {
|
||||
QVERIFY2(result.contains(cookie), cookie.toRawForm());
|
||||
result.removeAll(cookie);
|
||||
}
|
||||
QVERIFY2(result.isEmpty(), QTest::toString(result));
|
||||
}
|
||||
|
||||
void tst_QNetworkCookieJar::cookiesForUrl_data()
|
||||
{
|
||||
QTest::addColumn<QList<QNetworkCookie> >("allCookies");
|
||||
QTest::addColumn<QString>("url");
|
||||
QTest::addColumn<QList<QNetworkCookie> >("expectedResult");
|
||||
|
||||
QList<QNetworkCookie> allCookies;
|
||||
QList<QNetworkCookie> result;
|
||||
|
||||
QTest::newRow("no-cookies") << allCookies << "http://foo.bar/" << result;
|
||||
|
||||
QNetworkCookie cookie;
|
||||
cookie.setName("a");
|
||||
cookie.setPath("/web");
|
||||
cookie.setDomain(".qt-project.org");
|
||||
allCookies += cookie;
|
||||
|
||||
QTest::newRow("no-match-1") << allCookies << "http://foo.bar/" << result;
|
||||
QTest::newRow("no-match-2") << allCookies << "http://foo.bar/web" << result;
|
||||
QTest::newRow("no-match-3") << allCookies << "http://foo.bar/web/wiki" << result;
|
||||
QTest::newRow("no-match-4") << allCookies << "http://qt-project.org" << result;
|
||||
QTest::newRow("no-match-5") << allCookies << "http://qt-project.org" << result;
|
||||
QTest::newRow("no-match-6") << allCookies << "http://qt-project.org/webinar" << result;
|
||||
QTest::newRow("no-match-7") << allCookies << "http://qt-project.org/webinar" << result;
|
||||
QTest::newRow("no-match-8") << allCookies << "http://qt-project.org./web" << result;
|
||||
QTest::newRow("no-match-9") << allCookies << "http://qt-project.org./web" << result;
|
||||
|
||||
result = allCookies;
|
||||
QTest::newRow("match-1") << allCookies << "http://qt-project.org/web" << result;
|
||||
QTest::newRow("match-2") << allCookies << "http://qt-project.org/web/" << result;
|
||||
QTest::newRow("match-3") << allCookies << "http://qt-project.org/web/content" << result;
|
||||
QTest::newRow("match-4") << allCookies << "http://qt-project.org/web" << result;
|
||||
QTest::newRow("match-5") << allCookies << "http://qt-project.org/web/" << result;
|
||||
QTest::newRow("match-6") << allCookies << "http://qt-project.org/web/content" << result;
|
||||
|
||||
cookie.setPath("/web/wiki");
|
||||
allCookies += cookie;
|
||||
|
||||
// exact same results as before:
|
||||
QTest::newRow("one-match-1") << allCookies << "http://qt-project.org/web" << result;
|
||||
QTest::newRow("one-match-2") << allCookies << "http://qt-project.org/web/" << result;
|
||||
QTest::newRow("one-match-3") << allCookies << "http://qt-project.org/web/content" << result;
|
||||
QTest::newRow("one-match-4") << allCookies << "http://qt-project.org/web" << result;
|
||||
QTest::newRow("one-match-5") << allCookies << "http://qt-project.org/web/" << result;
|
||||
QTest::newRow("one-match-6") << allCookies << "http://qt-project.org/web/content" << result;
|
||||
|
||||
result.prepend(cookie); // longer path, it must match first
|
||||
QTest::newRow("two-matches-1") << allCookies << "http://qt-project.org/web/wiki" << result;
|
||||
QTest::newRow("two-matches-2") << allCookies << "http://qt-project.org/web/wiki" << result;
|
||||
|
||||
// invert the order;
|
||||
allCookies.clear();
|
||||
allCookies << result.at(1) << result.at(0);
|
||||
QTest::newRow("two-matches-3") << allCookies << "http://qt-project.org/web/wiki" << result;
|
||||
QTest::newRow("two-matches-4") << allCookies << "http://qt-project.org/web/wiki" << result;
|
||||
|
||||
// expired cookie
|
||||
allCookies.clear();
|
||||
cookie.setExpirationDate(QDateTime::fromString("09-Nov-1999", "dd-MMM-yyyy"));
|
||||
allCookies += cookie;
|
||||
result.clear();
|
||||
QTest::newRow("exp-match-1") << allCookies << "http://qt-project.org/web" << result;
|
||||
QTest::newRow("exp-match-2") << allCookies << "http://qt-project.org/web/" << result;
|
||||
QTest::newRow("exp-match-3") << allCookies << "http://qt-project.org/web/content" << result;
|
||||
QTest::newRow("exp-match-4") << allCookies << "http://qt-project.org/web" << result;
|
||||
QTest::newRow("exp-match-5") << allCookies << "http://qt-project.org/web/" << result;
|
||||
QTest::newRow("exp-match-6") << allCookies << "http://qt-project.org/web/content" << result;
|
||||
|
||||
// path matching
|
||||
allCookies.clear();
|
||||
QNetworkCookie anotherCookie;
|
||||
anotherCookie.setName("a");
|
||||
anotherCookie.setPath("/web");
|
||||
anotherCookie.setDomain(".qt-project.org");
|
||||
allCookies += anotherCookie;
|
||||
result.clear();
|
||||
QTest::newRow("path-unmatch-1") << allCookies << "http://qt-project.org/" << result;
|
||||
QTest::newRow("path-unmatch-2") << allCookies << "http://qt-project.org/something/else" << result;
|
||||
result += anotherCookie;
|
||||
QTest::newRow("path-match-1") << allCookies << "http://qt-project.org/web" << result;
|
||||
QTest::newRow("path-match-2") << allCookies << "http://qt-project.org/web/" << result;
|
||||
QTest::newRow("path-match-3") << allCookies << "http://qt-project.org/web/content" << result;
|
||||
|
||||
// secure cookies
|
||||
allCookies.clear();
|
||||
result.clear();
|
||||
QNetworkCookie secureCookie;
|
||||
secureCookie.setName("a");
|
||||
secureCookie.setPath("/web");
|
||||
secureCookie.setDomain(".qt-project.org");
|
||||
secureCookie.setSecure(true);
|
||||
allCookies += secureCookie;
|
||||
QTest::newRow("no-match-secure-1") << allCookies << "http://qt-project.org/web" << result;
|
||||
QTest::newRow("no-match-secure-2") << allCookies << "http://qt-project.org/web" << result;
|
||||
result += secureCookie;
|
||||
QTest::newRow("match-secure-1") << allCookies << "https://qt-project.org/web" << result;
|
||||
QTest::newRow("match-secure-2") << allCookies << "https://qt-project.org/web" << result;
|
||||
|
||||
// domain ending in .
|
||||
allCookies.clear();
|
||||
result.clear();
|
||||
QNetworkCookie cookieDot;
|
||||
cookieDot.setDomain(".example.com.");
|
||||
cookieDot.setName("a");
|
||||
allCookies += cookieDot;
|
||||
QTest::newRow("no-match-domain-dot") << allCookies << "http://example.com" << result;
|
||||
result += cookieDot;
|
||||
QTest::newRow("match-domain-dot") << allCookies << "http://example.com." << result;
|
||||
|
||||
// Root path in cookie, empty url path
|
||||
allCookies.clear();
|
||||
QNetworkCookie rootCookie;
|
||||
rootCookie.setName("a");
|
||||
rootCookie.setPath("/");
|
||||
rootCookie.setDomain("qt-project.org");
|
||||
allCookies += rootCookie;
|
||||
result.clear();
|
||||
result += rootCookie;
|
||||
QTest::newRow("root-path-match") << allCookies << "http://qt-project.org" << result;
|
||||
|
||||
// Domain in cookie happens to match a TLD
|
||||
allCookies.clear();
|
||||
QNetworkCookie tldCookie;
|
||||
tldCookie.setDomain(".support");
|
||||
tldCookie.setName("a");
|
||||
tldCookie.setValue("b");
|
||||
allCookies += tldCookie;
|
||||
result.clear();
|
||||
result += tldCookie;
|
||||
QTest::newRow("tld-cookie-match") << allCookies << "http://support/" << result;
|
||||
result.clear();
|
||||
QTest::newRow("tld-cookie-no-match") << allCookies << "http://a.support/" << result;
|
||||
}
|
||||
|
||||
void tst_QNetworkCookieJar::cookiesForUrl()
|
||||
{
|
||||
QFETCH(QList<QNetworkCookie>, allCookies);
|
||||
QFETCH(QString, url);
|
||||
QFETCH(QList<QNetworkCookie>, expectedResult);
|
||||
|
||||
MyCookieJar jar;
|
||||
jar.setAllCookies(allCookies);
|
||||
|
||||
QList<QNetworkCookie> result = jar.cookiesForUrl(url);
|
||||
QCOMPARE(result, expectedResult);
|
||||
}
|
||||
|
||||
// This test requires private API.
|
||||
#if defined(QT_BUILD_INTERNAL) && QT_CONFIG(topleveldomain)
|
||||
void tst_QNetworkCookieJar::effectiveTLDs_data()
|
||||
{
|
||||
QTest::addColumn<QString>("domain");
|
||||
QTest::addColumn<bool>("isTLD");
|
||||
|
||||
QTest::newRow("yes1") << "com" << true;
|
||||
QTest::newRow("yes2") << "de" << true;
|
||||
QTest::newRow("yes4") << "krodsherad.no" << true;
|
||||
QTest::newRow("yes5") << "1.bg" << true;
|
||||
QTest::newRow("yes6") << "com.cn" << true;
|
||||
QTest::newRow("yes7") << "org.ws" << true;
|
||||
QTest::newRow("yes8") << "co.uk" << true;
|
||||
QTest::newRow("yes10") << "hk.com" << true;
|
||||
QTest::newRow("yes11") << "hk.org" << true;
|
||||
|
||||
QTest::newRow("no1") << "anything.com" << false;
|
||||
QTest::newRow("no2") << "anything.de" << false;
|
||||
QTest::newRow("no3") << "eselsberg.ulm.museum" << false;
|
||||
QTest::newRow("no4") << "noe.krodsherad.no" << false;
|
||||
QTest::newRow("no5") << "2.1.bg" << false;
|
||||
QTest::newRow("no6") << "foo.com.cn" << false;
|
||||
QTest::newRow("no7") << "something.org.ws" << false;
|
||||
QTest::newRow("no8") << "teatime.co.uk" << false;
|
||||
QTest::newRow("no9") << "bla" << false;
|
||||
QTest::newRow("no10") << "bla.bla" << false;
|
||||
QTest::newRow("no11") << "mosreg.ru" << false;
|
||||
|
||||
const char16_t s1[] = {0x74, 0x72, 0x61, 0x6e, 0xf8, 0x79, 0x2e, 0x6e, 0x6f, 0x00}; // xn--trany-yua.no
|
||||
const char16_t s3[] = {0x7ec4, 0x7e54, 0x2e, 0x68, 0x6b, 0x00}; // xn--mk0axi.hk
|
||||
const char16_t s4[] = {0x7f51, 0x7edc, 0x2e, 0x63, 0x6e, 0x00}; // xn--io0a7i.cn
|
||||
const char16_t s5[] = {0x72, 0xe1, 0x68, 0x6b, 0x6b, 0x65, 0x72, 0xe1, 0x76, 0x6a, 0x75, 0x2e, 0x6e, 0x6f, 0x00}; // xn--rhkkervju-01af.no
|
||||
const char16_t s6[] = {0xb9a, 0xbbf, 0xb99, 0xbcd, 0xb95, 0xbaa, 0xbcd, 0xbaa, 0xbc2, 0xbb0, 0xbcd, 0x00}; // xn--clchc0ea0b2g2a9gcd
|
||||
const char16_t s7[] = {0x627, 0x644, 0x627, 0x631, 0x62f, 0x646, 0x00}; // xn--mgbayh7gpa
|
||||
QTest::newRow("yes-specialchars1") << QString::fromUtf16(s1) << true;
|
||||
QTest::newRow("yes-specialchars3") << QString::fromUtf16(s3) << true;
|
||||
QTest::newRow("yes-specialchars4") << QString::fromUtf16(s4) << true;
|
||||
QTest::newRow("yes-specialchars5") << QString::fromUtf16(s5) << true;
|
||||
QTest::newRow("yes-specialchars6") << QString::fromUtf16(s6) << true;
|
||||
QTest::newRow("yes-specialchars7") << QString::fromUtf16(s7) << true;
|
||||
|
||||
QTest::newRow("no-specialchars1") << QString::fromUtf16(s1).prepend("something") << false;
|
||||
QTest::newRow("no-specialchars3") << QString::fromUtf16(s3).prepend("foo") << false;
|
||||
QTest::newRow("no-specialchars4") << QString::fromUtf16(s4).prepend("bar") << false;
|
||||
QTest::newRow("no-specialchars6") << QString::fromUtf16(s6).prepend(QLatin1Char('.') + QString::fromUtf16(s6)) << false;
|
||||
QTest::newRow("no-specialchars7") << QString::fromUtf16(s7).prepend("bla") << false;
|
||||
|
||||
QTest::newRow("exception1") << "pref.iwate.jp" << false;
|
||||
QTest::newRow("exception2") << "omanpost.om" << false;
|
||||
QTest::newRow("exception3") << "omantel.om" << false;
|
||||
QTest::newRow("exception4") << "gobiernoelectronico.ar" << false;
|
||||
QTest::newRow("exception5") << "pref.ishikawa.jp" << false;
|
||||
|
||||
QTest::newRow("yes-wildcard1") << "*.jm" << true;
|
||||
QTest::newRow("yes-wildcard1.5") << "anything.jm" << true;
|
||||
QTest::newRow("yes-wildcard2") << "something.kh" << true;
|
||||
QTest::newRow("no-wildcard3") << "whatever.uk" << false; // was changed at some point
|
||||
QTest::newRow("yes-wildcard4") << "anything.sendai.jp" << true;
|
||||
QTest::newRow("yes-wildcard5") << "foo.sch.uk" << true;
|
||||
QTest::newRow("yes-platform.sh") << "eu.platform.sh" << true;
|
||||
QTest::newRow("no-platform.sh") << "something.platform.sh" << false;
|
||||
}
|
||||
|
||||
void tst_QNetworkCookieJar::effectiveTLDs()
|
||||
{
|
||||
QFETCH(QString, domain);
|
||||
QFETCH(bool, isTLD);
|
||||
QCOMPARE(qIsEffectiveTLD(domain), isTLD);
|
||||
}
|
||||
#endif
|
||||
|
||||
void tst_QNetworkCookieJar::rfc6265_data()
|
||||
{
|
||||
QTest::addColumn<QStringList>("received");
|
||||
QTest::addColumn<QList<QNetworkCookie> >("sent");
|
||||
QTest::addColumn<QString>("sentTo");
|
||||
|
||||
QFile file(QFINDTESTDATA("parser.json"));
|
||||
QVERIFY(file.open(QFile::ReadOnly | QFile::Text));
|
||||
QJsonDocument document;
|
||||
document = QJsonDocument::fromJson(file.readAll());
|
||||
QVERIFY(!document.isNull());
|
||||
QVERIFY(document.isArray());
|
||||
|
||||
for (const QJsonValue testCase : document.array()) {
|
||||
QJsonObject testObject = testCase.toObject();
|
||||
|
||||
//"test" - the test case name
|
||||
QString testCaseName = testObject.value("test").toString();
|
||||
if (testCaseName.toLower().startsWith("disabled"))
|
||||
continue;
|
||||
|
||||
//"received" - the cookies received from the server
|
||||
const QJsonArray received = testObject.value("received").toArray();
|
||||
QStringList receivedList;
|
||||
for (const QJsonValue receivedCookie : received)
|
||||
receivedList.append(receivedCookie.toString());
|
||||
|
||||
//"sent" - the cookies sent back to the server
|
||||
const QJsonArray sent = testObject.value("sent").toArray();
|
||||
QList<QNetworkCookie> sentList;
|
||||
for (const QJsonValue sentCookie : sent) {
|
||||
QJsonObject sentCookieObject = sentCookie.toObject();
|
||||
QNetworkCookie cookie;
|
||||
cookie.setName(sentCookieObject.value("name").toString().toUtf8());
|
||||
cookie.setValue(sentCookieObject.value("value").toString().toUtf8());
|
||||
sentList.append(cookie);
|
||||
}
|
||||
|
||||
//"sent-to" - the relative url where cookies are sent
|
||||
QTest::newRow(qPrintable(testCaseName)) << receivedList << sentList << testObject.value("sent-to").toString();
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QNetworkCookieJar::rfc6265()
|
||||
{
|
||||
QFETCH(QStringList, received);
|
||||
QFETCH(QList<QNetworkCookie>, sent);
|
||||
QFETCH(QString, sentTo);
|
||||
|
||||
QUrl receivedUrl("http://home.example.org:8888/cookie-parser");
|
||||
QUrl sentUrl("http://home.example.org:8888/cookie-parser-result");
|
||||
if (!sentTo.isEmpty())
|
||||
sentUrl = receivedUrl.resolved(sentTo);
|
||||
|
||||
QNetworkCookieJar jar;
|
||||
QList<QNetworkCookie> receivedCookies;
|
||||
foreach (const QString &cookieLine, received)
|
||||
receivedCookies.append(QNetworkCookie::parseCookies(cookieLine.toUtf8()));
|
||||
|
||||
jar.setCookiesFromUrl(receivedCookies, receivedUrl);
|
||||
QList<QNetworkCookie> cookiesToSend = jar.cookiesForUrl(sentUrl);
|
||||
|
||||
//compare cookies only using name/value, as the metadata isn't sent over the network
|
||||
QCOMPARE(cookiesToSend.size(), sent.size());
|
||||
bool ok = true;
|
||||
for (int i = 0; i < cookiesToSend.size(); i++) {
|
||||
if (cookiesToSend.at(i).name() != sent.at(i).name()) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
if (cookiesToSend.at(i).value() != sent.at(i).value()) {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!ok) {
|
||||
QNetworkRequest r(sentUrl);
|
||||
r.setHeader(QNetworkRequest::CookieHeader, QVariant::fromValue(cookiesToSend));
|
||||
QString actual = QString::fromUtf8(r.rawHeader("Cookie"));
|
||||
r.setHeader(QNetworkRequest::CookieHeader, QVariant::fromValue(sent));
|
||||
QString expected = QString::fromUtf8(r.rawHeader("Cookie"));
|
||||
|
||||
QVERIFY2(ok, qPrintable(QString("Expected: %1\nActual: %2").arg(expected).arg(actual)));
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QNetworkCookieJar)
|
||||
#include "tst_qnetworkcookiejar.moc"
|
||||
|
13
tests/auto/network/access/qnetworkdiskcache/CMakeLists.txt
Normal file
13
tests/auto/network/access/qnetworkdiskcache/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qnetworkdiskcache Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qnetworkdiskcache
|
||||
SOURCES
|
||||
tst_qnetworkdiskcache.cpp
|
||||
LIBRARIES
|
||||
Qt::Network
|
||||
)
|
@ -0,0 +1,818 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QtNetwork/QtNetwork>
|
||||
#include <QTest>
|
||||
#include <QTestEventLoop>
|
||||
#include <qnetworkdiskcache.h>
|
||||
#include <qrandom.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#define EXAMPLE_URL "http://user:pass@localhost:4/#foo"
|
||||
#define EXAMPLE_URL2 "http://user:pass@localhost:4/bar"
|
||||
//cached objects are organized into these many subdirs
|
||||
#define NUM_SUBDIRECTORIES 15
|
||||
|
||||
class tst_QNetworkDiskCache : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
tst_QNetworkDiskCache();
|
||||
|
||||
public slots:
|
||||
void accessAfterRemoveReadyReadSlot();
|
||||
void setCookieHeaderMetaDataChangedSlot();
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
void qnetworkdiskcache_data();
|
||||
void qnetworkdiskcache();
|
||||
|
||||
void prepare();
|
||||
void cacheSize();
|
||||
void clear();
|
||||
void data_data();
|
||||
void data();
|
||||
void metaData();
|
||||
void remove();
|
||||
void accessAfterRemove(); // QTBUG-17400
|
||||
void setCookieHeader(); // QTBUG-41514
|
||||
void setCacheDirectory_data();
|
||||
void setCacheDirectory();
|
||||
void updateMetaData();
|
||||
void fileMetaData();
|
||||
void expire();
|
||||
|
||||
void oldCacheVersionFile_data();
|
||||
void oldCacheVersionFile();
|
||||
|
||||
void streamVersion_data();
|
||||
void streamVersion();
|
||||
|
||||
void sync();
|
||||
|
||||
void crashWhenParentingCache();
|
||||
|
||||
private:
|
||||
QTemporaryDir tempDir;
|
||||
QUrl url; // used by accessAfterRemove(), setCookieHeader()
|
||||
QNetworkDiskCache *diskCache; // used by accessAfterRemove()
|
||||
QNetworkAccessManager *manager; // used by setCookieHeader()
|
||||
};
|
||||
|
||||
// FIXME same as in tst_qnetworkreply.cpp .. could be unified
|
||||
// Does not work for POST/PUT!
|
||||
class MiniHttpServer: public QTcpServer
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QTcpSocket *client; // always the last one that was received
|
||||
QByteArray dataToTransmit;
|
||||
QByteArray receivedData;
|
||||
bool doClose;
|
||||
bool multiple;
|
||||
int totalConnections;
|
||||
|
||||
MiniHttpServer(const QByteArray &data) : client(0), dataToTransmit(data), doClose(true), multiple(false), totalConnections(0)
|
||||
{
|
||||
listen();
|
||||
connect(this, SIGNAL(newConnection()), this, SLOT(doAccept()));
|
||||
}
|
||||
|
||||
public slots:
|
||||
void doAccept()
|
||||
{
|
||||
client = nextPendingConnection();
|
||||
client->setParent(this);
|
||||
++totalConnections;
|
||||
connect(client, SIGNAL(readyRead()), this, SLOT(readyReadSlot()));
|
||||
}
|
||||
|
||||
void readyReadSlot()
|
||||
{
|
||||
receivedData += client->readAll();
|
||||
int doubleEndlPos = receivedData.indexOf("\r\n\r\n");
|
||||
|
||||
if (doubleEndlPos != -1) {
|
||||
// multiple requests incoming. remove the bytes of the current one
|
||||
if (multiple)
|
||||
receivedData.remove(0, doubleEndlPos+4);
|
||||
|
||||
client->write(dataToTransmit);
|
||||
if (doClose) {
|
||||
client->disconnectFromHost();
|
||||
disconnect(client, 0, this, 0);
|
||||
client = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Subclass that exposes the protected functions.
|
||||
class SubQNetworkDiskCache : public QNetworkDiskCache
|
||||
{
|
||||
public:
|
||||
~SubQNetworkDiskCache()
|
||||
{
|
||||
if (!cacheDirectory().isEmpty() && clearOnDestruction)
|
||||
clear();
|
||||
}
|
||||
|
||||
QNetworkCacheMetaData call_fileMetaData(QString const &fileName)
|
||||
{ return SubQNetworkDiskCache::fileMetaData(fileName); }
|
||||
|
||||
qint64 call_expire()
|
||||
{ return SubQNetworkDiskCache::expire(); }
|
||||
|
||||
void setupWithOne(const QString &path, const QUrl &url, const QNetworkCacheMetaData &metaData = QNetworkCacheMetaData())
|
||||
{
|
||||
setCacheDirectory(path);
|
||||
|
||||
QIODevice *d = nullptr;
|
||||
if (metaData.isValid()) {
|
||||
d = prepare(metaData);
|
||||
} else {
|
||||
QNetworkCacheMetaData m;
|
||||
m.setUrl(url);
|
||||
QNetworkCacheMetaData::RawHeader header("content-type", "text/html");
|
||||
QNetworkCacheMetaData::RawHeaderList list;
|
||||
list.append(header);
|
||||
m.setRawHeaders(list);
|
||||
d = prepare(m);
|
||||
}
|
||||
d->write("Hello World!");
|
||||
insert(d);
|
||||
}
|
||||
|
||||
void setClearCacheOnDestruction(bool value) { clearOnDestruction = value; }
|
||||
|
||||
private:
|
||||
bool clearOnDestruction = true;
|
||||
};
|
||||
|
||||
tst_QNetworkDiskCache::tst_QNetworkDiskCache()
|
||||
: tempDir(QDir::tempPath() + "/tst_qnetworkdiskcache.XXXXXX")
|
||||
{
|
||||
}
|
||||
|
||||
// This will be called before the first test function is executed.
|
||||
// It is only called once.
|
||||
void tst_QNetworkDiskCache::initTestCase()
|
||||
{
|
||||
QVERIFY(tempDir.isValid());
|
||||
|
||||
SubQNetworkDiskCache cache;
|
||||
cache.setCacheDirectory(tempDir.path());
|
||||
}
|
||||
|
||||
// This will be called after the last test function is executed.
|
||||
// It is only called once.
|
||||
void tst_QNetworkDiskCache::cleanupTestCase()
|
||||
{
|
||||
QDir workingDir("foo");
|
||||
if (workingDir.exists())
|
||||
workingDir.removeRecursively();
|
||||
}
|
||||
|
||||
void tst_QNetworkDiskCache::qnetworkdiskcache_data()
|
||||
{
|
||||
}
|
||||
|
||||
void tst_QNetworkDiskCache::qnetworkdiskcache()
|
||||
{
|
||||
QUrl url(EXAMPLE_URL);
|
||||
SubQNetworkDiskCache cache;
|
||||
QCOMPARE(cache.cacheDirectory(), QString());
|
||||
QCOMPARE(cache.cacheSize(), qint64(0));
|
||||
cache.clear();
|
||||
QCOMPARE(cache.metaData(QUrl()), QNetworkCacheMetaData());
|
||||
QCOMPARE(cache.remove(QUrl()), false);
|
||||
QCOMPARE(cache.remove(url), false);
|
||||
cache.insert((QIODevice*)0);
|
||||
cache.setCacheDirectory(QString());
|
||||
cache.updateMetaData(QNetworkCacheMetaData());
|
||||
cache.prepare(QNetworkCacheMetaData());
|
||||
QCOMPARE(cache.call_fileMetaData(QString()), QNetworkCacheMetaData());
|
||||
|
||||
// leave one hanging around...
|
||||
QNetworkDiskCache badCache;
|
||||
QNetworkCacheMetaData metaData;
|
||||
metaData.setUrl(url);
|
||||
badCache.prepare(metaData);
|
||||
badCache.setCacheDirectory(tempDir.path());
|
||||
badCache.prepare(metaData);
|
||||
}
|
||||
|
||||
void tst_QNetworkDiskCache::prepare()
|
||||
{
|
||||
SubQNetworkDiskCache cache;
|
||||
cache.setCacheDirectory(tempDir.path());
|
||||
|
||||
QUrl url(EXAMPLE_URL);
|
||||
QNetworkCacheMetaData metaData;
|
||||
metaData.setUrl(url);
|
||||
|
||||
cache.prepare(metaData);
|
||||
cache.remove(url);
|
||||
}
|
||||
|
||||
// public qint64 cacheSize() const
|
||||
void tst_QNetworkDiskCache::cacheSize()
|
||||
{
|
||||
qint64 cacheSize = 0;
|
||||
{
|
||||
SubQNetworkDiskCache cache;
|
||||
cache.setCacheDirectory(tempDir.path());
|
||||
QCOMPARE(cache.cacheSize(), qint64(0));
|
||||
|
||||
{
|
||||
QUrl url(EXAMPLE_URL);
|
||||
QNetworkCacheMetaData metaData;
|
||||
metaData.setUrl(url);
|
||||
QIODevice *d = cache.prepare(metaData);
|
||||
cache.insert(d);
|
||||
cacheSize = cache.cacheSize();
|
||||
QVERIFY(cacheSize > qint64(0));
|
||||
}
|
||||
// Add a second item, some difference in behavior when the cache is not empty
|
||||
{
|
||||
QUrl url(EXAMPLE_URL2);
|
||||
QNetworkCacheMetaData metaData;
|
||||
metaData.setUrl(url);
|
||||
QIODevice *d = cache.prepare(metaData);
|
||||
cache.insert(d);
|
||||
QVERIFY(cache.cacheSize() > cacheSize);
|
||||
cacheSize = cache.cacheSize();
|
||||
}
|
||||
|
||||
// Don't clear the cache on destruction so we can re-open the cache and test its size.
|
||||
cache.setClearCacheOnDestruction(false);
|
||||
}
|
||||
|
||||
SubQNetworkDiskCache cache;
|
||||
cache.setCacheDirectory(tempDir.path());
|
||||
QCOMPARE(cache.cacheSize(), cacheSize);
|
||||
cache.clear();
|
||||
QCOMPARE(cache.cacheSize(), qint64(0));
|
||||
}
|
||||
|
||||
static QStringList countFiles(const QString dir)
|
||||
{
|
||||
QStringList list;
|
||||
QDir::Filters filter(QDir::AllEntries | QDir::NoDotAndDotDot);
|
||||
QDirIterator it(dir, filter, QDirIterator::Subdirectories);
|
||||
while (it.hasNext())
|
||||
list.append(it.next());
|
||||
return list;
|
||||
}
|
||||
|
||||
// public void clear()
|
||||
void tst_QNetworkDiskCache::clear()
|
||||
{
|
||||
SubQNetworkDiskCache cache;
|
||||
QUrl url(EXAMPLE_URL);
|
||||
cache.setupWithOne(tempDir.path(), url);
|
||||
QVERIFY(cache.cacheSize() > qint64(0));
|
||||
|
||||
QString cacheDirectory = cache.cacheDirectory();
|
||||
QCOMPARE(countFiles(cacheDirectory).size(), NUM_SUBDIRECTORIES + 3);
|
||||
cache.clear();
|
||||
QCOMPARE(countFiles(cacheDirectory).size(), NUM_SUBDIRECTORIES + 2);
|
||||
|
||||
// don't delete files that it didn't create
|
||||
QTemporaryFile file(cacheDirectory + "/XXXXXX");
|
||||
if (file.open()) {
|
||||
file.fileName(); // make sure it exists with a name
|
||||
QCOMPARE(countFiles(cacheDirectory).size(), NUM_SUBDIRECTORIES + 3);
|
||||
cache.clear();
|
||||
QCOMPARE(countFiles(cacheDirectory).size(), NUM_SUBDIRECTORIES + 3);
|
||||
}
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(QNetworkCacheMetaData)
|
||||
void tst_QNetworkDiskCache::data_data()
|
||||
{
|
||||
QTest::addColumn<QNetworkCacheMetaData>("data");
|
||||
|
||||
QTest::newRow("null") << QNetworkCacheMetaData();
|
||||
|
||||
QUrl url(EXAMPLE_URL);
|
||||
QNetworkCacheMetaData metaData;
|
||||
metaData.setUrl(url);
|
||||
QNetworkCacheMetaData::RawHeaderList headers;
|
||||
headers.append(QNetworkCacheMetaData::RawHeader("type", "bin"));
|
||||
metaData.setRawHeaders(headers);
|
||||
QTest::newRow("non-null") << metaData;
|
||||
}
|
||||
|
||||
// public QIODevice* data(QUrl const& url)
|
||||
void tst_QNetworkDiskCache::data()
|
||||
{
|
||||
QFETCH(QNetworkCacheMetaData, data);
|
||||
SubQNetworkDiskCache cache;
|
||||
QUrl url(EXAMPLE_URL);
|
||||
cache.setupWithOne(tempDir.path(), url, data);
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
QIODevice *d = cache.data(url);
|
||||
QVERIFY(d);
|
||||
QCOMPARE(d->readAll(), QByteArray("Hello World!"));
|
||||
delete d;
|
||||
}
|
||||
}
|
||||
|
||||
// public QNetworkCacheMetaData metaData(QUrl const& url)
|
||||
void tst_QNetworkDiskCache::metaData()
|
||||
{
|
||||
SubQNetworkDiskCache cache;
|
||||
|
||||
QUrl url(EXAMPLE_URL);
|
||||
QNetworkCacheMetaData metaData;
|
||||
metaData.setUrl(url);
|
||||
QNetworkCacheMetaData::RawHeaderList headers;
|
||||
headers.append(QNetworkCacheMetaData::RawHeader("type", "bin"));
|
||||
metaData.setRawHeaders(headers);
|
||||
metaData.setLastModified(QDateTime::currentDateTime());
|
||||
metaData.setExpirationDate(QDateTime::currentDateTime());
|
||||
metaData.setSaveToDisk(true);
|
||||
|
||||
cache.setupWithOne(tempDir.path(), url, metaData);
|
||||
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
QNetworkCacheMetaData cacheMetaData = cache.metaData(url);
|
||||
QVERIFY(cacheMetaData.isValid());
|
||||
QCOMPARE(metaData, cacheMetaData);
|
||||
}
|
||||
}
|
||||
|
||||
// public bool remove(QUrl const& url)
|
||||
void tst_QNetworkDiskCache::remove()
|
||||
{
|
||||
SubQNetworkDiskCache cache;
|
||||
QUrl url(EXAMPLE_URL);
|
||||
cache.setupWithOne(tempDir.path(), url);
|
||||
QString cacheDirectory = cache.cacheDirectory();
|
||||
QCOMPARE(countFiles(cacheDirectory).size(), NUM_SUBDIRECTORIES + 3);
|
||||
cache.remove(url);
|
||||
QCOMPARE(countFiles(cacheDirectory).size(), NUM_SUBDIRECTORIES + 2);
|
||||
}
|
||||
|
||||
void tst_QNetworkDiskCache::accessAfterRemove() // QTBUG-17400
|
||||
{
|
||||
QByteArray data("HTTP/1.1 200 OK\r\n"
|
||||
"Content-Length: 1\r\n"
|
||||
"\r\n"
|
||||
"a");
|
||||
|
||||
MiniHttpServer server(data);
|
||||
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
||||
SubQNetworkDiskCache subCache;
|
||||
subCache.setCacheDirectory(QLatin1String("cacheDir"));
|
||||
diskCache = &subCache;
|
||||
manager->setCache(&subCache);
|
||||
|
||||
url = QUrl("http://127.0.0.1:" + QString::number(server.serverPort()));
|
||||
QNetworkRequest request(url);
|
||||
|
||||
QNetworkReply *reply = manager->get(request);
|
||||
connect(reply, SIGNAL(readyRead()), this, SLOT(accessAfterRemoveReadyReadSlot()));
|
||||
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
|
||||
|
||||
QTestEventLoop::instance().enterLoop(5);
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
|
||||
reply->deleteLater();
|
||||
manager->deleteLater();
|
||||
}
|
||||
|
||||
void tst_QNetworkDiskCache::accessAfterRemoveReadyReadSlot()
|
||||
{
|
||||
diskCache->remove(url); // this used to cause a crash later on
|
||||
}
|
||||
|
||||
void tst_QNetworkDiskCache::setCookieHeader() // QTBUG-41514
|
||||
{
|
||||
SubQNetworkDiskCache *cache = new SubQNetworkDiskCache();
|
||||
url = QUrl("http://localhost:4/cookieTest.html"); // hopefully no one is running an HTTP server on port 4
|
||||
QNetworkCacheMetaData metaData;
|
||||
metaData.setUrl(url);
|
||||
|
||||
QNetworkCacheMetaData::RawHeaderList headers;
|
||||
headers.append(QNetworkCacheMetaData::RawHeader("Set-Cookie", "aaa=bbb"));
|
||||
metaData.setRawHeaders(headers);
|
||||
metaData.setSaveToDisk(true);
|
||||
QDateTime expirationDate = QDateTime::currentDateTime().addSecs(500);
|
||||
metaData.setExpirationDate(expirationDate);
|
||||
cache->setupWithOne(tempDir.path(), url, metaData);
|
||||
|
||||
manager = new QNetworkAccessManager();
|
||||
manager->setCache(cache);
|
||||
|
||||
QNetworkRequest request(url);
|
||||
request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
|
||||
QNetworkReply *reply = manager->get(request);
|
||||
connect(reply, SIGNAL(metaDataChanged()), this, SLOT(setCookieHeaderMetaDataChangedSlot()));
|
||||
connect(reply, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
|
||||
|
||||
QTestEventLoop::instance().enterLoop(5);
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
|
||||
QCOMPARE(reply->error(), QNetworkReply::NoError);
|
||||
|
||||
reply->deleteLater();
|
||||
manager->deleteLater();
|
||||
}
|
||||
|
||||
void tst_QNetworkDiskCache::setCookieHeaderMetaDataChangedSlot()
|
||||
{
|
||||
QList<QNetworkCookie> actualCookieJar = manager->cookieJar()->cookiesForUrl(url);
|
||||
QVERIFY(!actualCookieJar.empty());
|
||||
}
|
||||
|
||||
void tst_QNetworkDiskCache::setCacheDirectory_data()
|
||||
{
|
||||
QTest::addColumn<QString>("cacheDir");
|
||||
QTest::newRow("null") << QString();
|
||||
QDir dir("foo");
|
||||
QTest::newRow("foo") << dir.absolutePath() + QString("/");
|
||||
}
|
||||
|
||||
// public void setCacheDirectory(QString const& cacheDir)
|
||||
void tst_QNetworkDiskCache::setCacheDirectory()
|
||||
{
|
||||
QFETCH(QString, cacheDir);
|
||||
|
||||
SubQNetworkDiskCache cache;
|
||||
cache.setCacheDirectory(cacheDir);
|
||||
QCOMPARE(cache.cacheDirectory(), cacheDir);
|
||||
}
|
||||
|
||||
// public void updateMetaData(QNetworkCacheMetaData const& metaData)
|
||||
void tst_QNetworkDiskCache::updateMetaData()
|
||||
{
|
||||
QUrl url(EXAMPLE_URL);
|
||||
SubQNetworkDiskCache cache;
|
||||
cache.setupWithOne(tempDir.path(), url);
|
||||
|
||||
QNetworkCacheMetaData metaData = cache.metaData(url);
|
||||
metaData.setLastModified(QDateTime::currentDateTime());
|
||||
cache.updateMetaData(metaData);
|
||||
QNetworkCacheMetaData newMetaData = cache.metaData(url);
|
||||
QCOMPARE(newMetaData, metaData);
|
||||
}
|
||||
|
||||
// protected QNetworkCacheMetaData fileMetaData(QString const& fileName)
|
||||
void tst_QNetworkDiskCache::fileMetaData()
|
||||
{
|
||||
SubQNetworkDiskCache cache;
|
||||
QUrl url(EXAMPLE_URL);
|
||||
cache.setupWithOne(tempDir.path(), url);
|
||||
|
||||
url.setPassword(QString());
|
||||
url.setFragment(QString());
|
||||
|
||||
QString cacheDirectory = cache.cacheDirectory();
|
||||
QStringList list = countFiles(cacheDirectory);
|
||||
QCOMPARE(list.size(), NUM_SUBDIRECTORIES + 3);
|
||||
foreach(QString fileName, list) {
|
||||
QFileInfo info(fileName);
|
||||
if (info.isFile()) {
|
||||
QNetworkCacheMetaData metaData = cache.call_fileMetaData(fileName);
|
||||
QCOMPARE(metaData.url(), url);
|
||||
}
|
||||
}
|
||||
|
||||
QTemporaryFile file(cacheDirectory + "/qt_temp.XXXXXX");
|
||||
if (file.open()) {
|
||||
QNetworkCacheMetaData metaData = cache.call_fileMetaData(file.fileName());
|
||||
QVERIFY(!metaData.isValid());
|
||||
}
|
||||
}
|
||||
|
||||
// protected qint64 expire()
|
||||
void tst_QNetworkDiskCache::expire()
|
||||
{
|
||||
SubQNetworkDiskCache cache;
|
||||
cache.setCacheDirectory(tempDir.path());
|
||||
QCOMPARE(cache.call_expire(), (qint64)0);
|
||||
QUrl url(EXAMPLE_URL);
|
||||
cache.setupWithOne(tempDir.path(), url);
|
||||
QVERIFY(cache.call_expire() > (qint64)0);
|
||||
qint64 limit = (1024 * 1024 / 4) * 5;
|
||||
cache.setMaximumCacheSize(limit);
|
||||
|
||||
qint64 max = cache.maximumCacheSize();
|
||||
QCOMPARE(max, limit);
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
if (i % 3 == 0)
|
||||
QTest::qWait(2000);
|
||||
QNetworkCacheMetaData m;
|
||||
m.setUrl(QUrl("http://localhost:4/" + QString::number(i)));
|
||||
QIODevice *d = cache.prepare(m);
|
||||
QString bigString;
|
||||
bigString.fill(QLatin1Char('Z'), (1024 * 1024 / 4));
|
||||
d->write(bigString.toLatin1().data());
|
||||
cache.insert(d);
|
||||
QVERIFY(cache.call_expire() < max);
|
||||
}
|
||||
|
||||
QString cacheDirectory = cache.cacheDirectory();
|
||||
QStringList list = countFiles(cacheDirectory);
|
||||
QStringList cacheList;
|
||||
foreach(QString fileName, list) {
|
||||
QFileInfo info(fileName);
|
||||
if (info.isFile()) {
|
||||
QNetworkCacheMetaData metaData = cache.call_fileMetaData(fileName);
|
||||
cacheList.append(metaData.url().toString());
|
||||
}
|
||||
}
|
||||
std::sort(cacheList.begin(), cacheList.end());
|
||||
for (int i = 0; i < cacheList.size(); ++i) {
|
||||
QString fileName = cacheList[i];
|
||||
QCOMPARE(fileName, QLatin1String("http://localhost:4/") + QString::number(i + 6));
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QNetworkDiskCache::oldCacheVersionFile_data()
|
||||
{
|
||||
QTest::addColumn<int>("pass");
|
||||
QTest::newRow("0") << 0;
|
||||
QTest::newRow("1") << 1;
|
||||
}
|
||||
|
||||
void tst_QNetworkDiskCache::oldCacheVersionFile()
|
||||
{
|
||||
QFETCH(int, pass);
|
||||
SubQNetworkDiskCache cache;
|
||||
QUrl url(EXAMPLE_URL);
|
||||
cache.setupWithOne(tempDir.path(), url);
|
||||
|
||||
if (pass == 0) {
|
||||
QString name;
|
||||
{
|
||||
QTemporaryFile file(cache.cacheDirectory() + "/XXXXXX.d");
|
||||
file.setAutoRemove(false);
|
||||
QVERIFY2(file.open(), qPrintable(file.errorString()));
|
||||
QDataStream out(&file);
|
||||
out << qint32(0xe8);
|
||||
out << qint32(2);
|
||||
name = file.fileName();
|
||||
file.close();
|
||||
}
|
||||
|
||||
QVERIFY(QFile::exists(name));
|
||||
QNetworkCacheMetaData metaData = cache.call_fileMetaData(name);
|
||||
QVERIFY(!metaData.isValid());
|
||||
QVERIFY(!QFile::exists(name));
|
||||
} else {
|
||||
QStringList files = countFiles(cache.cacheDirectory());
|
||||
QCOMPARE(files.size(), NUM_SUBDIRECTORIES + 3);
|
||||
// find the file
|
||||
QString cacheFile;
|
||||
foreach (QString file, files) {
|
||||
QFileInfo info(file);
|
||||
if (info.isFile())
|
||||
cacheFile = file;
|
||||
}
|
||||
QVERIFY(QFile::exists(cacheFile));
|
||||
|
||||
QFile file(cacheFile);
|
||||
QVERIFY(file.open(QFile::ReadWrite));
|
||||
QDataStream out(&file);
|
||||
out << qint32(0xe8);
|
||||
out << qint32(2);
|
||||
file.close();
|
||||
|
||||
QIODevice *device = cache.data(url);
|
||||
QVERIFY(!device);
|
||||
QVERIFY(!QFile::exists(cacheFile));
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QNetworkDiskCache::streamVersion_data()
|
||||
{
|
||||
QTest::addColumn<int>("version");
|
||||
QTest::newRow("Qt 5.1") << int(QDataStream::Qt_5_1);
|
||||
QDataStream ds;
|
||||
QTest::newRow("current") << ds.version();
|
||||
QTest::newRow("higher than current") << ds.version() + 1;
|
||||
}
|
||||
|
||||
void tst_QNetworkDiskCache::streamVersion()
|
||||
{
|
||||
SubQNetworkDiskCache cache;
|
||||
QUrl url(EXAMPLE_URL);
|
||||
cache.setupWithOne(tempDir.path(), url);
|
||||
|
||||
QString cacheFile;
|
||||
// find the file
|
||||
QStringList files = countFiles(cache.cacheDirectory());
|
||||
foreach (const QString &file, files) {
|
||||
QFileInfo info(file);
|
||||
if (info.isFile()) {
|
||||
cacheFile = file;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QFile file(cacheFile);
|
||||
QVERIFY(file.open(QFile::ReadWrite|QIODevice::Truncate));
|
||||
QDataStream out(&file);
|
||||
QFETCH(int, version);
|
||||
if (version < out.version())
|
||||
out.setVersion(version);
|
||||
out << qint32(0xe8); // cache magic
|
||||
// Following code works only for cache file version 8 and should be updated on version change
|
||||
out << qint32(8);
|
||||
out << qint32(version);
|
||||
|
||||
QNetworkCacheMetaData md;
|
||||
md.setUrl(url);
|
||||
QNetworkCacheMetaData::RawHeader header("content-type", "text/html");
|
||||
QNetworkCacheMetaData::RawHeaderList list;
|
||||
list.append(header);
|
||||
md.setRawHeaders(list);
|
||||
md.setLastModified(QDateTime::currentDateTimeUtc().toOffsetFromUtc(3600));
|
||||
out << md;
|
||||
|
||||
bool compressed = true;
|
||||
out << compressed;
|
||||
|
||||
QByteArray data("Hello World!");
|
||||
out << qCompress(data);
|
||||
|
||||
file.close();
|
||||
|
||||
QNetworkCacheMetaData cachedMetaData = cache.call_fileMetaData(cacheFile);
|
||||
if (version > out.version()) {
|
||||
QVERIFY(!cachedMetaData.isValid());
|
||||
QVERIFY(!QFile::exists(cacheFile));
|
||||
} else {
|
||||
QVERIFY(cachedMetaData.isValid());
|
||||
QVERIFY(QFile::exists(cacheFile));
|
||||
QIODevice *dataDevice = cache.data(url);
|
||||
QVERIFY(dataDevice != 0);
|
||||
QByteArray cachedData = dataDevice->readAll();
|
||||
QCOMPARE(cachedData, data);
|
||||
}
|
||||
}
|
||||
|
||||
class Runner : public QThread
|
||||
{
|
||||
|
||||
public:
|
||||
Runner(const QString& cachePath)
|
||||
: QThread()
|
||||
, other(0)
|
||||
, cachePath(cachePath)
|
||||
{}
|
||||
|
||||
void run() override
|
||||
{
|
||||
QByteArray longString = "Hello World, this is some long string, well not really that long";
|
||||
for (int j = 0; j < 10; ++j)
|
||||
longString += longString;
|
||||
QByteArray longString2 = "Help, I am stuck in an autotest!";
|
||||
QUrl url(EXAMPLE_URL);
|
||||
|
||||
QNetworkCacheMetaData metaData;
|
||||
metaData.setUrl(url);
|
||||
QNetworkCacheMetaData::RawHeaderList headers;
|
||||
headers.append(QNetworkCacheMetaData::RawHeader("type", "bin"));
|
||||
metaData.setRawHeaders(headers);
|
||||
metaData.setLastModified(dt);
|
||||
metaData.setSaveToDisk(true);
|
||||
|
||||
QNetworkCacheMetaData metaData2 = metaData;
|
||||
metaData2.setExpirationDate(dt);
|
||||
|
||||
QNetworkDiskCache cache;
|
||||
cache.setCacheDirectory(cachePath);
|
||||
|
||||
int i = 0;
|
||||
for (; i < 5000; ++i) {
|
||||
if (other && other->isFinished())
|
||||
break;
|
||||
|
||||
if (write) {
|
||||
QNetworkCacheMetaData m;
|
||||
if (QRandomGenerator::global()->bounded(2) == 0)
|
||||
m = metaData;
|
||||
else
|
||||
m = metaData2;
|
||||
|
||||
if (QRandomGenerator::global()->bounded(20) == 1) {
|
||||
//qDebug() << "write update";
|
||||
cache.updateMetaData(m);
|
||||
continue;
|
||||
}
|
||||
|
||||
QIODevice *device = cache.prepare(m);
|
||||
if (QRandomGenerator::global()->bounded(20) == 1) {
|
||||
//qDebug() << "write remove";
|
||||
cache.remove(url);
|
||||
continue;
|
||||
}
|
||||
QVERIFY(device);
|
||||
if (QRandomGenerator::global()->bounded(2) == 0)
|
||||
device->write(longString);
|
||||
else
|
||||
device->write(longString2);
|
||||
//qDebug() << "write write" << device->size();
|
||||
cache.insert(device);
|
||||
continue;
|
||||
}
|
||||
|
||||
QNetworkCacheMetaData gotMetaData = cache.metaData(url);
|
||||
if (gotMetaData.isValid()) {
|
||||
QVERIFY(gotMetaData == metaData || gotMetaData == metaData2);
|
||||
QIODevice *d = cache.data(url);
|
||||
if (d) {
|
||||
QByteArray x = d->readAll();
|
||||
if (x != longString && x != longString2) {
|
||||
qDebug() << x.size() << QString(x);
|
||||
gotMetaData = cache.metaData(url);
|
||||
qDebug() << (gotMetaData.url().toString())
|
||||
<< gotMetaData.lastModified()
|
||||
<< gotMetaData.expirationDate()
|
||||
<< gotMetaData.saveToDisk();
|
||||
}
|
||||
if (gotMetaData.isValid())
|
||||
QVERIFY(x == longString || x == longString2);
|
||||
delete d;
|
||||
}
|
||||
}
|
||||
if (QRandomGenerator::global()->bounded(5) == 1)
|
||||
cache.remove(url);
|
||||
if (QRandomGenerator::global()->bounded(5) == 1)
|
||||
cache.clear();
|
||||
sleep(0);
|
||||
}
|
||||
}
|
||||
|
||||
QDateTime dt;
|
||||
bool write;
|
||||
Runner *other;
|
||||
QString cachePath;
|
||||
};
|
||||
|
||||
void tst_QNetworkDiskCache::crashWhenParentingCache()
|
||||
{
|
||||
// the trick here is to not send the complete response
|
||||
// but some data. So we get a readyRead() and it gets tried
|
||||
// to be saved to the cache
|
||||
QByteArray data("HTTP/1.0 200 OK\r\nCache-Control: max-age=300\r\nAge: 1\r\nContent-Length: 5\r\n\r\n123");
|
||||
MiniHttpServer server(data);
|
||||
|
||||
QNetworkAccessManager *manager = new QNetworkAccessManager();
|
||||
QNetworkDiskCache *diskCache = new QNetworkDiskCache(manager); // parent to qnam!
|
||||
// we expect the temp dir to be cleaned at some point anyway
|
||||
|
||||
const QString diskCachePath = QDir::tempPath() + QLatin1String("/cacheDir_")
|
||||
+ QString::number(QCoreApplication::applicationPid());
|
||||
diskCache->setCacheDirectory(diskCachePath);
|
||||
manager->setCache(diskCache);
|
||||
|
||||
QUrl url("http://127.0.0.1:" + QString::number(server.serverPort()));
|
||||
QNetworkRequest request(url);
|
||||
// request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::AlwaysNetwork);
|
||||
QNetworkReply *reply = manager->get(request); // new reply is parented to qnam
|
||||
|
||||
// wait for readyRead of reply!
|
||||
connect(reply, SIGNAL(readyRead()), &QTestEventLoop::instance(), SLOT(exitLoop()));
|
||||
QTestEventLoop::instance().enterLoop(5);
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
|
||||
delete manager; // crashed before..
|
||||
}
|
||||
|
||||
void tst_QNetworkDiskCache::sync()
|
||||
{
|
||||
// This tests would be a nice to have, but is currently not supported.
|
||||
return;
|
||||
|
||||
QTime midnight(0, 0, 0);
|
||||
Runner reader(tempDir.path());
|
||||
reader.dt = QDateTime::currentDateTime();
|
||||
reader.write = false;
|
||||
|
||||
Runner writer(tempDir.path());
|
||||
writer.dt = reader.dt;
|
||||
writer.write = true;
|
||||
|
||||
writer.other = &reader;
|
||||
reader.other = &writer;
|
||||
|
||||
writer.start();
|
||||
reader.start();
|
||||
writer.wait();
|
||||
reader.wait();
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QNetworkDiskCache)
|
||||
#include "tst_qnetworkdiskcache.moc"
|
||||
|
BIN
tests/auto/network/access/qnetworkreply/4G.br
Normal file
BIN
tests/auto/network/access/qnetworkreply/4G.br
Normal file
Binary file not shown.
52
tests/auto/network/access/qnetworkreply/BLACKLIST
Normal file
52
tests/auto/network/access/qnetworkreply/BLACKLIST
Normal file
@ -0,0 +1,52 @@
|
||||
# See qtbase/src/testlib/qtestblacklist.cpp for format
|
||||
[getErrors:ftp-host]
|
||||
linux
|
||||
[ioPostToHttpFromSocket]
|
||||
osx
|
||||
[ioHttpRedirectMultipartPost]
|
||||
b2qt
|
||||
linux
|
||||
[ioHttpRedirectPolicy]
|
||||
opensuse-leap
|
||||
b2qt
|
||||
ubuntu
|
||||
windows-10
|
||||
[putToFtp]
|
||||
windows-10
|
||||
[backgroundRequest]
|
||||
macos
|
||||
[deleteFromHttp]
|
||||
macos
|
||||
[httpCanReadLine]
|
||||
macos
|
||||
[httpRecursiveCreation]
|
||||
osx
|
||||
[httpWithNoCredentialUsage]
|
||||
macos
|
||||
[ioGetFromBuiltinHttp]
|
||||
osx
|
||||
[ioPostToHttpFromFile]
|
||||
macos
|
||||
[ioPostToHttpFromSocketSynchronous]
|
||||
osx
|
||||
[ioPostToHttpUploadProgress]
|
||||
osx
|
||||
[ioPutToHttpFromFile]
|
||||
macos
|
||||
[lastModifiedHeaderForHttp]
|
||||
macos
|
||||
[postToHttpSynchronous]
|
||||
macos
|
||||
[putToHttpSynchronous]
|
||||
macos
|
||||
[putToHttpsSynchronous]
|
||||
osx
|
||||
[receiveCookiesFromHttpSynchronous]
|
||||
osx
|
||||
[sendCookiesSynchronous]
|
||||
osx
|
||||
[backgroundRequestConnectInBackground]
|
||||
osx
|
||||
#QTBUG-103055
|
||||
[ioGetFromHttpWithProxyAuth]
|
||||
qnx
|
5
tests/auto/network/access/qnetworkreply/CMakeLists.txt
Normal file
5
tests/auto/network/access/qnetworkreply/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
add_subdirectory(echo)
|
||||
add_subdirectory(test)
|
17980
tests/auto/network/access/qnetworkreply/bigfile
Normal file
17980
tests/auto/network/access/qnetworkreply/bigfile
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,22 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDnDCCAoQCCQDV3otC4hs2KTANBgkqhkiG9w0BAQUFADCBjzELMAkGA1UEBhMC
|
||||
Tk8xDTALBgNVBAgTBE9zbG8xDTALBgNVBAcTBE9zbG8xDzANBgNVBAoTBlRUIEFT
|
||||
QTEOMAwGA1UECxMFUVQgU1cxHDAaBgNVBAMTE2FzcGlyaW5pa3MudHJvbGwubm8x
|
||||
IzAhBgkqhkiG9w0BCQEWFGFiYWJpY0B0cm9sbHRlY2guY29tMB4XDTA4MTEwMTA4
|
||||
NTcyOFoXDTA5MTEwMTA4NTcyOFowgY8xCzAJBgNVBAYTAk5PMQ0wCwYDVQQIEwRP
|
||||
c2xvMQ0wCwYDVQQHEwRPc2xvMQ8wDQYDVQQKEwZUVCBBU0ExDjAMBgNVBAsTBVFU
|
||||
IFNXMRwwGgYDVQQDExNhc3BpcmluaWtzLnRyb2xsLm5vMSMwIQYJKoZIhvcNAQkB
|
||||
FhRhYmFiaWNAdHJvbGx0ZWNoLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
|
||||
AQoCggEBAMV2bMD1DN3DMgbxU3DXw2i7EWGDXcWjTDtdHvqgIb+9nHqo3MJSrzJy
|
||||
qgEPoOsXqswMla9wDPZAsWv5gVAmVSqpy2lfEgfY7LaSHiGD75seF7zIy+CxREHW
|
||||
DofHXpJGGJpBCZEKQt2HfHu3+yAYNPucN78tWNZAcPbUg5tfxMZeepRimAZNIxBI
|
||||
93SDrl/f9Ka7hvPSzUQsnp8hfdpHlFPFznKfD6yPrjxgz2mT9efavJ4DhtyIa4m+
|
||||
paiX515CidDz4A8CFxKZbYvuqq1ilibF/si2so9VhALC77ZcAJP1IMuT8T+WUCxq
|
||||
skJqiSCncl0Hgr+ba8MDGF9UQYowgjMCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEA
|
||||
KcJuNUHvjB8ok3cnTmQEeF0LPPkgj28Tqb5TFB8xpVfRI+wvTYsHsmGdOKCgYJ3a
|
||||
7VflIsr63ojG8/rXK8H/cx2o2f2Hr3liJdi1UnoLDDRjBqGGz7JNuMreYokPvIbm
|
||||
eP01mVyK4PO2iYRwHUIAw5eeB1vMWKX2z95MupD+HRLtmGyaLALg8aQxj5N84Ewl
|
||||
eU2PQfhv8A1wj7aL17kfEUxDerQ1kUzlThJMV1J8Dl0l4C9N8evQkelROJU00i46
|
||||
oJikA8BW6EpgbnGyNyyj5Loy4wLPKew9nTS8MCJ5xPMQc0urbY/VzuOeUK7WQof7
|
||||
xOFSsRAVyQv+yqgmcZMCtg==
|
||||
-----END CERTIFICATE-----
|
75
tests/auto/network/access/qnetworkreply/certs/fluke.cert
Normal file
75
tests/auto/network/access/qnetworkreply/certs/fluke.cert
Normal file
@ -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-----
|
15
tests/auto/network/access/qnetworkreply/certs/fluke.key
Normal file
15
tests/auto/network/access/qnetworkreply/certs/fluke.key
Normal file
@ -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-----
|
@ -0,0 +1,16 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIClTCCAf4CCQC2xMhNhwvATDANBgkqhkiG9w0BAQQFADCBjjELMAkGA1UEChMC
|
||||
UXQxGTAXBgNVBAsTEENvcmUgQW5kIE5ldHdvcmsxGzAZBgkqhkiG9w0BCQEWDG5v
|
||||
Ym9keS5xdC5pbzENMAsGA1UEBxMET3NsbzENMAsGA1UECBMET3NsbzELMAkGA1UE
|
||||
BhMCTk8xHDAaBgNVBAMUEyoudGVzdC1uZXQucXQubG9jYWwwHhcNMTgwNzAxMTgz
|
||||
NjI3WhcNNDgwNjIzMTgzNjI3WjCBjjELMAkGA1UEChMCUXQxGTAXBgNVBAsTEENv
|
||||
cmUgQW5kIE5ldHdvcmsxGzAZBgkqhkiG9w0BCQEWDG5vYm9keS5xdC5pbzENMAsG
|
||||
A1UEBxMET3NsbzENMAsGA1UECBMET3NsbzELMAkGA1UEBhMCTk8xHDAaBgNVBAMU
|
||||
EyoudGVzdC1uZXQucXQubG9jYWwwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB
|
||||
AM2q22/WNMmn8cC+5EEYGeICySLmp9W6Ay6eKHr0Xxp3X3epETuPfvAuxp7rOtkS
|
||||
18EMUegkUj8jw0IMEcbyHKFC/rTCaYOt93CxGBXMIChiMPAsFeYzGa/D6xzAkfcR
|
||||
aJRQ+Ek3CDLXPnXfo7xpABXezYcPXAJrgsgBfWrwHdxzAgMBAAEwDQYJKoZIhvcN
|
||||
AQEEBQADgYEAZu/lQPy8PXeyyYGamOVms/FZKJ48BH1y8KC3BeBU5FYnhvgG7pz8
|
||||
Wz9JKvt2t/r45wQeAkNL6HnGUBhPJsHMjPHl5KktqN+db3D+FQygBeS2V1+zmC0X
|
||||
UZNRE4aWiHvt1Lq+pTx89SOMOpfqWfh4qTQKiE5jC2V4DeCNQ3u7uI8=
|
||||
-----END CERTIFICATE-----
|
@ -0,0 +1,17 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICyjCCAjMCFHPGDqJR+klHni4XbETMk6GLn/UEMA0GCSqGSIb3DQEBDQUAMIGj
|
||||
MRcwFQYDVQQKEw5UaGUgUXQgQ29tcGFueTEUMBIGA1UECxMLUXQgU29mdHdhcmUx
|
||||
IjAgBgkqhkiG9w0BCQEWE25vYm9keUBub2RvbWFpbi5vcmcxDTALBgNVBAcTBE9z
|
||||
bG8xDTALBgNVBAgTBE9zbG8xCzAJBgNVBAYTAk5PMSMwIQYDVQQDExpxdC10ZXN0
|
||||
LXNlcnZlci5xdC10ZXN0LW5ldDAeFw0yMjA2MjQxMTU4NDlaFw0zMjA2MjExMTU4
|
||||
NDlaMIGjMRcwFQYDVQQKEw5UaGUgUXQgQ29tcGFueTEUMBIGA1UECxMLUXQgU29m
|
||||
dHdhcmUxIjAgBgkqhkiG9w0BCQEWE25vYm9keUBub2RvbWFpbi5vcmcxDTALBgNV
|
||||
BAcTBE9zbG8xDTALBgNVBAgTBE9zbG8xCzAJBgNVBAYTAk5PMSMwIQYDVQQDExpx
|
||||
dC10ZXN0LXNlcnZlci5xdC10ZXN0LW5ldDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
|
||||
gYkCgYEAzarbb9Y0yafxwL7kQRgZ4gLJIuan1boDLp4oevRfGndfd6kRO49+8C7G
|
||||
nus62RLXwQxR6CRSPyPDQgwRxvIcoUL+tMJpg633cLEYFcwgKGIw8CwV5jMZr8Pr
|
||||
HMCR9xFolFD4STcIMtc+dd+jvGkAFd7Nhw9cAmuCyAF9avAd3HMCAwEAATANBgkq
|
||||
hkiG9w0BAQ0FAAOBgQCZyRe25WqOjrNS6BKPs7ep7eyCON3NKdWnfABZrSjGJQ87
|
||||
PoFKl6+9YBSlSpl8qk7c29ic+wA4qFQzPJkrbYIXjwVMAr+cC1kVrlUVqcwmvnKo
|
||||
5vj57/v8S0Uc4/GesIsxZR7QM+3diPDyk7Bsc3IkpINb31Dl0mlg25nztg8NxA==
|
||||
-----END CERTIFICATE-----
|
@ -0,0 +1,16 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIClzCCAgACCQDeuuUc2HkfKDANBgkqhkiG9w0BAQQFADCBjzELMAkGA1UEChMC
|
||||
UXQxGTAXBgNVBAsTEENvcmUgQW5kIE5ldHdvcmsxGzAZBgkqhkiG9w0BCQEWDG5v
|
||||
Ym9keS5xdC5pbzENMAsGA1UEBxMET3NsbzENMAsGA1UECBMET3NsbzELMAkGA1UE
|
||||
BhMCTk8xHTAbBgNVBAMTFHF0LXRlc3Qtc2VydmVyLmxvY2FsMB4XDTE5MDEyNTE1
|
||||
NDE0N1oXDTQ5MDExNzE1NDE0N1owgY8xCzAJBgNVBAoTAlF0MRkwFwYDVQQLExBD
|
||||
b3JlIEFuZCBOZXR3b3JrMRswGQYJKoZIhvcNAQkBFgxub2JvZHkucXQuaW8xDTAL
|
||||
BgNVBAcTBE9zbG8xDTALBgNVBAgTBE9zbG8xCzAJBgNVBAYTAk5PMR0wGwYDVQQD
|
||||
ExRxdC10ZXN0LXNlcnZlci5sb2NhbDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkC
|
||||
gYEAzarbb9Y0yafxwL7kQRgZ4gLJIuan1boDLp4oevRfGndfd6kRO49+8C7Gnus6
|
||||
2RLXwQxR6CRSPyPDQgwRxvIcoUL+tMJpg633cLEYFcwgKGIw8CwV5jMZr8PrHMCR
|
||||
9xFolFD4STcIMtc+dd+jvGkAFd7Nhw9cAmuCyAF9avAd3HMCAwEAATANBgkqhkiG
|
||||
9w0BAQQFAAOBgQB1dxK3Ia4sCpvSikKLaf1ZXu+9GKaNWKJe9bWex9/RmNOla9N2
|
||||
FIh6/CfaPFDy/OXCkyEiGg78iyg/DgqVoa9JJGV3diI6berisHMPJpv1syyz9YEU
|
||||
G3RQUClPcPV6EcedyqCdpbnIFtiSZbtJ0ZBGef4KzBN3rTmPucKb+bhMPg==
|
||||
-----END CERTIFICATE-----
|
27
tests/auto/network/access/qnetworkreply/certs/server.key
Normal file
27
tests/auto/network/access/qnetworkreply/certs/server.key
Normal file
@ -0,0 +1,27 @@
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEowIBAAKCAQEAyinVk3QBbjS+UczWP+jnugFn5YZuOnCPlPK0SmeUiZW0x4PA
|
||||
kXoks7LSra+XT2hg07rPBhEyQUE13qYw+RVBSexvhw2RDg76oV17jt7jVjb04hhK
|
||||
bSBKisW4UHF0rvyzoWzJzVxaqxfcFcYT7uE+t0cnCHi/MGX+9gUI8Dz46IopCA5k
|
||||
fKmA+XnF//Ov8wokIN4Wk0lqkAyWDCg/O5Av6H/zbr/U3CCI5eI5cRRIwSMDxbPX
|
||||
v9b+dgvxhMJGMku6UMMhSfk9ac6FCSNghYB7w3C9zIGiA/tOHysujwGzpzRKDzT5
|
||||
P/qNqLkLOxvspUh32BD/jgopAhoNi9pDm60iLQIDAQABAoIBAA/E45v02Ie4JYBL
|
||||
8gpaKHkh0vDcY4y7ave7VsTW/4cb3lYRuNugI2zA7h4OLEdNZQAe+jcG8FyWsZUE
|
||||
cZ18QvN5Ndna/Q2TrYkYuaKTUDhRYRihvGx2sFnSwmXD884SeBCHY9ZY9dmSquAn
|
||||
6zYe671wF2NZx9AGpLSb/+59Uw0QVkCDf23tb7ey5vHXJnNq5NINOnv1sNH/zbYR
|
||||
hJnUEVgRLkpda0r2LqIHbrCpcgjWQeoKscTzxTI016LAozBSqAvoLt4QYuvY8kI7
|
||||
boK8KF49HEwTydjgDI/W3Xa0YEzbXVLEReuWoMKFeayNp+GSFy0SwkzjY4zpUP1N
|
||||
xX6/2CECgYEA83fifDH8e4g1y8MuI8LzDRsMPOsvl3ZnB31JAcvUlLheVF1H3Slt
|
||||
NEGSKYtx4zb0o+5VKy6k6dqF8VXDcPDyyvItyZYZtv7YLIhu/HBJypr9NfdICNnK
|
||||
aPQWRZ/piAEi73vxx1qwIZapz1cJWg95mRv/QYVf8Xb7PgKcu5UL+ysCgYEA1JGv
|
||||
t1gNsKc4BtHmYmTnzdxz4GhkgJY1y/XfGzc2CfRPxo16Fob1WqQCTf1DtsCm0zTi
|
||||
sJdUBq/acMeeyTA6eA4LyfgEVVRVY4+kurW7JNGkR6xbWtWQX11jkVGOZ65MIvtY
|
||||
ZMg3xo3w+hYvMhK0ZC9aSXgnl0crvGAJtd8ZzAcCgYAwnTmODvUZPYNwYlKuNVkO
|
||||
vt3ctCFWnv/HkQ6o2yhhYccEFXQqBwGVM5qZzQw6kFic+xPqgW/Qeh/Qpo1V2ebA
|
||||
+0aFQAF2dsB3c+6lXU5+tB/nTK8HhWVTO5nO4TViQMfXBeqrIcKVkl3p1rk5UGm5
|
||||
VsvLK3SS5G0aXq8pDYPM7QKBgQChsQvjP8RyGlCAx4siTzUQH1+5VE8WjKvxMF58
|
||||
OjwNyFwiYR18Iz5gqx7hqgOm8NY1FCZXQ1T0HTHg1cdPrDLdfXm0MMdDDPpC2FHq
|
||||
gDARarI2nsGCz66ZC9WgBVR4Q1nAxkXPq4jZrMCfyt4tjZLQHkDkX9RluwpmqPrZ
|
||||
8BGUYwKBgB1u8mPXIxyGSHYitqf40eIr5yzlrCgDWoqRQf4jFSUNyB/+YT2VqIXu
|
||||
WfixkX9WW0sx/c79c9791Sf+vp9+DPPtMYDGc6y0xrbxyC+yT7Uo4Azbs/g3Kftl
|
||||
WhYt/L1CB5oOcilYGR+YodN0l2tV1WrCNSNdtbPyoDHrM9S22Rjm
|
||||
-----END RSA PRIVATE KEY-----
|
22
tests/auto/network/access/qnetworkreply/certs/server.pem
Normal file
22
tests/auto/network/access/qnetworkreply/certs/server.pem
Normal file
@ -0,0 +1,22 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDrzCCApcCFGtyyXYbHuuIIseKAiLxL5nMKEnwMA0GCSqGSIb3DQEBCwUAMIGT
|
||||
MQswCQYDVQQGEwJOTzENMAsGA1UECAwET3NsbzEQMA4GA1UEBwwHTnlkYWxlbjEN
|
||||
MAsGA1UECgwEVFF0QzEUMBIGA1UECwwLRGV2ZWxvcG1lbnQxFzAVBgNVBAMMDmZs
|
||||
dWtlLnRyb2xsLm5vMSUwIwYJKoZIhvcNAQkBFhZhaGFuc3NlbkB0cm9sbHRlY2gu
|
||||
Y29tMB4XDTIyMDcwODA2Mjc1OVoXDTMyMDcwNTA2Mjc1OVowgZMxCzAJBgNVBAYT
|
||||
Ak5PMQ0wCwYDVQQIDARPc2xvMRAwDgYDVQQHDAdOeWRhbGVuMQ0wCwYDVQQKDARU
|
||||
UXRDMRQwEgYDVQQLDAtEZXZlbG9wbWVudDEXMBUGA1UEAwwOZmx1a2UudHJvbGwu
|
||||
bm8xJTAjBgkqhkiG9w0BCQEWFmFoYW5zc2VuQHRyb2xsdGVjaC5jb20wggEiMA0G
|
||||
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDKKdWTdAFuNL5RzNY/6Oe6AWflhm46
|
||||
cI+U8rRKZ5SJlbTHg8CReiSzstKtr5dPaGDTus8GETJBQTXepjD5FUFJ7G+HDZEO
|
||||
DvqhXXuO3uNWNvTiGEptIEqKxbhQcXSu/LOhbMnNXFqrF9wVxhPu4T63RycIeL8w
|
||||
Zf72BQjwPPjoiikIDmR8qYD5ecX/86/zCiQg3haTSWqQDJYMKD87kC/of/Nuv9Tc
|
||||
IIjl4jlxFEjBIwPFs9e/1v52C/GEwkYyS7pQwyFJ+T1pzoUJI2CFgHvDcL3MgaID
|
||||
+04fKy6PAbOnNEoPNPk/+o2ouQs7G+ylSHfYEP+OCikCGg2L2kObrSItAgMBAAEw
|
||||
DQYJKoZIhvcNAQELBQADggEBALHdGWQ4YqucGJSP1n1ANrLILy+sXqEP7hMdG5HH
|
||||
GDZ/ygUhjTZ/k5Cj0+auC4Aw490l8Tj8gmzt68KJmgSH+z1erY67+fhWtAewDzU5
|
||||
zIMqKHja1hSb5JIdWaD7ZFBQzor2beBO0u+VzegWqe20kw2mkFAcdQTsV28hvr1v
|
||||
rcgpVkegQcmHpr6FBpYFmtnizpPnX5Zm+JJAlvSGvoYMI5i9Vc7/gdx790NeaXmy
|
||||
yD1ueFMfsPtAcZq8cSbGSCS5/pcuhIx+5O9+V8iwN9lKdYksTCLAn4SREHzlgi68
|
||||
SGY0OUMlXeD82K0+mDv+hzSmq4sk7CDGbSxVV5TwzFXDgNc=
|
||||
-----END CERTIFICATE-----
|
1235
tests/auto/network/access/qnetworkreply/data/gzip.rcc.cpp
Normal file
1235
tests/auto/network/access/qnetworkreply/data/gzip.rcc.cpp
Normal file
File diff suppressed because it is too large
Load Diff
116
tests/auto/network/access/qnetworkreply/data/zstandard.rcc.cpp
Normal file
116
tests/auto/network/access/qnetworkreply/data/zstandard.rcc.cpp
Normal file
@ -0,0 +1,116 @@
|
||||
/****************************************************************************
|
||||
** Resource object code
|
||||
**
|
||||
** Created by: The Resource Compiler for Qt version 6.0.0
|
||||
**
|
||||
** WARNING! All changes made in this file will be lost!
|
||||
*****************************************************************************/
|
||||
|
||||
static const unsigned char qt_resource_data[] = {
|
||||
// D:/projects/qt/dev/src/qtbase/tests/auto/network/access/decompresshelper/4G.zst
|
||||
0x0,0x0,0x1,0x75,
|
||||
0x0,
|
||||
0x2,0x3,0x93,0x78,0xda,0xed,0xd4,0x21,0xe,0x83,0x40,0x10,0x86,0xd1,0x29,0x98,
|
||||
0x26,0x98,0x3d,0x46,0x1d,0x1a,0x8f,0xec,0x29,0x50,0xdc,0x84,0x13,0xe1,0x2b,0x90,
|
||||
0x1c,0x89,0xcd,0x32,0xe9,0x25,0x2a,0xfa,0x26,0x79,0xc9,0xe8,0x5f,0x7c,0xaf,0x7d,
|
||||
0xac,0xc7,0x1a,0x79,0x8f,0xf4,0x8e,0x78,0xe6,0x73,0xb5,0xa9,0x74,0x5d,0x94,0x0,
|
||||
0xfe,0xcf,0xfc,0xed,0x41,0x6d,0xe7,0x50,0xcc,0x1,0x32,0x60,0xe,0x90,0x1,0x73,
|
||||
0x80,0xc,0x98,0x4,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,
|
||||
0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,
|
||||
0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,
|
||||
0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,
|
||||
0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,
|
||||
0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,
|
||||
0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,
|
||||
0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,
|
||||
0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,
|
||||
0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,
|
||||
0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,
|
||||
0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,
|
||||
0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,
|
||||
0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,
|
||||
0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,
|
||||
0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,
|
||||
0x64,0x0,0x90,0x1,0x40,0x6,0x0,0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x0,
|
||||
0x19,0x0,0x64,0x0,0x90,0x1,0x40,0x6,0x80,0x5f,0xe8,0xd3,0xf2,0x69,0xdb,0xd,
|
||||
0xcd,0x15,0x90,0xe9,
|
||||
|
||||
};
|
||||
|
||||
static const unsigned char qt_resource_name[] = {
|
||||
// 4G.zst
|
||||
0x0,0x6,
|
||||
0x3,0x8a,0x61,0xa4,
|
||||
0x0,0x34,
|
||||
0x0,0x47,0x0,0x2e,0x0,0x7a,0x0,0x73,0x0,0x74,
|
||||
|
||||
};
|
||||
|
||||
static const unsigned char qt_resource_struct[] = {
|
||||
// :
|
||||
0x0,0x0,0x0,0x0,0x0,0x2,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,
|
||||
0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,
|
||||
// :/4G.zst
|
||||
0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,
|
||||
0x0,0x0,0x1,0x72,0x1c,0x8d,0x7,0xac,
|
||||
|
||||
};
|
||||
|
||||
#ifdef QT_NAMESPACE
|
||||
# define QT_RCC_PREPEND_NAMESPACE(name) ::QT_NAMESPACE::name
|
||||
# define QT_RCC_MANGLE_NAMESPACE0(x) x
|
||||
# define QT_RCC_MANGLE_NAMESPACE1(a, b) a##_##b
|
||||
# define QT_RCC_MANGLE_NAMESPACE2(a, b) QT_RCC_MANGLE_NAMESPACE1(a,b)
|
||||
# define QT_RCC_MANGLE_NAMESPACE(name) QT_RCC_MANGLE_NAMESPACE2( \
|
||||
QT_RCC_MANGLE_NAMESPACE0(name), QT_RCC_MANGLE_NAMESPACE0(QT_NAMESPACE))
|
||||
#else
|
||||
# define QT_RCC_PREPEND_NAMESPACE(name) name
|
||||
# define QT_RCC_MANGLE_NAMESPACE(name) name
|
||||
#endif
|
||||
|
||||
#ifdef QT_NAMESPACE
|
||||
namespace QT_NAMESPACE {
|
||||
#endif
|
||||
|
||||
bool qRegisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *);
|
||||
bool qUnregisterResourceData(int, const unsigned char *, const unsigned char *, const unsigned char *);
|
||||
|
||||
#if defined(__ELF__) || defined(__APPLE__)
|
||||
static inline unsigned char qResourceFeatureZlib()
|
||||
{
|
||||
extern const unsigned char qt_resourceFeatureZlib;
|
||||
return qt_resourceFeatureZlib;
|
||||
}
|
||||
#else
|
||||
unsigned char qResourceFeatureZlib();
|
||||
#endif
|
||||
|
||||
#ifdef QT_NAMESPACE
|
||||
}
|
||||
#endif
|
||||
|
||||
int QT_RCC_MANGLE_NAMESPACE(qInitResources_zstandard)();
|
||||
int QT_RCC_MANGLE_NAMESPACE(qInitResources_zstandard)()
|
||||
{
|
||||
int version = 3;
|
||||
QT_RCC_PREPEND_NAMESPACE(qRegisterResourceData)
|
||||
(version, qt_resource_struct, qt_resource_name, qt_resource_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int QT_RCC_MANGLE_NAMESPACE(qCleanupResources_zstandard)();
|
||||
int QT_RCC_MANGLE_NAMESPACE(qCleanupResources_zstandard)()
|
||||
{
|
||||
int version = 3;
|
||||
version += QT_RCC_PREPEND_NAMESPACE(qResourceFeatureZlib());
|
||||
QT_RCC_PREPEND_NAMESPACE(qUnregisterResourceData)
|
||||
(version, qt_resource_struct, qt_resource_name, qt_resource_data);
|
||||
return 1;
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct initializer {
|
||||
initializer() { QT_RCC_MANGLE_NAMESPACE(qInitResources_zstandard)(); }
|
||||
~initializer() { QT_RCC_MANGLE_NAMESPACE(qCleanupResources_zstandard)(); }
|
||||
} dummy;
|
||||
}
|
12
tests/auto/network/access/qnetworkreply/echo/CMakeLists.txt
Normal file
12
tests/auto/network/access/qnetworkreply/echo/CMakeLists.txt
Normal file
@ -0,0 +1,12 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## echo Binary:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_executable(echo
|
||||
OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
SOURCES
|
||||
main.cpp
|
||||
)
|
23
tests/auto/network/access/qnetworkreply/echo/main.cpp
Normal file
23
tests/auto/network/access/qnetworkreply/echo/main.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QtCore/QFile>
|
||||
|
||||
int main(int argc, char **)
|
||||
{
|
||||
if (argc < 2) {
|
||||
printf("usage: echo\n");
|
||||
printf("echos all its input to its output.\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
QFile file;
|
||||
file.open(stdin, QFile::ReadWrite);
|
||||
QByteArray data = file.readAll();
|
||||
file.close();
|
||||
|
||||
file.open(stdout, QFile::WriteOnly);
|
||||
file.write(data);
|
||||
file.close();
|
||||
return 0;
|
||||
}
|
1
tests/auto/network/access/qnetworkreply/element.xml
Normal file
1
tests/auto/network/access/qnetworkreply/element.xml
Normal file
@ -0,0 +1 @@
|
||||
<root attr="value" attr2="value2"><person /><fruit /></root>
|
0
tests/auto/network/access/qnetworkreply/empty
Normal file
0
tests/auto/network/access/qnetworkreply/empty
Normal file
BIN
tests/auto/network/access/qnetworkreply/image1.jpg
Normal file
BIN
tests/auto/network/access/qnetworkreply/image1.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 1021 KiB |
BIN
tests/auto/network/access/qnetworkreply/image2.jpg
Normal file
BIN
tests/auto/network/access/qnetworkreply/image2.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 859 KiB |
BIN
tests/auto/network/access/qnetworkreply/image3.jpg
Normal file
BIN
tests/auto/network/access/qnetworkreply/image3.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 867 KiB |
3
tests/auto/network/access/qnetworkreply/index.html
Normal file
3
tests/auto/network/access/qnetworkreply/index.html
Normal file
@ -0,0 +1,3 @@
|
||||
<h1>Welcome to qt-test-server</h1>
|
||||
<img src="gif/fluke.gif" alt="fluke">
|
||||
<p>This is a network test server. It serves as a cacheing ftp and http proxy, transparent http/socks5 proxy, imap, ftp and http server, plus more.</p>
|
283
tests/auto/network/access/qnetworkreply/resource
Normal file
283
tests/auto/network/access/qnetworkreply/resource
Normal file
@ -0,0 +1,283 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Network Working Group L. Masinter
|
||||
Request for Comments: 2397 Xerox Corporation
|
||||
Category: Standards Track August 1998
|
||||
|
||||
|
||||
The "data" URL scheme
|
||||
|
||||
Status of this Memo
|
||||
|
||||
This document specifies an Internet standards track protocol for the
|
||||
Internet community, and requests discussion and suggestions for
|
||||
improvements. Please refer to the current edition of the "Internet
|
||||
Official Protocol Standards" (STD 1) for the standardization state
|
||||
and status of this protocol. Distribution of this memo is unlimited.
|
||||
|
||||
Copyright Notice
|
||||
|
||||
Copyright (C) The Internet Society (1998). All Rights Reserved.
|
||||
|
||||
1. Abstract
|
||||
|
||||
A new URL scheme, "data", is defined. It allows inclusion of small
|
||||
data items as "immediate" data, as if it had been included
|
||||
externally.
|
||||
|
||||
2. Description
|
||||
|
||||
Some applications that use URLs also have a need to embed (small)
|
||||
media type data directly inline. This document defines a new URL
|
||||
scheme that would work like 'immediate addressing'. The URLs are of
|
||||
the form:
|
||||
|
||||
data:[<mediatype>][;base64],<data>
|
||||
|
||||
The <mediatype> is an Internet media type specification (with
|
||||
optional parameters.) The appearance of ";base64" means that the data
|
||||
is encoded as base64. Without ";base64", the data (as a sequence of
|
||||
octets) is represented using ASCII encoding for octets inside the
|
||||
range of safe URL characters and using the standard %xx hex encoding
|
||||
of URLs for octets outside that range. If <mediatype> is omitted, it
|
||||
defaults to text/plain;charset=US-ASCII. As a shorthand,
|
||||
"text/plain" can be omitted but the charset parameter supplied.
|
||||
|
||||
The "data:" URL scheme is only useful for short values. Note that
|
||||
some applications that use URLs may impose a length limit; for
|
||||
example, URLs embedded within <A> anchors in HTML have a length limit
|
||||
determined by the SGML declaration for HTML [RFC1866]. The LITLEN
|
||||
(1024) limits the number of characters which can appear in a single
|
||||
|
||||
|
||||
|
||||
Masinter Standards Track [Page 1]
|
||||
|
||||
RFC 2397 The "data" URL scheme August 1998
|
||||
|
||||
|
||||
attribute value literal, the ATTSPLEN (2100) limits the sum of all
|
||||
lengths of all attribute value specifications which appear in a tag,
|
||||
and the TAGLEN (2100) limits the overall length of a tag.
|
||||
|
||||
The "data" URL scheme has no relative URL forms.
|
||||
|
||||
3. Syntax
|
||||
|
||||
dataurl := "data:" [ mediatype ] [ ";base64" ] "," data
|
||||
mediatype := [ type "/" subtype ] *( ";" parameter )
|
||||
data := *urlchar
|
||||
parameter := attribute "=" value
|
||||
|
||||
where "urlchar" is imported from [RFC2396], and "type", "subtype",
|
||||
"attribute" and "value" are the corresponding tokens from [RFC2045],
|
||||
represented using URL escaped encoding of [RFC2396] as necessary.
|
||||
|
||||
Attribute values in [RFC2045] are allowed to be either represented as
|
||||
tokens or as quoted strings. However, within a "data" URL, the
|
||||
"quoted-string" representation would be awkward, since the quote mark
|
||||
is itself not a valid urlchar. For this reason, parameter values
|
||||
should use the URL Escaped encoding instead of quoted string if the
|
||||
parameter values contain any "tspecial".
|
||||
|
||||
The ";base64" extension is distinguishable from a content-type
|
||||
parameter by the fact that it doesn't have a following "=" sign.
|
||||
|
||||
4. Examples
|
||||
|
||||
A data URL might be used for arbitrary types of data. The URL
|
||||
|
||||
data:,A%20brief%20note
|
||||
|
||||
encodes the text/plain string "A brief note", which might be useful
|
||||
in a footnote link.
|
||||
|
||||
The HTML fragment:
|
||||
|
||||
<IMG
|
||||
SRC="data:image/gif;base64,R0lGODdhMAAwAPAAAAAAAP///ywAAAAAMAAw
|
||||
AAAC8IyPqcvt3wCcDkiLc7C0qwyGHhSWpjQu5yqmCYsapyuvUUlvONmOZtfzgFz
|
||||
ByTB10QgxOR0TqBQejhRNzOfkVJ+5YiUqrXF5Y5lKh/DeuNcP5yLWGsEbtLiOSp
|
||||
a/TPg7JpJHxyendzWTBfX0cxOnKPjgBzi4diinWGdkF8kjdfnycQZXZeYGejmJl
|
||||
ZeGl9i2icVqaNVailT6F5iJ90m6mvuTS4OK05M0vDk0Q4XUtwvKOzrcd3iq9uis
|
||||
F81M1OIcR7lEewwcLp7tuNNkM3uNna3F2JQFo97Vriy/Xl4/f1cf5VWzXyym7PH
|
||||
hhx4dbgYKAAA7"
|
||||
ALT="Larry">
|
||||
|
||||
|
||||
|
||||
|
||||
Masinter Standards Track [Page 2]
|
||||
|
||||
RFC 2397 The "data" URL scheme August 1998
|
||||
|
||||
|
||||
could be used for a small inline image in a HTML document. (The
|
||||
embedded image is probably near the limit of utility. For anything
|
||||
else larger, data URLs are likely to be inappropriate.)
|
||||
|
||||
A data URL scheme's media type specification can include other
|
||||
parameters; for example, one might specify a charset parameter.
|
||||
|
||||
data:text/plain;charset=iso-8859-7,%be%fg%be
|
||||
|
||||
can be used for a short sequence of greek characters.
|
||||
|
||||
Some applications may use the "data" URL scheme in order to provide
|
||||
setup parameters for other kinds of networking applications. For
|
||||
example, one might create a media type
|
||||
application/vnd-xxx-query
|
||||
|
||||
whose content consists of a query string and a database identifier
|
||||
for the "xxx" vendor's databases. A URL of the form:
|
||||
|
||||
data:application/vnd-xxx-
|
||||
query,select_vcount,fcol_from_fieldtable/local
|
||||
|
||||
could then be used in a local application to launch the "helper" for
|
||||
application/vnd-xxx-query and give it the immediate data included.
|
||||
|
||||
5. History
|
||||
|
||||
This idea was originally proposed August 1995. Some versions of the
|
||||
data URL scheme have been used in the definition of VRML, and a
|
||||
version has appeared as part of a proposal for embedded data in HTML.
|
||||
Various changes have been made, based on requests, to elide the media
|
||||
type, pack the indication of the base64 encoding more tightly, and
|
||||
eliminate "quoted printable" as an encoding since it would not easily
|
||||
yield valid URLs without additional %xx encoding, which itself is
|
||||
sufficient. The "data" URL scheme is in use in VRML, new applications
|
||||
of HTML, and various commercial products. It is being used for object
|
||||
parameters in Java and ActiveX applications.
|
||||
|
||||
6. Security
|
||||
|
||||
Interpretation of the data within a "data" URL has the same security
|
||||
considerations as any implementation of the given media type. An
|
||||
application should not interpret the contents of a data URL which is
|
||||
marked with a media type that has been disallowed for processing by
|
||||
the application's configuration.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Masinter Standards Track [Page 3]
|
||||
|
||||
RFC 2397 The "data" URL scheme August 1998
|
||||
|
||||
|
||||
Sites which use firewall proxies to disallow the retrieval of certain
|
||||
media types (such as application script languages or types with known
|
||||
security problems) will find it difficult to screen against the
|
||||
inclusion of such types using the "data" URL scheme. However, they
|
||||
should be aware of the threat and take whatever precautions are
|
||||
considered necessary within their domain.
|
||||
|
||||
The effect of using long "data" URLs in applications is currently
|
||||
unknown; some software packages may exhibit unreasonable behavior
|
||||
when confronted with data that exceeds its allocated buffer size.
|
||||
|
||||
7. References
|
||||
|
||||
[RFC2396] Berners-Lee, T., Fielding, R., and L. Masinter,
|
||||
"Uniform Resource Identifiers (URI): Generic Syntax", RFC
|
||||
2396, August 1998.
|
||||
|
||||
[RFC1866] Berners-Lee, T., and D. Connolly, "Hypertext Markup
|
||||
Language - 2.0.", RFC 1866, November 1995.
|
||||
|
||||
[RFC2045] Freed N., and N. Borenstein., "Multipurpose Internet Mail
|
||||
Extensions (MIME) Part One: Format of Internet Message
|
||||
Bodies", RFC 2045, November 1996.
|
||||
|
||||
Author contact information:
|
||||
|
||||
Larry Masinter
|
||||
Xerox Palo Alto Research Center
|
||||
3333 Coyote Hill Road
|
||||
Palo Alto, CA 94304
|
||||
|
||||
EMail: masinter@parc.xerox.com
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Masinter Standards Track [Page 4]
|
||||
|
||||
RFC 2397 The "data" URL scheme August 1998
|
||||
|
||||
|
||||
Full Copyright Statement
|
||||
|
||||
Copyright (C) The Internet Society (1998). All Rights Reserved.
|
||||
|
||||
This document and translations of it may be copied and furnished to
|
||||
others, and derivative works that comment on or otherwise explain it
|
||||
or assist in its implementation may be prepared, copied, published
|
||||
and distributed, in whole or in part, without restriction of any
|
||||
kind, provided that the above copyright notice and this paragraph are
|
||||
included on all such copies and derivative works. However, this
|
||||
document itself may not be modified in any way, such as by removing
|
||||
the copyright notice or references to the Internet Society or other
|
||||
Internet organizations, except as needed for the purpose of
|
||||
developing Internet standards in which case the procedures for
|
||||
copyrights defined in the Internet Standards process must be
|
||||
followed, or as required to translate it into languages other than
|
||||
English.
|
||||
|
||||
The limited permissions granted above are perpetual and will not be
|
||||
revoked by the Internet Society or its successors or assigns.
|
||||
|
||||
This document and the information contained herein is provided on an
|
||||
"AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
|
||||
TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
|
||||
BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
|
||||
HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Masinter Standards Track [Page 5]
|
||||
|
899
tests/auto/network/access/qnetworkreply/rfc3252.txt
Normal file
899
tests/auto/network/access/qnetworkreply/rfc3252.txt
Normal file
@ -0,0 +1,899 @@
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Network Working Group H. Kennedy
|
||||
Request for Comments: 3252 Mimezine
|
||||
Category: Informational 1 April 2002
|
||||
|
||||
|
||||
Binary Lexical Octet Ad-hoc Transport
|
||||
|
||||
Status of this Memo
|
||||
|
||||
This memo provides information for the Internet community. It does
|
||||
not specify an Internet standard of any kind. Distribution of this
|
||||
memo is unlimited.
|
||||
|
||||
Copyright Notice
|
||||
|
||||
Copyright (C) The Internet Society (2002). All Rights Reserved.
|
||||
|
||||
Abstract
|
||||
|
||||
This document defines a reformulation of IP and two transport layer
|
||||
protocols (TCP and UDP) as XML applications.
|
||||
|
||||
1. Introduction
|
||||
|
||||
1.1. Overview
|
||||
|
||||
This document describes the Binary Lexical Octet Ad-hoc Transport
|
||||
(BLOAT): a reformulation of a widely-deployed network-layer protocol
|
||||
(IP [RFC791]), and two associated transport layer protocols (TCP
|
||||
[RFC793] and UDP [RFC768]) as XML [XML] applications. It also
|
||||
describes methods for transporting BLOAT over Ethernet and IEEE 802
|
||||
networks as well as encapsulating BLOAT in IP for gatewaying BLOAT
|
||||
across the public Internet.
|
||||
|
||||
1.2. Motivation
|
||||
|
||||
The wild popularity of XML as a basis for application-level protocols
|
||||
such as the Blocks Extensible Exchange Protocol [RFC3080], the Simple
|
||||
Object Access Protocol [SOAP], and Jabber [JABBER] prompted
|
||||
investigation into the possibility of extending the use of XML in the
|
||||
protocol stack. Using XML at both the transport and network layer in
|
||||
addition to the application layer would provide for an amazing amount
|
||||
of power and flexibility while removing dependencies on proprietary
|
||||
and hard-to-understand binary protocols. This protocol unification
|
||||
would also allow applications to use a single XML parser for all
|
||||
aspects of their operation, eliminating developer time spent figuring
|
||||
out the intricacies of each new protocol, and moving the hard work of
|
||||
|
||||
|
||||
|
||||
|
||||
Kennedy Informational [Page 1]
|
||||
|
||||
RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
|
||||
|
||||
|
||||
parsing to the XML toolset. The use of XML also mitigates concerns
|
||||
over "network vs. host" byte ordering which is at the root of many
|
||||
network application bugs.
|
||||
|
||||
1.3. Relation to Existing Protocols
|
||||
|
||||
The reformulations specified in this RFC follow as closely as
|
||||
possible the spirit of the RFCs on which they are based, and so MAY
|
||||
contain elements or attributes that would not be needed in a pure
|
||||
reworking (e.g. length attributes, which are implicit in XML.)
|
||||
|
||||
The layering of network and transport protocols are maintained in
|
||||
this RFC despite the optimizations that could be made if the line
|
||||
were somewhat blurred (i.e. merging TCP and IP into a single, larger
|
||||
element in the DTD) in order to foster future use of this protocol as
|
||||
a basis for reformulating other protocols (such as ICMP.)
|
||||
|
||||
Other than the encoding, the behavioral aspects of each of the
|
||||
existing protocols remain unchanged. Routing, address spaces, TCP
|
||||
congestion control, etc. behave as specified in the extant standards.
|
||||
Adapting to new standards and experimental algorithm heuristics for
|
||||
improving performance will become much easier once the move to BLOAT
|
||||
has been completed.
|
||||
|
||||
1.4. Requirement Levels
|
||||
|
||||
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
|
||||
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
|
||||
document are to be interpreted as described in BCP 14, RFC 2119
|
||||
[RFC2119].
|
||||
|
||||
2. IPoXML
|
||||
|
||||
This protocol MUST be implemented to be compliant with this RFC.
|
||||
IPoXML is the root protocol REQUIRED for effective use of TCPoXML
|
||||
(section 3.) and higher-level application protocols.
|
||||
|
||||
The DTD for this document type can be found in section 7.1.
|
||||
|
||||
The routing of IPoXML can be easily implemented on hosts with an XML
|
||||
parser, as the regular structure lends itself handily to parsing and
|
||||
validation of the document/datagram and then processing the
|
||||
destination address, TTL, and checksum before sending it on to its
|
||||
next-hop.
|
||||
|
||||
The reformulation of IPv4 was chosen over IPv6 [RFC2460] due to the
|
||||
wider deployment of IPv4 and the fact that implementing IPv6 as XML
|
||||
would have exceeded the 1500 byte Ethernet MTU.
|
||||
|
||||
|
||||
|
||||
Kennedy Informational [Page 2]
|
||||
|
||||
RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
|
||||
|
||||
|
||||
All BLOAT implementations MUST use - and specify - the UTF-8 encoding
|
||||
of RFC 2279 [RFC2279]. All BLOAT document/datagrams MUST be well-
|
||||
formed and include the XMLDecl.
|
||||
|
||||
2.1. IP Description
|
||||
|
||||
A number of items have changed (for the better) from the original IP
|
||||
specification. Bit-masks, where present have been converted into
|
||||
human-readable values. IP addresses are listed in their dotted-
|
||||
decimal notation [RFC1123]. Length and checksum values are present
|
||||
as decimal integers.
|
||||
|
||||
To calculate the length and checksum fields of the IP element, a
|
||||
canonicalized form of the element MUST be used. The canonical form
|
||||
SHALL have no whitespace (including newline characters) between
|
||||
elements and only one space character between attributes. There
|
||||
SHALL NOT be a space following the last attribute in an element.
|
||||
|
||||
An iterative method SHOULD be used to calculate checksums, as the
|
||||
length field will vary based on the size of the checksum.
|
||||
|
||||
The payload element bears special attention. Due to the character
|
||||
set restrictions of XML, the payload of IP datagrams (which MAY
|
||||
contain arbitrary data) MUST be encoded for transport. This RFC
|
||||
REQUIRES the contents of the payload to be encoded in the base-64
|
||||
encoding of RFC 2045 [RFC2045], but removes the requirement that the
|
||||
encoded output MUST be wrapped on 76-character lines.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Kennedy Informational [Page 3]
|
||||
|
||||
RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
|
||||
|
||||
|
||||
2.2. Example Datagram
|
||||
|
||||
The following is an example IPoXML datagram with an empty payload:
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
|
||||
<ip>
|
||||
<header length="474">
|
||||
<version value="4"/>
|
||||
<tos precedence="Routine" delay="Normal" throughput="Normal"
|
||||
relibility="Normal" reserved="0"/>
|
||||
<total.length value="461"/>
|
||||
<id value="1"/>
|
||||
<flags reserved="0" df="dont" mf="last"/>
|
||||
<offset value="0"/>
|
||||
<ttl value="255"/>
|
||||
<protocol value="6"/>
|
||||
<checksum value="8707"/>
|
||||
<source address="10.0.0.22"/>
|
||||
<destination address="10.0.0.1"/>
|
||||
<options>
|
||||
<end copied="0" class="0" number="0"/>
|
||||
</options>
|
||||
<padding pad="0"/>
|
||||
</header>
|
||||
<payload>
|
||||
</payload>
|
||||
</ip>
|
||||
|
||||
3. TCPoXML
|
||||
|
||||
This protocol MUST be implemented to be compliant with this RFC. The
|
||||
DTD for this document type can be found in section 7.2.
|
||||
|
||||
3.1. TCP Description
|
||||
|
||||
A number of items have changed from the original TCP specification.
|
||||
Bit-masks, where present have been converted into human-readable
|
||||
values. Length and checksum and port values are present as decimal
|
||||
integers.
|
||||
|
||||
To calculate the length and checksum fields of the TCP element, a
|
||||
canonicalized form of the element MUST be used as in section 2.1.
|
||||
|
||||
An iterative method SHOULD be used to calculate checksums as in
|
||||
section 2.1.
|
||||
|
||||
The payload element MUST be encoded as in section 2.1.
|
||||
|
||||
|
||||
|
||||
Kennedy Informational [Page 4]
|
||||
|
||||
RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
|
||||
|
||||
|
||||
The TCP offset element was expanded to a maximum of 255 from 16 to
|
||||
allow for the increased size of the header in XML.
|
||||
|
||||
TCPoXML datagrams encapsulated by IPoXML MAY omit the <?xml?> header
|
||||
as well as the <!DOCTYPE> declaration.
|
||||
|
||||
3.2. Example Datagram
|
||||
|
||||
The following is an example TCPoXML datagram with an empty payload:
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
|
||||
<tcp>
|
||||
<tcp.header>
|
||||
<src port="31415"/>
|
||||
<dest port="42424"/>
|
||||
<sequence number="322622954"/>
|
||||
<acknowledgement number="689715995"/>
|
||||
<offset number=""/>
|
||||
<reserved value="0"/>
|
||||
<control syn="1" ack="1"/>
|
||||
<window size="1"/>
|
||||
<urgent pointer="0"/>
|
||||
<checksum value="2988"/>
|
||||
<tcp.options>
|
||||
<tcp.end kind="0"/>
|
||||
</tcp.options>
|
||||
<padding pad="0"/>
|
||||
</tcp.header>
|
||||
<payload>
|
||||
</payload>
|
||||
</tcp>
|
||||
|
||||
4. UDPoXML
|
||||
|
||||
This protocol MUST be implemented to be compliant with this RFC. The
|
||||
DTD for this document type can be found in section 7.3.
|
||||
|
||||
4.1. UDP Description
|
||||
|
||||
A number of items have changed from the original UDP specification.
|
||||
Bit-masks, where present have been converted into human-readable
|
||||
values. Length and checksum and port values are present as decimal
|
||||
integers.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Kennedy Informational [Page 5]
|
||||
|
||||
RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
|
||||
|
||||
|
||||
To calculate the length and checksum fields of the UDP element, a
|
||||
canonicalized form of the element MUST be used as in section 2.1. An
|
||||
iterative method SHOULD be used to calculate checksums as in section
|
||||
2.1.
|
||||
|
||||
The payload element MUST be encoded as in section 2.1.
|
||||
|
||||
UDPoXML datagrams encapsulated by IPoXML MAY omit the <?xml?> header
|
||||
as well as the <!DOCTYPE> declaration.
|
||||
|
||||
4.2. Example Datagram
|
||||
|
||||
The following is an example UDPoXML datagram with an empty payload:
|
||||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
|
||||
<udp>
|
||||
<udp.header>
|
||||
<src port="31415"/>
|
||||
<dest port="42424"/>
|
||||
<udp.length value="143"/>
|
||||
<checksum value="2988"/>
|
||||
</udp.header>
|
||||
<payload>
|
||||
</payload>
|
||||
</udp>
|
||||
|
||||
5. Network Transport
|
||||
|
||||
This document provides for the transmission of BLOAT datagrams over
|
||||
two common families of physical layer transport. Future RFCs will
|
||||
address additional transports as routing vendors catch up to the
|
||||
specification, and we begin to see BLOAT routed across the Internet
|
||||
backbone.
|
||||
|
||||
5.1. Ethernet
|
||||
|
||||
BLOAT is encapsulated in Ethernet datagrams as in [RFC894] with the
|
||||
exception that the type field of the Ethernet frame MUST contain the
|
||||
value 0xBEEF. The first 5 octets of the Ethernet frame payload will
|
||||
be 0x3c 3f 78 6d 6c ("<?xml".)
|
||||
|
||||
5.2. IEEE 802
|
||||
|
||||
BLOAT is encapsulated in IEEE 802 Networks as in [RFC1042] except
|
||||
that the protocol type code for IPoXML is 0xBEEF.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Kennedy Informational [Page 6]
|
||||
|
||||
RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
|
||||
|
||||
|
||||
6. Gatewaying over IP
|
||||
|
||||
In order to facilitate the gradual introduction of BLOAT into the
|
||||
public Internet, BLOAT MAY be encapsulated in IP as in [RFC2003] to
|
||||
gateway between networks that run BLOAT natively on their LANs.
|
||||
|
||||
7. DTDs
|
||||
|
||||
The Transport DTDs (7.2. and 7.3.) build on the definitions in the
|
||||
Network DTD (7.1.)
|
||||
|
||||
The DTDs are referenced by their PubidLiteral and SystemLiteral (from
|
||||
[XML]) although it is understood that most IPoXML implementations
|
||||
will not need to pull down the DTD, as it will normally be embedded
|
||||
in the implementation, and presents something of a catch-22 if you
|
||||
need to load part of your network protocol over the network.
|
||||
|
||||
7.1. IPoXML DTD
|
||||
|
||||
<!--
|
||||
DTD for IP over XML.
|
||||
Refer to this DTD as:
|
||||
|
||||
<!DOCTYPE ip PUBLIC "-//IETF//DTD BLOAT 1.0 IP//EN" "bloat.dtd">
|
||||
-->
|
||||
<!--
|
||||
DTD data types:
|
||||
|
||||
Digits [0..9]+
|
||||
|
||||
Precedence "NetworkControl | InternetworkControl |
|
||||
CRITIC | FlashOverride | Flash | Immediate |
|
||||
Priority | Routine"
|
||||
|
||||
IP4Addr "dotted-decimal" notation of [RFC1123]
|
||||
|
||||
Class [0..3]
|
||||
|
||||
Sec "Unclassified | Confidential | EFTO | MMMM | PROG |
|
||||
Restricted | Secret | Top Secret | Reserved"
|
||||
|
||||
Compartments [0..65535]
|
||||
|
||||
Handling [0..65535]
|
||||
|
||||
TCC [0..16777216]
|
||||
|
||||
-->
|
||||
|
||||
|
||||
|
||||
Kennedy Informational [Page 7]
|
||||
|
||||
RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
|
||||
|
||||
|
||||
<!ENTITY % Digits "CDATA">
|
||||
<!ENTITY % Precedence "CDATA">
|
||||
<!ENTITY % IP4Addr "CDATA">
|
||||
<!ENTITY % Class "CDATA">
|
||||
<!ENTITY % Sec "CDATA">
|
||||
<!ENTITY % Compartments "CDATA">
|
||||
<!ENTITY % Handling "CDATA">
|
||||
<!ENTITY % TCC "CDATA">
|
||||
|
||||
<!ELEMENT ip (header, payload)>
|
||||
|
||||
<!ELEMENT header (version, tos, total.length, id, flags, offset, ttl,
|
||||
protocol, checksum, source, destination, options,
|
||||
padding)>
|
||||
<!-- length of header in 32-bit words -->
|
||||
<!ATTLIST header
|
||||
length %Digits; #REQUIRED>
|
||||
|
||||
<!ELEMENT version EMPTY>
|
||||
<!-- ip version. SHOULD be "4" -->
|
||||
<!ATTLIST version
|
||||
value %Digits; #REQUIRED>
|
||||
|
||||
<!ELEMENT tos EMPTY>
|
||||
<!ATTLIST tos
|
||||
precedence %Precedence; #REQUIRED
|
||||
delay (normal | low) #REQUIRED
|
||||
throughput (normal | high) #REQUIRED
|
||||
relibility (normal | high) #REQUIRED
|
||||
reserved CDATA #FIXED "0">
|
||||
|
||||
<!ELEMENT total.length EMPTY>
|
||||
<!--
|
||||
total length of datagram (header and payload) in octets, MUST be
|
||||
less than 65,535 (and SHOULD be less than 1024 for IPoXML on local
|
||||
ethernets).
|
||||
-->
|
||||
<!ATTLIST total.length
|
||||
value %Digits; #REQUIRED>
|
||||
|
||||
<!ELEMENT id EMPTY>
|
||||
<!-- 0 <= id <= 65,535 -->
|
||||
<!ATTLIST id
|
||||
value %Digits; #REQUIRED>
|
||||
|
||||
<!ELEMENT flags EMPTY>
|
||||
<!-- df = don't fragment, mf = more fragments -->
|
||||
<!ATTLIST flags
|
||||
|
||||
|
||||
|
||||
Kennedy Informational [Page 8]
|
||||
|
||||
RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
|
||||
|
||||
|
||||
reserved CDATA #FIXED "0"
|
||||
df (may|dont) #REQUIRED
|
||||
mf (last|more) #REQUIRED>
|
||||
|
||||
<!ELEMENT offset EMPTY>
|
||||
<!-- 0 <= offset <= 8192 measured in 8 octet (64-bit) chunks -->
|
||||
<!ATTLIST offset
|
||||
value %Digits; #REQUIRED>
|
||||
|
||||
<!ELEMENT ttl EMPTY>
|
||||
<!-- 0 <= ttl <= 255 -->
|
||||
<!ATTLIST ttl
|
||||
value %Digits; #REQUIRED>
|
||||
|
||||
<!ELEMENT protocol EMPTY>
|
||||
<!-- 0 <= protocol <= 255 (per IANA) -->
|
||||
<!ATTLIST protocol
|
||||
value %Digits; #REQUIRED>
|
||||
|
||||
<!ELEMENT checksum EMPTY>
|
||||
<!-- 0 <= checksum <= 65535 (over header only) -->
|
||||
<!ATTLIST checksum
|
||||
value %Digits; #REQUIRED>
|
||||
|
||||
<!ELEMENT source EMPTY>
|
||||
<!ATTLIST source
|
||||
address %IP4Addr; #REQUIRED>
|
||||
|
||||
<!ELEMENT destination EMPTY>
|
||||
<!ATTLIST destination
|
||||
address %IP4Addr; #REQUIRED>
|
||||
|
||||
<!ELEMENT options ( end | noop | security | loose | strict | record
|
||||
| stream | timestamp )*>
|
||||
|
||||
<!ELEMENT end EMPTY>
|
||||
<!ATTLIST end
|
||||
copied (0|1) #REQUIRED
|
||||
class CDATA #FIXED "0"
|
||||
number CDATA #FIXED "0">
|
||||
|
||||
<!ELEMENT noop EMPTY>
|
||||
<!ATTLIST noop
|
||||
copied (0|1) #REQUIRED
|
||||
class CDATA #FIXED "0"
|
||||
number CDATA #FIXED "1">
|
||||
|
||||
<!ELEMENT security EMPTY>
|
||||
|
||||
|
||||
|
||||
Kennedy Informational [Page 9]
|
||||
|
||||
RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
|
||||
|
||||
|
||||
<!ATTLIST security
|
||||
copied CDATA #FIXED "1"
|
||||
class CDATA #FIXED "0"
|
||||
number CDATA #FIXED "2"
|
||||
length CDATA #FIXED "11"
|
||||
security %Sec; #REQUIRED
|
||||
compartments %Compartments; #REQUIRED
|
||||
handling %Handling; #REQUIRED
|
||||
tcc %TCC; #REQUIRED>
|
||||
<!ELEMENT loose (hop)+>
|
||||
<!ATTLIST loose
|
||||
copied CDATA #FIXED "1"
|
||||
class CDATA #FIXED "0"
|
||||
number CDATA #FIXED "3"
|
||||
length %Digits; #REQUIRED
|
||||
pointer %Digits; #REQUIRED>
|
||||
|
||||
<!ELEMENT hop EMPTY>
|
||||
<!ATTLIST hop
|
||||
address %IP4Addr; #REQUIRED>
|
||||
|
||||
<!ELEMENT strict (hop)+>
|
||||
<!ATTLIST strict
|
||||
copied CDATA #FIXED "1"
|
||||
class CDATA #FIXED "0"
|
||||
number CDATA #FIXED "9"
|
||||
length %Digits; #REQUIRED
|
||||
pointer %Digits; #REQUIRED>
|
||||
|
||||
<!ELEMENT record (hop)+>
|
||||
<!ATTLIST record
|
||||
copied CDATA #FIXED "0"
|
||||
class CDATA #FIXED "0"
|
||||
number CDATA #FIXED "7"
|
||||
length %Digits; #REQUIRED
|
||||
pointer %Digits; #REQUIRED>
|
||||
|
||||
<!ELEMENT stream EMPTY>
|
||||
<!-- 0 <= id <= 65,535 -->
|
||||
<!ATTLIST stream
|
||||
copied CDATA #FIXED "1"
|
||||
class CDATA #FIXED "0"
|
||||
number CDATA #FIXED "8"
|
||||
length CDATA #FIXED "4"
|
||||
id %Digits; #REQUIRED>
|
||||
|
||||
<!ELEMENT timestamp (tstamp)+>
|
||||
<!-- 0 <= oflw <=15 -->
|
||||
|
||||
|
||||
|
||||
Kennedy Informational [Page 10]
|
||||
|
||||
RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
|
||||
|
||||
|
||||
<!ATTLIST timestamp
|
||||
copied CDATA #FIXED "0"
|
||||
class CDATA #FIXED "2"
|
||||
number CDATA #FIXED "4"
|
||||
length %Digits; #REQUIRED
|
||||
pointer %Digits; #REQUIRED
|
||||
oflw %Digits; #REQUIRED
|
||||
flag (0 | 1 | 3) #REQUIRED>
|
||||
|
||||
<!ELEMENT tstamp EMPTY>
|
||||
<!ATTLIST tstamp
|
||||
time %Digits; #REQUIRED
|
||||
address %IP4Addr; #IMPLIED>
|
||||
<!--
|
||||
padding to bring header to 32-bit boundary.
|
||||
pad MUST be "0"*
|
||||
-->
|
||||
<!ELEMENT padding EMPTY>
|
||||
<!ATTLIST padding
|
||||
pad CDATA #REQUIRED>
|
||||
|
||||
<!-- payload MUST be encoded as base-64 [RFC2045], as modified
|
||||
by section 2.1 of this RFC -->
|
||||
<!ELEMENT payload (CDATA)>
|
||||
|
||||
7.2. TCPoXML DTD
|
||||
|
||||
<!--
|
||||
DTD for TCP over XML.
|
||||
Refer to this DTD as:
|
||||
|
||||
<!DOCTYPE tcp PUBLIC "-//IETF//DTD BLOAT 1.0 TCP//EN" "bloat.dtd">
|
||||
-->
|
||||
|
||||
<!-- the pseudoheader is only included for checksum calculations -->
|
||||
<!ELEMENT tcp (tcp.pseudoheader?, tcp.header, payload)>
|
||||
|
||||
<!ELEMENT tcp.header (src, dest, sequence, acknowledgement, offset,
|
||||
reserved, control, window, checksum, urgent,
|
||||
tcp.options, padding)>
|
||||
|
||||
<!ELEMENT src EMPTY>
|
||||
<!-- 0 <= port <= 65,535 -->
|
||||
<!ATTLIST src
|
||||
port %Digits; #REQUIRED>
|
||||
|
||||
<!ELEMENT dest EMPTY>
|
||||
<!-- 0 <= port <= 65,535 -->
|
||||
|
||||
|
||||
|
||||
Kennedy Informational [Page 11]
|
||||
|
||||
RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
|
||||
|
||||
|
||||
<!ATTLIST dest
|
||||
port %Digits; #REQUIRED>
|
||||
|
||||
<!ELEMENT sequence EMPTY>
|
||||
<!-- 0 <= number <= 4294967295 -->
|
||||
<!ATTLIST sequence
|
||||
number %Digits; #REQUIRED>
|
||||
|
||||
<!ELEMENT acknowledgement EMPTY>
|
||||
<!-- 0 <= number <= 4294967295 -->
|
||||
<!ATTLIST acknowledgement
|
||||
number %Digits; #REQUIRED>
|
||||
|
||||
<!ELEMENT offset EMPTY>
|
||||
<!-- 0 <= number <= 255 -->
|
||||
<!ATTLIST offset
|
||||
number %Digits; #REQUIRED>
|
||||
|
||||
<!ELEMENT reserved EMPTY>
|
||||
<!ATTLIST reserved
|
||||
value CDATA #FIXED "0">
|
||||
|
||||
<!ELEMENT control EMPTY>
|
||||
<!ATTLIST control
|
||||
urg (0|1) #IMPLIED
|
||||
ack (0|1) #IMPLIED
|
||||
psh (0|1) #IMPLIED
|
||||
rst (0|1) #IMPLIED
|
||||
syn (0|1) #IMPLIED
|
||||
fin (0|1) #IMPLIED>
|
||||
|
||||
<!ELEMENT window EMPTY>
|
||||
<!-- 0 <= size <= 65,535 -->
|
||||
<!ATTLIST window
|
||||
size %Digits; #REQUIRED>
|
||||
|
||||
<!--
|
||||
checksum as in ip, but with
|
||||
the following pseudo-header added into the tcp element:
|
||||
-->
|
||||
<!ELEMENT tcp.pseudoheader (source, destination, protocol,
|
||||
tcp.length)>
|
||||
|
||||
<!--
|
||||
tcp header + data length in octets. does not include the size of
|
||||
|
||||
the pseudoheader.
|
||||
-->
|
||||
|
||||
|
||||
|
||||
Kennedy Informational [Page 12]
|
||||
|
||||
RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
|
||||
|
||||
|
||||
<!ELEMENT tcp.length EMPTY>
|
||||
<!ATTLIST tcp.length
|
||||
value %Digits; #REQUIRED>
|
||||
|
||||
<!ELEMENT urgent EMPTY>
|
||||
<!-- 0 <= pointer <= 65,535 -->
|
||||
<!ATTLIST urgent
|
||||
pointer %Digits; #REQUIRED>
|
||||
|
||||
<!ELEMENT tcp.options (tcp.end | tcp.noop | tcp.mss)+>
|
||||
|
||||
<!ELEMENT tcp.end EMPTY>
|
||||
<!ATTLIST tcp.end
|
||||
kind CDATA #FIXED "0">
|
||||
|
||||
<!ELEMENT tcp.noop EMPTY>
|
||||
<!ATTLIST tcp.noop
|
||||
kind CDATA #FIXED "1">
|
||||
|
||||
<!ELEMENT tcp.mss EMPTY>
|
||||
<!ATTLIST tcp.mss
|
||||
kind CDATA #FIXED "2"
|
||||
length CDATA #FIXED "4"
|
||||
size %Digits; #REQUIRED>
|
||||
|
||||
7.3. UDPoXML DTD
|
||||
|
||||
<!--
|
||||
DTD for UDP over XML.
|
||||
Refer to this DTD as:
|
||||
|
||||
<!DOCTYPE udp PUBLIC "-//IETF//DTD BLOAT 1.0 UDP//EN" "bloat.dtd">
|
||||
-->
|
||||
|
||||
<!ELEMENT udp (udp.pseudoheader?, udp.header, payload)>
|
||||
|
||||
<!ELEMENT udp.header (src, dest, udp.length, checksum)>
|
||||
|
||||
<!ELEMENT udp.pseudoheader (source, destination, protocol,
|
||||
udp.length)>
|
||||
|
||||
<!--
|
||||
udp header + data length in octets. does not include the size of
|
||||
the pseudoheader.
|
||||
-->
|
||||
<!ELEMENT udp.length EMPTY>
|
||||
<!ATTLIST udp.length
|
||||
value %Digits; #REQUIRED>
|
||||
|
||||
|
||||
|
||||
Kennedy Informational [Page 13]
|
||||
|
||||
RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
|
||||
|
||||
|
||||
8. Security Considerations
|
||||
|
||||
XML, as a subset of SGML, has the same security considerations as
|
||||
specified in SGML Media Types [RFC1874]. Security considerations
|
||||
that apply to IP, TCP and UDP also likely apply to BLOAT as it does
|
||||
not attempt to correct for issues not related to message format.
|
||||
|
||||
9. References
|
||||
|
||||
[JABBER] Miller, J., "Jabber", draft-miller-jabber-00.txt,
|
||||
February 2002. (Work in Progress)
|
||||
|
||||
[RFC768] Postel, J., "User Datagram Protocol", STD 6, RFC 768,
|
||||
August 1980.
|
||||
|
||||
[RFC791] Postel, J., "Internet Protocol", STD 5, RFC 791,
|
||||
September 1981.
|
||||
|
||||
[RFC793] Postel, J., "Transmission Control Protocol", STD 7, RFC
|
||||
793, September 1981.
|
||||
|
||||
[RFC894] Hornig, C., "Standard for the Transmission of IP
|
||||
Datagrams over Ethernet Networks.", RFC 894, April 1984.
|
||||
|
||||
[RFC1042] Postel, J. and J. Reynolds, "Standard for the
|
||||
Transmission of IP Datagrams Over IEEE 802 Networks", STD
|
||||
43, RFC 1042, February 1988.
|
||||
|
||||
[RFC1123] Braden, R., "Requirements for Internet Hosts -
|
||||
Application and Support", RFC 1123, October 1989.
|
||||
|
||||
[RFC1874] Levinson, E., "SGML Media Types", RFC 1874, December
|
||||
1995.
|
||||
|
||||
[RFC2003] Perkins, C., "IP Encapsulation within IP", RFC 2003,
|
||||
October 1996.
|
||||
|
||||
[RFC2045] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
|
||||
Extensions (MIME) Part One: Format of Internet Message
|
||||
Bodies", RFC 2045, November 1996.
|
||||
|
||||
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
|
||||
Requirement Levels", BCP 14, RFC 2119, March 1997.
|
||||
|
||||
[RFC2279] Yergeau, F., "UTF-8, a transformation format of ISO
|
||||
10646", RFC 2279, January 1998.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Kennedy Informational [Page 14]
|
||||
|
||||
RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
|
||||
|
||||
|
||||
[RFC2460] Deering, S. and R. Hinden, "Internet Protocol, Version 6
|
||||
(IPv6) Specification", RFC 2460, December 1998.
|
||||
|
||||
[RFC3080] Rose, M., "The Blocks Extensible Exchange Protocol Core",
|
||||
RFC 3080, March 2001.
|
||||
|
||||
[SOAP] Box, D., Ehnebuske, D., Kakivaya, G., Layman, A.,
|
||||
Mendelsohn, N., Nielsen, H. F., Thatte, S. Winer, D.,
|
||||
"Simple Object Access Protocol (SOAP) 1.1" World Wide Web
|
||||
Consortium Note, May 2000 http://www.w3.org/TR/SOAP/
|
||||
|
||||
[XML] Bray, T., Paoli, J., Sperberg-McQueen, C. M., "Extensible
|
||||
Markup Language (XML)" World Wide Web Consortium
|
||||
Recommendation REC- xml-19980210.
|
||||
http://www.w3.org/TR/1998/REC-xml-19980210
|
||||
|
||||
10. Author's Address
|
||||
|
||||
Hugh Kennedy
|
||||
Mimezine
|
||||
1060 West Addison
|
||||
Chicago, IL 60613
|
||||
USA
|
||||
|
||||
EMail: kennedyh@engin.umich.edu
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Kennedy Informational [Page 15]
|
||||
|
||||
RFC 3252 Binary Lexical Octet Ad-hoc Transport 1 April 2002
|
||||
|
||||
|
||||
11. Full Copyright Statement
|
||||
|
||||
Copyright (C) The Internet Society (2002). All Rights Reserved.
|
||||
|
||||
This document and translations of it may be copied and furnished to
|
||||
others, and derivative works that comment on or otherwise explain it
|
||||
or assist in its implementation may be prepared, copied, published
|
||||
and distributed, in whole or in part, without restriction of any
|
||||
kind, provided that the above copyright notice and this paragraph are
|
||||
included on all such copies and derivative works. However, this
|
||||
document itself may not be modified in any way, such as by removing
|
||||
the copyright notice or references to the Internet Society or other
|
||||
Internet organizations, except as needed for the purpose of
|
||||
developing Internet standards in which case the procedures for
|
||||
copyrights defined in the Internet Standards process must be
|
||||
followed, or as required to translate it into languages other than
|
||||
English.
|
||||
|
||||
The limited permissions granted above are perpetual and will not be
|
||||
revoked by the Internet Society or its successors or assigns.
|
||||
|
||||
This document and the information contained herein is provided on an
|
||||
"AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING
|
||||
TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
|
||||
BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION
|
||||
HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
|
||||
|
||||
Acknowledgement
|
||||
|
||||
Funding for the RFC Editor function is currently provided by the
|
||||
Internet Society.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Kennedy Informational [Page 16]
|
||||
|
1
tests/auto/network/access/qnetworkreply/smb-file.txt
Normal file
1
tests/auto/network/access/qnetworkreply/smb-file.txt
Normal file
@ -0,0 +1 @@
|
||||
This is 34 bytes. Do not change...
|
47
tests/auto/network/access/qnetworkreply/test/CMakeLists.txt
Normal file
47
tests/auto/network/access/qnetworkreply/test/CMakeLists.txt
Normal file
@ -0,0 +1,47 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qnetworkreply Test:
|
||||
#####################################################################
|
||||
|
||||
# Collect test data
|
||||
list(APPEND test_data "../empty")
|
||||
list(APPEND test_data "../rfc3252.txt")
|
||||
list(APPEND test_data "../resource")
|
||||
list(APPEND test_data "../bigfile")
|
||||
file(GLOB_RECURSE test_data_glob
|
||||
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
../*.jpg)
|
||||
list(APPEND test_data ${test_data_glob})
|
||||
list(APPEND test_data "../certs")
|
||||
list(APPEND test_data "../index.html")
|
||||
list(APPEND test_data "../smb-file.txt")
|
||||
|
||||
qt_internal_add_test(tst_qnetworkreply
|
||||
OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../"
|
||||
SOURCES
|
||||
../tst_qnetworkreply.cpp
|
||||
../data/gzip.rcc.cpp
|
||||
../data/zstandard.rcc.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::NetworkPrivate
|
||||
TESTDATA ${test_data}
|
||||
QT_TEST_SERVER_LIST "vsftpd" "apache2" "ftp-proxy" "danted" "squid"
|
||||
)
|
||||
add_dependencies(tst_qnetworkreply echo)
|
||||
|
||||
# Resources:
|
||||
set(qnetworkreply_resource_files
|
||||
"../resource"
|
||||
)
|
||||
|
||||
qt_internal_add_resource(tst_qnetworkreply "qnetworkreply"
|
||||
PREFIX
|
||||
"/"
|
||||
BASE
|
||||
".."
|
||||
FILES
|
||||
${qnetworkreply_resource_files}
|
||||
)
|
@ -0,0 +1,3 @@
|
||||
<h1>Welcome to qt-test-server</h1>
|
||||
<img src="fluke.gif" alt="fluke">
|
||||
<p>This is a network test server. It serves as a caching ftp and http proxy, transparent http/socks5 proxy, imap, ftp and http server, and more.</p>
|
10094
tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
Normal file
10094
tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
Normal file
File diff suppressed because it is too large
Load Diff
13
tests/auto/network/access/qnetworkrequest/CMakeLists.txt
Normal file
13
tests/auto/network/access/qnetworkrequest/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qnetworkrequest Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qnetworkrequest
|
||||
SOURCES
|
||||
tst_qnetworkrequest.cpp
|
||||
LIBRARIES
|
||||
Qt::Network
|
||||
)
|
@ -0,0 +1,556 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include <QtNetwork/QNetworkRequest>
|
||||
#include <QtNetwork/QNetworkCookie>
|
||||
|
||||
#include <QtCore/QDateTime>
|
||||
#include <QtCore/QTimeZone>
|
||||
#include <QtCore/QUrl>
|
||||
|
||||
Q_DECLARE_METATYPE(QNetworkRequest::KnownHeaders)
|
||||
|
||||
class tst_QNetworkRequest: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void ctor_data();
|
||||
void ctor();
|
||||
void implicitDefaultCtor();
|
||||
void setUrl_data();
|
||||
void setUrl();
|
||||
void setRawHeader_data();
|
||||
void setRawHeader();
|
||||
void rawHeaderList_data();
|
||||
void rawHeaderList();
|
||||
void setHeader_data();
|
||||
void setHeader();
|
||||
void rawHeaderParsing_data();
|
||||
void rawHeaderParsing();
|
||||
void originatingObject();
|
||||
|
||||
void removeHeader();
|
||||
};
|
||||
|
||||
void tst_QNetworkRequest::ctor_data()
|
||||
{
|
||||
QTest::addColumn<QUrl>("url");
|
||||
|
||||
QTest::newRow("nothing") << QUrl();
|
||||
QTest::newRow("empty") << QUrl();
|
||||
QTest::newRow("http") << QUrl("http://qt-project.org");
|
||||
}
|
||||
|
||||
void tst_QNetworkRequest::ctor()
|
||||
{
|
||||
QFETCH(QUrl, url);
|
||||
|
||||
if (qstrcmp(QTest::currentDataTag(), "nothing") == 0) {
|
||||
QNetworkRequest request;
|
||||
QCOMPARE(request.url(), url);
|
||||
} else {
|
||||
QNetworkRequest request(url);
|
||||
QCOMPARE(request.url(), url);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QNetworkRequest::implicitDefaultCtor()
|
||||
{
|
||||
QNetworkRequest r = {};
|
||||
Q_UNUSED(r);
|
||||
}
|
||||
|
||||
void tst_QNetworkRequest::setUrl_data()
|
||||
{
|
||||
ctor_data();
|
||||
}
|
||||
|
||||
void tst_QNetworkRequest::setUrl()
|
||||
{
|
||||
QFETCH(QUrl, url);
|
||||
QNetworkRequest request;
|
||||
|
||||
if (qstrcmp(QTest::currentDataTag(), "nothing") != 0)
|
||||
request.setUrl(url);
|
||||
|
||||
QCOMPARE(request.url(), url);
|
||||
}
|
||||
|
||||
void tst_QNetworkRequest::setRawHeader_data()
|
||||
{
|
||||
QTest::addColumn<QByteArray>("header");
|
||||
QTest::addColumn<QByteArray>("value");
|
||||
QTest::addColumn<QByteArray>("headerToGet");
|
||||
QTest::addColumn<QByteArray>("expectedValue");
|
||||
QTest::addColumn<bool>("hasHeader");
|
||||
|
||||
QTest::newRow("null-header") << QByteArray() << QByteArray("abc")
|
||||
<< QByteArray() << QByteArray() << false;
|
||||
QTest::newRow("empty-header") << QByteArray("") << QByteArray("abc")
|
||||
<< QByteArray("") << QByteArray() << false;
|
||||
QTest::newRow("null-value") << QByteArray("foo") << QByteArray()
|
||||
<< QByteArray("foo") << QByteArray() << false;
|
||||
QTest::newRow("empty-value") << QByteArray("foo") << QByteArray("")
|
||||
<< QByteArray("foo") << QByteArray("") << true;
|
||||
QTest::newRow("empty-value-vs-null") << QByteArray("foo") << QByteArray("")
|
||||
<< QByteArray("foo") << QByteArray() << true;
|
||||
|
||||
QTest::newRow("UPPER-UPPER") << QByteArray("FOO") << QByteArray("abc")
|
||||
<< QByteArray("FOO") << QByteArray("abc") << true;
|
||||
QTest::newRow("UPPER-Mixed") << QByteArray("FOO") << QByteArray("abc")
|
||||
<< QByteArray("Foo") << QByteArray("abc") << true;
|
||||
QTest::newRow("UPPER-lower") << QByteArray("FOO") << QByteArray("abc")
|
||||
<< QByteArray("foo") << QByteArray("abc") << true;
|
||||
QTest::newRow("Mixed-UPPER") << QByteArray("Foo") << QByteArray("abc")
|
||||
<< QByteArray("FOO") << QByteArray("abc") << true;
|
||||
QTest::newRow("Mixed-Mixed") << QByteArray("Foo") << QByteArray("abc")
|
||||
<< QByteArray("Foo") << QByteArray("abc") << true;
|
||||
QTest::newRow("Mixed-lower") << QByteArray("Foo") << QByteArray("abc")
|
||||
<< QByteArray("foo") << QByteArray("abc") << true;
|
||||
QTest::newRow("lower-UPPER") << QByteArray("foo") << QByteArray("abc")
|
||||
<< QByteArray("FOO") << QByteArray("abc") << true;
|
||||
QTest::newRow("lower-Mixed") << QByteArray("foo") << QByteArray("abc")
|
||||
<< QByteArray("Foo") << QByteArray("abc") << true;
|
||||
QTest::newRow("lower-lower") << QByteArray("foo") << QByteArray("abc")
|
||||
<< QByteArray("foo") << QByteArray("abc") << true;
|
||||
}
|
||||
|
||||
void tst_QNetworkRequest::setRawHeader()
|
||||
{
|
||||
QFETCH(QByteArray, header);
|
||||
QFETCH(QByteArray, value);
|
||||
QFETCH(QByteArray, headerToGet);
|
||||
QFETCH(QByteArray, expectedValue);
|
||||
QFETCH(bool, hasHeader);
|
||||
|
||||
QNetworkRequest request;
|
||||
request.setRawHeader(header, value);
|
||||
|
||||
QCOMPARE(request.hasRawHeader(headerToGet), hasHeader);
|
||||
QCOMPARE(request.rawHeader(headerToGet), expectedValue);
|
||||
}
|
||||
|
||||
void tst_QNetworkRequest::rawHeaderList_data()
|
||||
{
|
||||
QTest::addColumn<QList<QByteArray> >("set");
|
||||
QTest::addColumn<QList<QByteArray> >("expected");
|
||||
|
||||
QTest::newRow("empty") << QList<QByteArray>() << QList<QByteArray>();
|
||||
|
||||
QList<QByteArray> set;
|
||||
QList<QByteArray> expected;
|
||||
|
||||
set << "foo";
|
||||
expected = set;
|
||||
QTest::newRow("one") << set << expected;
|
||||
|
||||
set << "bar";
|
||||
expected = set;
|
||||
QTest::newRow("two") << set << expected;
|
||||
|
||||
set.clear();
|
||||
expected.clear();
|
||||
set << "foo" << "foo";
|
||||
expected << "foo";
|
||||
QTest::newRow("repeated") << set << expected;
|
||||
|
||||
set.clear();
|
||||
expected.clear();
|
||||
set << "foo" << "bar" << "foo";
|
||||
expected << "bar" << "foo";
|
||||
QTest::newRow("repeated-interleaved") << set << expected;
|
||||
}
|
||||
|
||||
void tst_QNetworkRequest::rawHeaderList()
|
||||
{
|
||||
QFETCH(QList<QByteArray>, set);
|
||||
QFETCH(QList<QByteArray>, expected);
|
||||
|
||||
QNetworkRequest request;
|
||||
foreach (QByteArray header, set)
|
||||
request.setRawHeader(header, "a value");
|
||||
|
||||
QList<QByteArray> got = request.rawHeaderList();
|
||||
QCOMPARE(got.size(), expected.size());
|
||||
for (int i = 0; i < got.size(); ++i)
|
||||
QCOMPARE(got.at(i), expected.at(i));
|
||||
}
|
||||
|
||||
void tst_QNetworkRequest::setHeader_data()
|
||||
{
|
||||
QTest::addColumn<QNetworkRequest::KnownHeaders>("cookedHeader");
|
||||
QTest::addColumn<QVariant>("cookedValue");
|
||||
QTest::addColumn<bool>("success");
|
||||
QTest::addColumn<QString>("rawHeader");
|
||||
QTest::addColumn<QString>("rawValue");
|
||||
|
||||
QTest::newRow("Content-Type-Null") << QNetworkRequest::ContentTypeHeader << QVariant()
|
||||
<< false << "Content-Type" << "";
|
||||
QTest::newRow("Content-Type-String") << QNetworkRequest::ContentTypeHeader << QVariant("text/html")
|
||||
<< true
|
||||
<< "Content-Type" << "text/html";
|
||||
QTest::newRow("Content-Type-ByteArray") << QNetworkRequest::ContentTypeHeader
|
||||
<< QVariant("text/html") << true
|
||||
<< "Content-Type" << "text/html";
|
||||
|
||||
QTest::newRow("Content-Length-Int") << QNetworkRequest::ContentLengthHeader << QVariant(1)
|
||||
<< true << "Content-Length" << "1";
|
||||
QTest::newRow("Content-Length-Int64") << QNetworkRequest::ContentLengthHeader << QVariant(qint64(1))
|
||||
<< true << "Content-Length" << "1";
|
||||
|
||||
QTest::newRow("Location-String") << QNetworkRequest::LocationHeader << QVariant("http://foo/with space")
|
||||
<< true << "Location" << "http://foo/with space";
|
||||
QTest::newRow("Location-ByteArray") << QNetworkRequest::LocationHeader
|
||||
<< QVariant("http://foo/with space")
|
||||
<< true << "Location" << "http://foo/with space";
|
||||
QTest::newRow("Location-Url") << QNetworkRequest::LocationHeader
|
||||
<< QVariant(QUrl("http://foo/with space"))
|
||||
<< true << "Location" << "http://foo/with%20space";
|
||||
|
||||
QTest::newRow("Last-Modified-Date") << QNetworkRequest::LastModifiedHeader
|
||||
<< QVariant(QDate(2007, 11, 01))
|
||||
<< true << "Last-Modified"
|
||||
<< "Thu, 01 Nov 2007 00:00:00 GMT";
|
||||
QTest::newRow("Last-Modified-DateTime-UTC")
|
||||
<< QNetworkRequest::LastModifiedHeader
|
||||
<< QVariant(QDateTime(QDate(2007, 11, 1), QTime(18, 8, 30), QTimeZone::UTC))
|
||||
<< true << "Last-Modified" << "Thu, 01 Nov 2007 18:08:30 GMT";
|
||||
// QTBUG-80666: format dates correctly (as GMT) even if the date passed in isn't in UTC:
|
||||
QTest::newRow("Last-Modified-DateTime-Local")
|
||||
<< QNetworkRequest::LastModifiedHeader
|
||||
<< QVariant(QDateTime(QDate(2007, 11, 1), QTime(18, 8, 30), QTimeZone::UTC).toLocalTime())
|
||||
<< true << "Last-Modified" << "Thu, 01 Nov 2007 18:08:30 GMT";
|
||||
QTest::newRow("Last-Modified-DateTime-Offset")
|
||||
<< QNetworkRequest::LastModifiedHeader
|
||||
<< QVariant(QDateTime(QDate(2007, 11, 1), QTime(18, 8, 30),
|
||||
QTimeZone::UTC).toOffsetFromUtc(3600))
|
||||
<< true << "Last-Modified" << "Thu, 01 Nov 2007 18:08:30 GMT";
|
||||
#if QT_CONFIG(timezone)
|
||||
QTimeZone cet("Europe/Oslo");
|
||||
if (cet.isValid()) {
|
||||
QTest::newRow("Last-Modified-DateTime-CET")
|
||||
<< QNetworkRequest::LastModifiedHeader
|
||||
<< QVariant(QDateTime(QDate(2007, 11, 1), QTime(18, 8, 30),
|
||||
QTimeZone::UTC).toTimeZone(cet))
|
||||
<< true << "Last-Modified" << "Thu, 01 Nov 2007 18:08:30 GMT";
|
||||
}
|
||||
#endif
|
||||
|
||||
QTest::newRow("If-Modified-Since-Date") << QNetworkRequest::IfModifiedSinceHeader
|
||||
<< QVariant(QDate(2017, 7, 01))
|
||||
<< true << "If-Modified-Since"
|
||||
<< "Sat, 01 Jul 2017 00:00:00 GMT";
|
||||
QTest::newRow("If-Modified-Since-DateTime") << QNetworkRequest::IfModifiedSinceHeader
|
||||
<< QVariant(QDateTime(QDate(2017, 7, 01),
|
||||
QTime(3, 14, 15),
|
||||
QTimeZone::UTC))
|
||||
<< true << "If-Modified-Since"
|
||||
<< "Sat, 01 Jul 2017 03:14:15 GMT";
|
||||
|
||||
QTest::newRow("Etag-strong") << QNetworkRequest::ETagHeader << QVariant(R"("xyzzy")")
|
||||
<< true << "ETag" << R"("xyzzy")";
|
||||
QTest::newRow("Etag-weak") << QNetworkRequest::ETagHeader << QVariant(R"(W/"xyzzy")")
|
||||
<< true << "ETag" << R"(W/"xyzzy")";
|
||||
QTest::newRow("Etag-empty") << QNetworkRequest::ETagHeader << QVariant(R"("")")
|
||||
<< true << "ETag" << R"("")";
|
||||
|
||||
QTest::newRow("If-Match-empty") << QNetworkRequest::IfMatchHeader << QVariant(R"("")")
|
||||
<< true << "If-Match" << R"("")";
|
||||
QTest::newRow("If-Match-any") << QNetworkRequest::IfMatchHeader << QVariant(R"("*")")
|
||||
<< true << "If-Match" << R"("*")";
|
||||
QTest::newRow("If-Match-single") << QNetworkRequest::IfMatchHeader << QVariant(R"("xyzzy")")
|
||||
<< true << "If-Match" << R"("xyzzy")";
|
||||
QTest::newRow("If-Match-multiple") << QNetworkRequest::IfMatchHeader
|
||||
<< QVariant(R"("xyzzy", "r2d2xxxx", "c3piozzzz")")
|
||||
<< true << "If-Match"
|
||||
<< R"("xyzzy", "r2d2xxxx", "c3piozzzz")";
|
||||
|
||||
QTest::newRow("If-None-Match-empty") << QNetworkRequest::IfNoneMatchHeader << QVariant(R"("")")
|
||||
<< true << "If-None-Match" << R"("")";
|
||||
QTest::newRow("If-None-Match-any") << QNetworkRequest::IfNoneMatchHeader << QVariant(R"("*")")
|
||||
<< true << "If-None-Match" << R"("*")";
|
||||
QTest::newRow("If-None-Match-single") << QNetworkRequest::IfNoneMatchHeader << QVariant(R"("xyzzy")")
|
||||
<< true << "If-None-Match" << R"("xyzzy")";
|
||||
QTest::newRow("If-None-Match-multiple") << QNetworkRequest::IfNoneMatchHeader
|
||||
<< QVariant(R"("xyzzy", W/"r2d2xxxx", "c3piozzzz")")
|
||||
<< true << "If-None-Match"
|
||||
<< R"("xyzzy", W/"r2d2xxxx", "c3piozzzz")";
|
||||
|
||||
QNetworkCookie cookie;
|
||||
cookie.setName("a");
|
||||
cookie.setValue("b");
|
||||
QTest::newRow("Cookie-1") << QNetworkRequest::CookieHeader
|
||||
<< QVariant::fromValue(QList<QNetworkCookie>() << cookie)
|
||||
<< true << "Cookie"
|
||||
<< "a=b";
|
||||
QTest::newRow("SetCookie-1") << QNetworkRequest::SetCookieHeader
|
||||
<< QVariant::fromValue(QList<QNetworkCookie>() << cookie)
|
||||
<< true << "Set-Cookie"
|
||||
<< "a=b";
|
||||
|
||||
cookie.setPath("/");
|
||||
QTest::newRow("Cookie-2") << QNetworkRequest::CookieHeader
|
||||
<< QVariant::fromValue(QList<QNetworkCookie>() << cookie)
|
||||
<< true << "Cookie"
|
||||
<< "a=b";
|
||||
QTest::newRow("SetCookie-2") << QNetworkRequest::SetCookieHeader
|
||||
<< QVariant::fromValue(QList<QNetworkCookie>() << cookie)
|
||||
<< true << "Set-Cookie"
|
||||
<< "a=b; path=/";
|
||||
|
||||
QNetworkCookie cookie2;
|
||||
cookie2.setName("c");
|
||||
cookie2.setValue("d");
|
||||
QTest::newRow("Cookie-3") << QNetworkRequest::CookieHeader
|
||||
<< QVariant::fromValue(QList<QNetworkCookie>() << cookie << cookie2)
|
||||
<< true << "Cookie"
|
||||
<< "a=b; c=d";
|
||||
QTest::newRow("SetCookie-3") << QNetworkRequest::SetCookieHeader
|
||||
<< QVariant::fromValue(QList<QNetworkCookie>() << cookie << cookie2)
|
||||
<< true << "Set-Cookie"
|
||||
<< "a=b; path=/, c=d";
|
||||
}
|
||||
|
||||
void tst_QNetworkRequest::setHeader()
|
||||
{
|
||||
QFETCH(QNetworkRequest::KnownHeaders, cookedHeader);
|
||||
QFETCH(QVariant, cookedValue);
|
||||
QFETCH(bool, success);
|
||||
QFETCH(QString, rawHeader);
|
||||
QFETCH(QString, rawValue);
|
||||
|
||||
QNetworkRequest request;
|
||||
request.setHeader(cookedHeader, cookedValue);
|
||||
|
||||
QCOMPARE(request.header(cookedHeader).isNull(), !success);
|
||||
QCOMPARE(request.hasRawHeader(rawHeader.toLatin1()), success);
|
||||
QCOMPARE(request.rawHeader(rawHeader.toLatin1()).isEmpty(), !success);
|
||||
|
||||
if (success) {
|
||||
QCOMPARE(request.header(cookedHeader), cookedValue);
|
||||
QCOMPARE(QString(request.rawHeader(rawHeader.toLatin1())), rawValue);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QNetworkRequest::rawHeaderParsing_data()
|
||||
{
|
||||
QTest::addColumn<QNetworkRequest::KnownHeaders>("cookedHeader");
|
||||
QTest::addColumn<QVariant>("cookedValue");
|
||||
QTest::addColumn<bool>("success");
|
||||
QTest::addColumn<QString>("rawHeader");
|
||||
QTest::addColumn<QString>("rawValue");
|
||||
|
||||
QTest::newRow("Content-Type") << QNetworkRequest::ContentTypeHeader << QVariant("text/html")
|
||||
<< true
|
||||
<< "Content-Type" << "text/html";
|
||||
QTest::newRow("Content-Length") << QNetworkRequest::ContentLengthHeader << QVariant(qint64(1))
|
||||
<< true << "Content-Length" << " 1 ";
|
||||
QTest::newRow("Location") << QNetworkRequest::LocationHeader
|
||||
<< QVariant(QUrl("http://foo/with space"))
|
||||
<< true << "Location" << "http://foo/with%20space";
|
||||
QTest::newRow("Last-Modified-RFC1123") << QNetworkRequest::LastModifiedHeader
|
||||
<< QVariant(QDateTime(QDate(1994, 11, 06),
|
||||
QTime(8, 49, 37),
|
||||
QTimeZone::UTC))
|
||||
<< true << "Last-Modified"
|
||||
<< "Sun, 06 Nov 1994 08:49:37 GMT";
|
||||
QTest::newRow("Last-Modified-RFC850") << QNetworkRequest::LastModifiedHeader
|
||||
<< QVariant(QDateTime(QDate(1994, 11, 06),
|
||||
QTime(8, 49, 37),
|
||||
QTimeZone::UTC))
|
||||
<< true << "Last-Modified"
|
||||
<< "Sunday, 06-Nov-94 08:49:37 GMT";
|
||||
QTest::newRow("Last-Modified-asctime") << QNetworkRequest::LastModifiedHeader
|
||||
<< QVariant(QDateTime(QDate(1994, 11, 06),
|
||||
QTime(8, 49, 37),
|
||||
QTimeZone::UTC))
|
||||
<< true << "Last-Modified"
|
||||
<< "Sun Nov 6 08:49:37 1994";
|
||||
|
||||
QTest::newRow("If-Modified-Since-RFC1123") << QNetworkRequest::IfModifiedSinceHeader
|
||||
<< QVariant(QDateTime(QDate(1994, 8, 06),
|
||||
QTime(8, 49, 37),
|
||||
QTimeZone::UTC))
|
||||
<< true << "If-Modified-Since"
|
||||
<< "Sun, 06 Aug 1994 08:49:37 GMT";
|
||||
QTest::newRow("If-Modified-Since-RFC850") << QNetworkRequest::IfModifiedSinceHeader
|
||||
<< QVariant(QDateTime(QDate(1994, 8, 06),
|
||||
QTime(8, 49, 37),
|
||||
QTimeZone::UTC))
|
||||
<< true << "If-Modified-Since"
|
||||
<< "Sunday, 06-Aug-94 08:49:37 GMT";
|
||||
QTest::newRow("If-Modified-Since-asctime") << QNetworkRequest::IfModifiedSinceHeader
|
||||
<< QVariant(QDateTime(QDate(1994, 8, 06),
|
||||
QTime(8, 49, 37),
|
||||
QTimeZone::UTC))
|
||||
<< true << "If-Modified-Since"
|
||||
<< "Sun Aug 6 08:49:37 1994";
|
||||
|
||||
QTest::newRow("Etag-strong") << QNetworkRequest::ETagHeader << QVariant(R"("xyzzy")")
|
||||
<< true << "ETag" << R"("xyzzy")";
|
||||
QTest::newRow("Etag-weak") << QNetworkRequest::ETagHeader << QVariant(R"(W/"xyzzy")")
|
||||
<< true << "ETag" << R"(W/"xyzzy")";
|
||||
QTest::newRow("Etag-empty") << QNetworkRequest::ETagHeader << QVariant(R"("")")
|
||||
<< true << "ETag" << R"("")";
|
||||
|
||||
QTest::newRow("If-Match-empty") << QNetworkRequest::IfMatchHeader << QVariant(QStringList(R"("")"))
|
||||
<< true << "If-Match" << R"("")";
|
||||
QTest::newRow("If-Match-any") << QNetworkRequest::IfMatchHeader << QVariant(QStringList(R"("*")"))
|
||||
<< true << "If-Match" << R"("*")";
|
||||
QTest::newRow("If-Match-single") << QNetworkRequest::IfMatchHeader
|
||||
<< QVariant(QStringList(R"("xyzzy")"))
|
||||
<< true << "If-Match" << R"("xyzzy")";
|
||||
QTest::newRow("If-Match-multiple") << QNetworkRequest::IfMatchHeader
|
||||
<< QVariant(QStringList({R"("xyzzy")",
|
||||
R"("r2d2xxxx")",
|
||||
R"("c3piozzzz")"}))
|
||||
<< true << "If-Match"
|
||||
<< R"("xyzzy", "r2d2xxxx", "c3piozzzz")";
|
||||
|
||||
QTest::newRow("If-None-Match-empty") << QNetworkRequest::IfNoneMatchHeader
|
||||
<< QVariant(QStringList(R"("")"))
|
||||
<< true << "If-None-Match" << R"("")";
|
||||
QTest::newRow("If-None-Match-any") << QNetworkRequest::IfNoneMatchHeader
|
||||
<< QVariant(QStringList(R"("*")"))
|
||||
<< true << "If-None-Match" << R"("*")";
|
||||
QTest::newRow("If-None-Match-single") << QNetworkRequest::IfNoneMatchHeader
|
||||
<< QVariant(QStringList(R"("xyzzy")"))
|
||||
<< true << "If-None-Match" << R"("xyzzy")";
|
||||
QTest::newRow("If-None-Match-multiple") << QNetworkRequest::IfNoneMatchHeader
|
||||
<< QVariant(QStringList({R"("xyzzy")",
|
||||
R"(W/"r2d2xxxx")",
|
||||
R"("c3piozzzz")"}))
|
||||
<< true << "If-None-Match"
|
||||
<< R"("xyzzy", W/"r2d2xxxx", "c3piozzzz")";
|
||||
|
||||
QTest::newRow("Content-Length-invalid1") << QNetworkRequest::ContentLengthHeader << QVariant()
|
||||
<< false << "Content-Length" << "1a";
|
||||
QTest::newRow("Content-Length-invalid2") << QNetworkRequest::ContentLengthHeader << QVariant()
|
||||
<< false << "Content-Length" << "a";
|
||||
|
||||
|
||||
QTest::newRow("Location-invalid1") << QNetworkRequest::LocationHeader << QVariant() << false
|
||||
<< "Location" << "abc";
|
||||
QTest::newRow("Location-invalid2") << QNetworkRequest::LocationHeader << QVariant() << false
|
||||
<< "Location" << "1http://foo";
|
||||
QTest::newRow("Location-invalid3") << QNetworkRequest::LocationHeader << QVariant() << false
|
||||
<< "Location" << "http://foo/%gg";
|
||||
|
||||
// don't test for invalid dates because we may want to support broken servers in the future
|
||||
|
||||
QNetworkCookie cookie;
|
||||
cookie.setName("a");
|
||||
cookie.setValue("b");
|
||||
QTest::newRow("Cookie-1") << QNetworkRequest::CookieHeader
|
||||
<< QVariant::fromValue(QList<QNetworkCookie>() << cookie)
|
||||
<< true << "Cookie"
|
||||
<< "a=b";
|
||||
QTest::newRow("SetCookie-1") << QNetworkRequest::SetCookieHeader
|
||||
<< QVariant::fromValue(QList<QNetworkCookie>() << cookie)
|
||||
<< true << "Set-Cookie"
|
||||
<< "a=b";
|
||||
|
||||
cookie.setPath("/");
|
||||
QTest::newRow("SetCookie-2") << QNetworkRequest::SetCookieHeader
|
||||
<< QVariant::fromValue(QList<QNetworkCookie>() << cookie)
|
||||
<< true << "Set-Cookie"
|
||||
<< "a=b; path=/";
|
||||
|
||||
QNetworkCookie cookie2;
|
||||
cookie.setPath("");
|
||||
cookie2.setName("c");
|
||||
cookie2.setValue("d");
|
||||
QTest::newRow("Cookie-3") << QNetworkRequest::CookieHeader
|
||||
<< QVariant::fromValue(QList<QNetworkCookie>() << cookie << cookie2)
|
||||
<< true << "Cookie"
|
||||
<< "a=b; c=d";
|
||||
cookie.setPath("/");
|
||||
QTest::newRow("SetCookie-3") << QNetworkRequest::SetCookieHeader
|
||||
<< QVariant::fromValue(QList<QNetworkCookie>() << cookie << cookie2)
|
||||
<< true << "Set-Cookie"
|
||||
<< "a=b; path=/\nc=d";
|
||||
QTest::newRow("Content-Disposition") << QNetworkRequest::ContentDispositionHeader
|
||||
<< QVariant("attachment; filename=\"test.txt\"") << true
|
||||
<< "Content-Disposition" << "attachment; filename=\"test.txt\"";
|
||||
}
|
||||
|
||||
void tst_QNetworkRequest::rawHeaderParsing()
|
||||
{
|
||||
QFETCH(QNetworkRequest::KnownHeaders, cookedHeader);
|
||||
QFETCH(QVariant, cookedValue);
|
||||
QFETCH(bool, success);
|
||||
QFETCH(QString, rawHeader);
|
||||
QFETCH(QString, rawValue);
|
||||
|
||||
QNetworkRequest request;
|
||||
request.setRawHeader(rawHeader.toLatin1(), rawValue.toLatin1());
|
||||
|
||||
// even if it doesn't parse, it's as a raw header
|
||||
QVERIFY(request.hasRawHeader(rawHeader.toLatin1()));
|
||||
QVERIFY(request.hasRawHeader(rawHeader.toLower().toLatin1()));
|
||||
QCOMPARE(QString(request.rawHeader(rawHeader.toLatin1())), rawValue);
|
||||
|
||||
QCOMPARE(request.header(cookedHeader).isNull(), !success);
|
||||
if (cookedValue.metaType().id() < QMetaType::User)
|
||||
QCOMPARE(request.header(cookedHeader), cookedValue);
|
||||
else if (cookedValue.userType() == qMetaTypeId<QList<QNetworkCookie> >())
|
||||
QCOMPARE(qvariant_cast<QList<QNetworkCookie> >(request.header(cookedHeader)),
|
||||
qvariant_cast<QList<QNetworkCookie> >(cookedValue));
|
||||
}
|
||||
|
||||
void tst_QNetworkRequest::removeHeader()
|
||||
{
|
||||
QNetworkRequest request;
|
||||
|
||||
request.setRawHeader("Foo", "1");
|
||||
QVERIFY(request.hasRawHeader("Foo"));
|
||||
QVERIFY(request.hasRawHeader("foo"));
|
||||
request.setRawHeader("Foo", QByteArray());
|
||||
QVERIFY(!request.hasRawHeader("Foo"));
|
||||
|
||||
// same, but remove with different capitalisation
|
||||
request.setRawHeader("Foo", "1");
|
||||
QVERIFY(request.hasRawHeader("Foo"));
|
||||
QVERIFY(request.hasRawHeader("foo"));
|
||||
request.setRawHeader("foo", QByteArray());
|
||||
QVERIFY(!request.hasRawHeader("Foo"));
|
||||
|
||||
// same, but not the first
|
||||
request.setRawHeader("Bar", "2");
|
||||
request.setRawHeader("Foo", "1");
|
||||
QVERIFY(request.hasRawHeader("Foo"));
|
||||
QVERIFY(request.hasRawHeader("foo"));
|
||||
request.setRawHeader("foo", QByteArray());
|
||||
QVERIFY(!request.hasRawHeader("Foo"));
|
||||
QVERIFY(request.hasRawHeader("bar"));
|
||||
|
||||
// same, but not the first nor last
|
||||
request.setRawHeader("Foo", "1");
|
||||
request.setRawHeader("Bar", "3");
|
||||
QVERIFY(request.hasRawHeader("Foo"));
|
||||
QVERIFY(request.hasRawHeader("foo"));
|
||||
request.setRawHeader("foo", QByteArray());
|
||||
QVERIFY(!request.hasRawHeader("Foo"));
|
||||
QVERIFY(request.hasRawHeader("bar"));
|
||||
}
|
||||
|
||||
void tst_QNetworkRequest::originatingObject()
|
||||
{
|
||||
QNetworkRequest request;
|
||||
|
||||
QVERIFY(!request.originatingObject());
|
||||
|
||||
{
|
||||
QObject dummy;
|
||||
request.setOriginatingObject(&dummy);
|
||||
QCOMPARE(request.originatingObject(), &dummy);
|
||||
}
|
||||
|
||||
QVERIFY(!request.originatingObject());
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QNetworkRequest)
|
||||
#include "tst_qnetworkrequest.moc"
|
23
tests/auto/network/kernel/CMakeLists.txt
Normal file
23
tests/auto/network/kernel/CMakeLists.txt
Normal file
@ -0,0 +1,23 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
if(NOT INTEGRITY)
|
||||
add_subdirectory(qdnslookup)
|
||||
add_subdirectory(qdnslookup_appless)
|
||||
endif()
|
||||
if(QT_FEATURE_networkinterface)
|
||||
add_subdirectory(qnetworkproxyfactory)
|
||||
add_subdirectory(qnetworkinterface)
|
||||
endif()
|
||||
add_subdirectory(qnetworkproxy)
|
||||
add_subdirectory(qnetworkdatagram)
|
||||
add_subdirectory(qnetworkaddressentry)
|
||||
add_subdirectory(qhostaddress)
|
||||
if(QT_FEATURE_private_tests AND NOT MACOS AND NOT INTEGRITY)
|
||||
add_subdirectory(qhostinfo)
|
||||
endif()
|
||||
if(QT_FEATURE_private_tests)
|
||||
add_subdirectory(qauthenticator)
|
||||
add_subdirectory(qnetworkinformation)
|
||||
add_subdirectory(qnetworkinformation_appless)
|
||||
endif()
|
17
tests/auto/network/kernel/qauthenticator/CMakeLists.txt
Normal file
17
tests/auto/network/kernel/qauthenticator/CMakeLists.txt
Normal file
@ -0,0 +1,17 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
if(NOT QT_FEATURE_private_tests)
|
||||
return()
|
||||
endif()
|
||||
|
||||
#####################################################################
|
||||
## tst_qauthenticator Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qauthenticator
|
||||
SOURCES
|
||||
tst_qauthenticator.cpp
|
||||
LIBRARIES
|
||||
Qt::NetworkPrivate
|
||||
)
|
192
tests/auto/network/kernel/qauthenticator/tst_qauthenticator.cpp
Normal file
192
tests/auto/network/kernel/qauthenticator/tst_qauthenticator.cpp
Normal file
@ -0,0 +1,192 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
|
||||
#include <QtCore/QString>
|
||||
#include <QTest>
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtNetwork/QAuthenticator>
|
||||
|
||||
#include <private/qauthenticator_p.h>
|
||||
|
||||
class tst_QAuthenticator : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
tst_QAuthenticator();
|
||||
|
||||
private Q_SLOTS:
|
||||
void basicAuth();
|
||||
void basicAuth_data();
|
||||
|
||||
void ntlmAuth_data();
|
||||
void ntlmAuth();
|
||||
|
||||
void sha256AndMd5Digest();
|
||||
|
||||
void equalityOperators();
|
||||
|
||||
void isMethodSupported();
|
||||
};
|
||||
|
||||
tst_QAuthenticator::tst_QAuthenticator()
|
||||
{
|
||||
}
|
||||
|
||||
void tst_QAuthenticator::basicAuth_data()
|
||||
{
|
||||
QTest::addColumn<QString>("data");
|
||||
QTest::addColumn<QString>("realm");
|
||||
QTest::addColumn<QString>("user");
|
||||
QTest::addColumn<QString>("password");
|
||||
QTest::addColumn<QByteArray>("expectedReply");
|
||||
|
||||
QTest::newRow("just-user") << "" << "" << "foo" << "" << QByteArray("foo:").toBase64();
|
||||
QTest::newRow("user-password") << "" << "" << "foo" << "bar" << QByteArray("foo:bar").toBase64();
|
||||
QTest::newRow("user-password-realm") << "realm=\"secure area\"" << "secure area" << "foo" << "bar" << QByteArray("foo:bar").toBase64();
|
||||
}
|
||||
|
||||
void tst_QAuthenticator::basicAuth()
|
||||
{
|
||||
QFETCH(QString, data);
|
||||
QFETCH(QString, realm);
|
||||
QFETCH(QString, user);
|
||||
QFETCH(QString, password);
|
||||
QFETCH(QByteArray, expectedReply);
|
||||
|
||||
QAuthenticator auth;
|
||||
auth.detach();
|
||||
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(auth);
|
||||
QCOMPARE(priv->phase, QAuthenticatorPrivate::Start);
|
||||
|
||||
QList<QPair<QByteArray, QByteArray> > headers;
|
||||
headers << qMakePair(QByteArray("WWW-Authenticate"), "Basic " + data.toUtf8());
|
||||
priv->parseHttpResponse(headers, /*isProxy = */ false, {});
|
||||
|
||||
QCOMPARE(auth.realm(), realm);
|
||||
QCOMPARE(auth.option("realm").toString(), realm);
|
||||
|
||||
auth.setUser(user);
|
||||
auth.setPassword(password);
|
||||
|
||||
QCOMPARE(priv->phase, QAuthenticatorPrivate::Start);
|
||||
|
||||
QCOMPARE(priv->calculateResponse("GET", "/", u"").constData(), QByteArray("Basic " + expectedReply).constData());
|
||||
}
|
||||
|
||||
void tst_QAuthenticator::ntlmAuth_data()
|
||||
{
|
||||
QTest::addColumn<QString>("data");
|
||||
QTest::addColumn<QString>("realm");
|
||||
QTest::addColumn<bool>("sso");
|
||||
|
||||
QTest::newRow("no-realm") << "TlRMTVNTUAACAAAAHAAcADAAAAAFAoEATFZ3OLRQADIAAAAAAAAAAJYAlgBMAAAAUQBUAC0AVABFAFMAVAAtAEQATwBNAEEASQBOAAIAHABRAFQALQBUAEUAUwBUAC0ARABPAE0AQQBJAE4AAQAcAFEAVAAtAFQARQBTAFQALQBTAEUAUgBWAEUAUgAEABYAcQB0AC0AdABlAHMAdAAtAG4AZQB0AAMANABxAHQALQB0AGUAcwB0AC0AcwBlAHIAdgBlAHIALgBxAHQALQB0AGUAcwB0AC0AbgBlAHQAAAAAAA==" << "" << false;
|
||||
QTest::newRow("with-realm") << "TlRMTVNTUAACAAAADAAMADgAAAAFAoECWCZkccFFAzwAAAAAAAAAAL4AvgBEAAAABQLODgAAAA9NAEcARABOAE8ASwACAAwATQBHAEQATgBPAEsAAQAcAE4ATwBLAC0AQQBNAFMAUwBTAEYARQAtADAAMQAEACAAbQBnAGQAbgBvAGsALgBuAG8AawBpAGEALgBjAG8AbQADAD4AbgBvAGsALQBhAG0AcwBzAHMAZgBlAC0AMAAxAC4AbQBnAGQAbgBvAGsALgBuAG8AawBpAGEALgBjAG8AbQAFACAAbQBnAGQAbgBvAGsALgBuAG8AawBpAGEALgBjAG8AbQAAAAAA" << "NOE" << false;
|
||||
QTest::newRow("no-realm-sso") << "TlRMTVNTUAACAAAAHAAcADAAAAAFAoEATFZ3OLRQADIAAAAAAAAAAJYAlgBMAAAAUQBUAC0AVABFAFMAVAAtAEQATwBNAEEASQBOAAIAHABRAFQALQBUAEUAUwBUAC0ARABPAE0AQQBJAE4AAQAcAFEAVAAtAFQARQBTAFQALQBTAEUAUgBWAEUAUgAEABYAcQB0AC0AdABlAHMAdAAtAG4AZQB0AAMANABxAHQALQB0AGUAcwB0AC0AcwBlAHIAdgBlAHIALgBxAHQALQB0AGUAcwB0AC0AbgBlAHQAAAAAAA==" << "" << true;
|
||||
QTest::newRow("with-realm-sso") << "TlRMTVNTUAACAAAADAAMADgAAAAFAoECWCZkccFFAzwAAAAAAAAAAL4AvgBEAAAABQLODgAAAA9NAEcARABOAE8ASwACAAwATQBHAEQATgBPAEsAAQAcAE4ATwBLAC0AQQBNAFMAUwBTAEYARQAtADAAMQAEACAAbQBnAGQAbgBvAGsALgBuAG8AawBpAGEALgBjAG8AbQADAD4AbgBvAGsALQBhAG0AcwBzAHMAZgBlAC0AMAAxAC4AbQBnAGQAbgBvAGsALgBuAG8AawBpAGEALgBjAG8AbQAFACAAbQBnAGQAbgBvAGsALgBuAG8AawBpAGEALgBjAG8AbQAAAAAA" << "NOE" << true;
|
||||
}
|
||||
|
||||
void tst_QAuthenticator::ntlmAuth()
|
||||
{
|
||||
QFETCH(QString, data);
|
||||
QFETCH(QString, realm);
|
||||
QFETCH(bool, sso);
|
||||
|
||||
QAuthenticator auth;
|
||||
if (!sso) {
|
||||
auth.setUser("unimportant");
|
||||
auth.setPassword("unimportant");
|
||||
}
|
||||
|
||||
auth.detach();
|
||||
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(auth);
|
||||
QCOMPARE(priv->phase, QAuthenticatorPrivate::Start);
|
||||
|
||||
QList<QPair<QByteArray, QByteArray> > headers;
|
||||
|
||||
// NTLM phase 1: negotiate
|
||||
// This phase of NTLM contains no information, other than what we're willing to negotiate
|
||||
// Current implementation uses flags:
|
||||
// NTLMSSP_NEGOTIATE_UNICODE | NTLMSSP_NEGOTIATE_NTLM | NTLMSSP_REQUEST_TARGET
|
||||
headers << qMakePair(QByteArrayLiteral("WWW-Authenticate"), QByteArrayLiteral("NTLM"));
|
||||
priv->parseHttpResponse(headers, /*isProxy = */ false, {});
|
||||
if (sso)
|
||||
QVERIFY(priv->calculateResponse("GET", "/", u"").startsWith("NTLM "));
|
||||
else
|
||||
QCOMPARE(priv->calculateResponse("GET", "/", u"").constData(), "NTLM TlRMTVNTUAABAAAABYIIAAAAAAAAAAAAAAAAAAAAAAA=");
|
||||
|
||||
// NTLM phase 2: challenge
|
||||
headers.clear();
|
||||
headers << qMakePair(QByteArray("WWW-Authenticate"), "NTLM " + data.toUtf8());
|
||||
priv->parseHttpResponse(headers, /*isProxy = */ false, {});
|
||||
|
||||
QEXPECT_FAIL("with-realm", "NTLM authentication code doesn't extract the realm", Continue);
|
||||
QEXPECT_FAIL("with-realm-sso", "NTLM authentication code doesn't extract the realm", Continue);
|
||||
QCOMPARE(auth.realm(), realm);
|
||||
|
||||
QVERIFY(priv->calculateResponse("GET", "/", u"").startsWith("NTLM "));
|
||||
}
|
||||
|
||||
// We don't (currently) support SHA256. So, when presented with the option of MD5 or SHA256,
|
||||
// we should always pick MD5.
|
||||
void tst_QAuthenticator::sha256AndMd5Digest()
|
||||
{
|
||||
QByteArray md5 = "Digest realm=\"\", nonce=\"\", algorithm=MD5, qop=\"auth\"";
|
||||
QByteArray sha256 = "Digest realm=\"\", nonce=\"\", algorithm=SHA-256, qop=\"auth\"";
|
||||
|
||||
QAuthenticator auth;
|
||||
auth.setUser("unimportant");
|
||||
auth.setPassword("unimportant");
|
||||
|
||||
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(auth);
|
||||
QVERIFY(priv->isMethodSupported("digest")); // sanity check
|
||||
|
||||
QCOMPARE(priv->phase, QAuthenticatorPrivate::Start);
|
||||
QList<QPair<QByteArray, QByteArray>> headers;
|
||||
// Put sha256 first, so that its parsed first...
|
||||
headers.emplace_back("WWW-Authenticate", sha256);
|
||||
headers.emplace_back("WWW-Authenticate", md5);
|
||||
priv->parseHttpResponse(headers, false, QString());
|
||||
|
||||
QByteArray response = priv->calculateResponse("GET", "/index", {});
|
||||
QCOMPARE(priv->phase, QAuthenticatorPrivate::Done);
|
||||
|
||||
QVERIFY(!response.isEmpty());
|
||||
QVERIFY(!response.contains("algorithm=SHA-256"));
|
||||
QVERIFY(response.contains("algorithm=MD5"));
|
||||
}
|
||||
|
||||
void tst_QAuthenticator::equalityOperators()
|
||||
{
|
||||
QAuthenticator s1, s2;
|
||||
QVERIFY(s2 == s1);
|
||||
QVERIFY(s1 == s2);
|
||||
QVERIFY(!(s1 != s2));
|
||||
QVERIFY(!(s2 != s1));
|
||||
s1.setUser("User");
|
||||
QVERIFY(!(s2 == s1));
|
||||
QVERIFY(!(s1 == s2));
|
||||
QVERIFY(s1 != s2);
|
||||
QVERIFY(s2 != s1);
|
||||
}
|
||||
|
||||
void tst_QAuthenticator::isMethodSupported()
|
||||
{
|
||||
QVERIFY(QAuthenticatorPrivate::isMethodSupported("basic"));
|
||||
QVERIFY(QAuthenticatorPrivate::isMethodSupported("Basic realm=\"Shadow\""));
|
||||
QVERIFY(QAuthenticatorPrivate::isMethodSupported("DIgesT"));
|
||||
QVERIFY(QAuthenticatorPrivate::isMethodSupported("NTLM"));
|
||||
QVERIFY(QAuthenticatorPrivate::isMethodSupported("ntlm"));
|
||||
#if QT_CONFIG(sspi) || QT_CONFIG(gssapi)
|
||||
QVERIFY(QAuthenticatorPrivate::isMethodSupported("negotiate"));
|
||||
#else
|
||||
QVERIFY(!QAuthenticatorPrivate::isMethodSupported("negotiate"));
|
||||
#endif
|
||||
|
||||
QVERIFY(!QAuthenticatorPrivate::isMethodSupported("Bearer"));
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QAuthenticator);
|
||||
|
||||
#include "tst_qauthenticator.moc"
|
2
tests/auto/network/kernel/qdnslookup/BLACKLIST
Normal file
2
tests/auto/network/kernel/qdnslookup/BLACKLIST
Normal file
@ -0,0 +1,2 @@
|
||||
[lookup]
|
||||
*
|
13
tests/auto/network/kernel/qdnslookup/CMakeLists.txt
Normal file
13
tests/auto/network/kernel/qdnslookup/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qdnslookup Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qdnslookup
|
||||
SOURCES
|
||||
tst_qdnslookup.cpp
|
||||
LIBRARIES
|
||||
Qt::Network
|
||||
)
|
403
tests/auto/network/kernel/qdnslookup/tst_qdnslookup.cpp
Normal file
403
tests/auto/network/kernel/qdnslookup/tst_qdnslookup.cpp
Normal file
@ -0,0 +1,403 @@
|
||||
// Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
|
||||
// Copyright (C) 2016 Intel Corporation.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
|
||||
#include <QTest>
|
||||
#include <QSignalSpy>
|
||||
|
||||
#include <QtNetwork/QDnsLookup>
|
||||
#include <QtNetwork/QHostAddress>
|
||||
|
||||
static const int Timeout = 15000; // 15s
|
||||
|
||||
class tst_QDnsLookup: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QString domainName(const QString &input);
|
||||
QString domainNameList(const QString &input);
|
||||
QStringList domainNameListAlternatives(const QString &input);
|
||||
public slots:
|
||||
void initTestCase();
|
||||
|
||||
private slots:
|
||||
void lookup_data();
|
||||
void lookup();
|
||||
void lookupReuse();
|
||||
void lookupAbortRetry();
|
||||
void bindingsAndProperties();
|
||||
};
|
||||
|
||||
void tst_QDnsLookup::initTestCase()
|
||||
{
|
||||
QTest::addColumn<QString>("tld");
|
||||
QTest::newRow("normal") << ".test.qt-project.org";
|
||||
QTest::newRow("idn") << ".alqualond\xc3\xab.test.qt-project.org";
|
||||
}
|
||||
|
||||
QString tst_QDnsLookup::domainName(const QString &input)
|
||||
{
|
||||
if (input.isEmpty())
|
||||
return input;
|
||||
|
||||
if (input.endsWith(QLatin1Char('.'))) {
|
||||
QString nodot = input;
|
||||
nodot.chop(1);
|
||||
return nodot;
|
||||
}
|
||||
|
||||
QFETCH_GLOBAL(QString, tld);
|
||||
return input + tld;
|
||||
}
|
||||
|
||||
QString tst_QDnsLookup::domainNameList(const QString &input)
|
||||
{
|
||||
QStringList list = input.split(QLatin1Char(';'));
|
||||
QString result;
|
||||
foreach (const QString &s, list) {
|
||||
if (!result.isEmpty())
|
||||
result += ';';
|
||||
result += domainName(s);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList tst_QDnsLookup::domainNameListAlternatives(const QString &input)
|
||||
{
|
||||
QStringList alternatives = input.split('|');
|
||||
for (int i = 0; i < alternatives.size(); ++i)
|
||||
alternatives[i] = domainNameList(alternatives[i]);
|
||||
return alternatives;
|
||||
}
|
||||
|
||||
void tst_QDnsLookup::lookup_data()
|
||||
{
|
||||
QTest::addColumn<int>("type");
|
||||
QTest::addColumn<QString>("domain");
|
||||
QTest::addColumn<int>("error");
|
||||
QTest::addColumn<QString>("cname");
|
||||
QTest::addColumn<QString>("host");
|
||||
QTest::addColumn<QString>("mx");
|
||||
QTest::addColumn<QString>("ns");
|
||||
QTest::addColumn<QString>("ptr");
|
||||
QTest::addColumn<QString>("srv");
|
||||
QTest::addColumn<QString>("txt");
|
||||
|
||||
QTest::newRow("a-empty") << int(QDnsLookup::A) << "" << int(QDnsLookup::InvalidRequestError) << "" << "" << "" << "" << ""<< "" << "";
|
||||
QTest::newRow("a-notfound") << int(QDnsLookup::A) << "invalid.invalid" << int(QDnsLookup::NotFoundError) << "" << "" << "" << "" << "" << "" << "";
|
||||
QTest::newRow("a-single") << int(QDnsLookup::A) << "a-single" << int(QDnsLookup::NoError) << "" << "192.0.2.1" << "" << "" << "" << "" << "";
|
||||
QTest::newRow("a-multi") << int(QDnsLookup::A) << "a-multi" << int(QDnsLookup::NoError) << "" << "192.0.2.1;192.0.2.2;192.0.2.3" << "" << "" << "" << "" << "";
|
||||
QTest::newRow("aaaa-empty") << int(QDnsLookup::AAAA) << "" << int(QDnsLookup::InvalidRequestError) << "" << "" << "" << "" << "" << "" << "";
|
||||
QTest::newRow("aaaa-notfound") << int(QDnsLookup::AAAA) << "invalid.invalid" << int(QDnsLookup::NotFoundError) << "" << "" << "" << "" << "" << "" << "";
|
||||
QTest::newRow("aaaa-single") << int(QDnsLookup::AAAA) << "aaaa-single" << int(QDnsLookup::NoError) << "" << "2001:db8::1" << "" << "" << "" << "" << "";
|
||||
QTest::newRow("aaaa-multi") << int(QDnsLookup::AAAA) << "aaaa-multi" << int(QDnsLookup::NoError) << "" << "2001:db8::1;2001:db8::2;2001:db8::3" << "" << "" << "" << "" << "";
|
||||
|
||||
QTest::newRow("any-empty") << int(QDnsLookup::ANY) << "" << int(QDnsLookup::InvalidRequestError) << "" << "" << "" << "" << "" << "" << "";
|
||||
QTest::newRow("any-notfound") << int(QDnsLookup::ANY) << "invalid.invalid" << int(QDnsLookup::NotFoundError) << "" << "" << "" << "" << "" << "" << "";
|
||||
QTest::newRow("any-a-single") << int(QDnsLookup::ANY) << "a-single" << int(QDnsLookup::NoError) << "" << "192.0.2.1" << "" << "" << "" << "" << "";
|
||||
QTest::newRow("any-a-plus-aaaa") << int(QDnsLookup::ANY) << "a-plus-aaaa" << int(QDnsLookup::NoError) << "" << "198.51.100.1;2001:db8::1:1" << "" << "" << "" << "" << "";
|
||||
QTest::newRow("any-multi") << int(QDnsLookup::ANY) << "multi" << int(QDnsLookup::NoError) << "" << "198.51.100.1;198.51.100.2;198.51.100.3;2001:db8::1:1;2001:db8::1:2" << "" << "" << "" << "" << "";
|
||||
|
||||
QTest::newRow("mx-empty") << int(QDnsLookup::MX) << "" << int(QDnsLookup::InvalidRequestError) << "" << "" << "" << "" << "" << "" << "";
|
||||
QTest::newRow("mx-notfound") << int(QDnsLookup::MX) << "invalid.invalid" << int(QDnsLookup::NotFoundError) << "" << "" << "" << "" << "" << "" << "";
|
||||
QTest::newRow("mx-single") << int(QDnsLookup::MX) << "mx-single" << int(QDnsLookup::NoError) << "" << "" << "10 multi" << "" << "" << "" << "";
|
||||
QTest::newRow("mx-single-cname") << int(QDnsLookup::MX) << "mx-single-cname" << int(QDnsLookup::NoError) << "" << "" << "10 cname" << "" << "" << "" << "";
|
||||
QTest::newRow("mx-multi") << int(QDnsLookup::MX) << "mx-multi" << int(QDnsLookup::NoError) << "" << "" << "10 multi;20 a-single" << "" << "" << "" << "";
|
||||
QTest::newRow("mx-multi-sameprio") << int(QDnsLookup::MX) << "mx-multi-sameprio" << int(QDnsLookup::NoError) << "" << ""
|
||||
<< "10 multi;10 a-single|"
|
||||
"10 a-single;10 multi" << "" << "" << "" << "";
|
||||
|
||||
QTest::newRow("ns-empty") << int(QDnsLookup::NS) << "" << int(QDnsLookup::InvalidRequestError) << "" << "" << "" << "" << "" << "" << "";
|
||||
QTest::newRow("ns-notfound") << int(QDnsLookup::NS) << "invalid.invalid" << int(QDnsLookup::NotFoundError) << "" << "" << "" << "" << "" << "" << "";
|
||||
QTest::newRow("ns-single") << int(QDnsLookup::NS) << "ns-single" << int(QDnsLookup::NoError) << "" << "" << "" << "ns11.cloudns.net." << "" << "" << "";
|
||||
QTest::newRow("ns-multi") << int(QDnsLookup::NS) << "ns-multi" << int(QDnsLookup::NoError) << "" << "" << "" << "ns11.cloudns.net.;ns12.cloudns.net." << "" << "" << "";
|
||||
|
||||
QTest::newRow("ptr-empty") << int(QDnsLookup::PTR) << "" << int(QDnsLookup::InvalidRequestError) << "" << "" << "" << "" << "" << "" << "";
|
||||
QTest::newRow("ptr-notfound") << int(QDnsLookup::PTR) << "invalid.invalid" << int(QDnsLookup::NotFoundError) << "" << "" << "" << "" << "" << "" << "";
|
||||
#if 0
|
||||
// temporarily disabled since the new hosting provider can't insert
|
||||
// PTR records outside of the in-addr.arpa zone
|
||||
QTest::newRow("ptr-single") << int(QDnsLookup::PTR) << "ptr-single" << int(QDnsLookup::NoError) << "" << "" << "" << "" << "a-single" << "" << "";
|
||||
#endif
|
||||
|
||||
QTest::newRow("srv-empty") << int(QDnsLookup::SRV) << "" << int(QDnsLookup::InvalidRequestError) << "" << "" << "" << "" << "" << "" << "";
|
||||
QTest::newRow("srv-notfound") << int(QDnsLookup::SRV) << "invalid.invalid" << int(QDnsLookup::NotFoundError) << "" << "" << "" << "" << "" << "" << "";
|
||||
QTest::newRow("srv-single") << int(QDnsLookup::SRV) << "_echo._tcp.srv-single" << int(QDnsLookup::NoError) << "" << "" << "" << "" << "" << "5 0 7 multi" << "";
|
||||
QTest::newRow("srv-prio") << int(QDnsLookup::SRV) << "_echo._tcp.srv-prio" << int(QDnsLookup::NoError) << "" << "" << "" << "" << "" << "1 0 7 multi;2 0 7 a-plus-aaaa" << "";
|
||||
QTest::newRow("srv-weighted") << int(QDnsLookup::SRV) << "_echo._tcp.srv-weighted" << int(QDnsLookup::NoError) << "" << "" << "" << "" << ""
|
||||
<< "5 75 7 multi;5 25 7 a-plus-aaaa|"
|
||||
"5 25 7 a-plus-aaaa;5 75 7 multi" << "";
|
||||
QTest::newRow("srv-multi") << int(QDnsLookup::SRV) << "_echo._tcp.srv-multi" << int(QDnsLookup::NoError) << "" << "" << "" << "" << ""
|
||||
<< "1 50 7 multi;2 50 7 a-single;2 50 7 aaaa-single;3 50 7 a-multi|"
|
||||
"1 50 7 multi;2 50 7 aaaa-single;2 50 7 a-single;3 50 7 a-multi" << "";
|
||||
|
||||
QTest::newRow("txt-empty") << int(QDnsLookup::TXT) << "" << int(QDnsLookup::InvalidRequestError) << "" << "" << "" << "" << "" << "" << "";
|
||||
QTest::newRow("txt-notfound") << int(QDnsLookup::TXT) << "invalid.invalid" << int(QDnsLookup::NotFoundError) << "" << "" << "" << "" << "" << "" << "";
|
||||
QTest::newRow("txt-single") << int(QDnsLookup::TXT) << "txt-single" << int(QDnsLookup::NoError) << "" << "" << "" << "" << "" << "" << "Hello";
|
||||
QTest::newRow("txt-multi-onerr") << int(QDnsLookup::TXT) << "txt-multi-onerr" << int(QDnsLookup::NoError) << "" << "" << "" << "" << "" << ""
|
||||
<< QString::fromLatin1("Hello\0World", sizeof("Hello\0World") - 1);
|
||||
QTest::newRow("txt-multi-multirr") << int(QDnsLookup::TXT) << "txt-multi-multirr" << int(QDnsLookup::NoError) << "" << "" << "" << "" << "" << "" << "Hello;World";
|
||||
}
|
||||
|
||||
static QByteArray msgDnsLookup(QDnsLookup::Error actualError,
|
||||
int expectedError,
|
||||
const QString &domain,
|
||||
const QString &cname,
|
||||
const QString &host,
|
||||
const QString &srv,
|
||||
const QString &mx,
|
||||
const QString &ns,
|
||||
const QString &ptr,
|
||||
const QString &errorString)
|
||||
{
|
||||
QString result;
|
||||
QTextStream str(&result);
|
||||
str << "Actual error: " << actualError;
|
||||
if (!errorString.isEmpty())
|
||||
str << " (" << errorString << ')';
|
||||
str << ", expected: " << expectedError;
|
||||
str << ", domain: " << domain;
|
||||
if (!cname.isEmpty())
|
||||
str << ", cname: " << cname;
|
||||
str << ", host: " << host;
|
||||
if (!srv.isEmpty())
|
||||
str << " server: " << srv;
|
||||
if (!mx.isEmpty())
|
||||
str << " mx: " << mx;
|
||||
if (!ns.isEmpty())
|
||||
str << " ns: " << ns;
|
||||
if (!ptr.isEmpty())
|
||||
str << " ptr: " << ptr;
|
||||
return result.toLocal8Bit();
|
||||
}
|
||||
|
||||
void tst_QDnsLookup::lookup()
|
||||
{
|
||||
QFETCH(int, type);
|
||||
QFETCH(QString, domain);
|
||||
QFETCH(int, error);
|
||||
QFETCH(QString, cname);
|
||||
QFETCH(QString, host);
|
||||
QFETCH(QString, mx);
|
||||
QFETCH(QString, ns);
|
||||
QFETCH(QString, ptr);
|
||||
QFETCH(QString, srv);
|
||||
QFETCH(QString, txt);
|
||||
|
||||
// transform the inputs
|
||||
domain = domainName(domain);
|
||||
cname = domainName(cname);
|
||||
ns = domainNameList(ns);
|
||||
ptr = domainNameList(ptr);
|
||||
|
||||
// SRV and MX have reply entries that can change order
|
||||
// and we can't sort
|
||||
QStringList mx_alternatives = domainNameListAlternatives(mx);
|
||||
QStringList srv_alternatives = domainNameListAlternatives(srv);
|
||||
|
||||
QDnsLookup lookup;
|
||||
lookup.setType(static_cast<QDnsLookup::Type>(type));
|
||||
lookup.setName(domain);
|
||||
lookup.lookup();
|
||||
QTRY_VERIFY_WITH_TIMEOUT(lookup.isFinished(), Timeout);
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
if (lookup.errorString() == QStringLiteral("Not yet supported on Android"))
|
||||
QEXPECT_FAIL("", "Not yet supported on Android", Abort);
|
||||
#endif
|
||||
|
||||
QVERIFY2(int(lookup.error()) == error,
|
||||
msgDnsLookup(lookup.error(), error, domain, cname, host, srv, mx, ns, ptr, lookup.errorString()));
|
||||
if (error == QDnsLookup::NoError)
|
||||
QVERIFY(lookup.errorString().isEmpty());
|
||||
QCOMPARE(int(lookup.type()), type);
|
||||
QCOMPARE(lookup.name(), domain);
|
||||
|
||||
// canonical names
|
||||
if (!cname.isEmpty()) {
|
||||
QVERIFY(!lookup.canonicalNameRecords().isEmpty());
|
||||
const QDnsDomainNameRecord cnameRecord = lookup.canonicalNameRecords().first();
|
||||
QCOMPARE(cnameRecord.name(), domain);
|
||||
QCOMPARE(cnameRecord.value(), cname);
|
||||
} else {
|
||||
QVERIFY(lookup.canonicalNameRecords().isEmpty());
|
||||
}
|
||||
|
||||
// host addresses
|
||||
const QString hostName = cname.isEmpty() ? domain : cname;
|
||||
QStringList addresses;
|
||||
foreach (const QDnsHostAddressRecord &record, lookup.hostAddressRecords()) {
|
||||
//reply may include A & AAAA records for nameservers, ignore them and only look at records matching the query
|
||||
if (record.name() == hostName)
|
||||
addresses << record.value().toString().toLower();
|
||||
}
|
||||
addresses.sort();
|
||||
QCOMPARE(addresses.join(';'), host);
|
||||
|
||||
// mail exchanges
|
||||
QStringList mailExchanges;
|
||||
foreach (const QDnsMailExchangeRecord &record, lookup.mailExchangeRecords()) {
|
||||
QCOMPARE(record.name(), domain);
|
||||
mailExchanges << QString::number(record.preference()) + QLatin1Char(' ') + record.exchange();
|
||||
}
|
||||
QVERIFY2(mx_alternatives.contains(mailExchanges.join(';')),
|
||||
qPrintable("Actual: " + mailExchanges.join(';') + "\nExpected one of:\n" + mx_alternatives.join('\n')));
|
||||
|
||||
// name servers
|
||||
QStringList nameServers;
|
||||
foreach (const QDnsDomainNameRecord &record, lookup.nameServerRecords()) {
|
||||
//reply may include NS records for authoritative nameservers, ignore them and only look at records matching the query
|
||||
if (record.name() == domain)
|
||||
nameServers << record.value();
|
||||
}
|
||||
nameServers.sort();
|
||||
QCOMPARE(nameServers.join(';'), ns);
|
||||
|
||||
// pointers
|
||||
if (!ptr.isEmpty()) {
|
||||
QVERIFY(!lookup.pointerRecords().isEmpty());
|
||||
const QDnsDomainNameRecord ptrRecord = lookup.pointerRecords().first();
|
||||
QCOMPARE(ptrRecord.name(), domain);
|
||||
QCOMPARE(ptrRecord.value(), ptr);
|
||||
} else {
|
||||
QVERIFY(lookup.pointerRecords().isEmpty());
|
||||
}
|
||||
|
||||
// services
|
||||
QStringList services;
|
||||
foreach (const QDnsServiceRecord &record, lookup.serviceRecords()) {
|
||||
QCOMPARE(record.name(), domain);
|
||||
services << (QString::number(record.priority()) + QLatin1Char(' ')
|
||||
+ QString::number(record.weight()) + QLatin1Char(' ')
|
||||
+ QString::number(record.port()) + QLatin1Char(' ') + record.target());
|
||||
}
|
||||
QVERIFY2(srv_alternatives.contains(services.join(';')),
|
||||
qPrintable("Actual: " + services.join(';') + "\nExpected one of:\n" + srv_alternatives.join('\n')));
|
||||
|
||||
// text
|
||||
QStringList texts;
|
||||
foreach (const QDnsTextRecord &record, lookup.textRecords()) {
|
||||
QCOMPARE(record.name(), domain);
|
||||
QString text;
|
||||
foreach (const QByteArray &ba, record.values()) {
|
||||
if (!text.isEmpty())
|
||||
text += '\0';
|
||||
text += QString::fromLatin1(ba);
|
||||
}
|
||||
texts << text;
|
||||
}
|
||||
texts.sort();
|
||||
QCOMPARE(texts.join(';'), txt);
|
||||
}
|
||||
|
||||
void tst_QDnsLookup::lookupReuse()
|
||||
{
|
||||
QDnsLookup lookup;
|
||||
|
||||
// first lookup
|
||||
lookup.setType(QDnsLookup::A);
|
||||
lookup.setName(domainName("a-single"));
|
||||
lookup.lookup();
|
||||
QTRY_VERIFY_WITH_TIMEOUT(lookup.isFinished(), Timeout);
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
if (lookup.errorString() == QStringLiteral("Not yet supported on Android"))
|
||||
QEXPECT_FAIL("", "Not yet supported on Android", Abort);
|
||||
#endif
|
||||
|
||||
QCOMPARE(int(lookup.error()), int(QDnsLookup::NoError));
|
||||
QVERIFY(!lookup.hostAddressRecords().isEmpty());
|
||||
QCOMPARE(lookup.hostAddressRecords().first().name(), domainName("a-single"));
|
||||
QCOMPARE(lookup.hostAddressRecords().first().value(), QHostAddress("192.0.2.1"));
|
||||
|
||||
// second lookup
|
||||
lookup.setType(QDnsLookup::AAAA);
|
||||
lookup.setName(domainName("aaaa-single"));
|
||||
lookup.lookup();
|
||||
QTRY_VERIFY_WITH_TIMEOUT(lookup.isFinished(), Timeout);
|
||||
QCOMPARE(int(lookup.error()), int(QDnsLookup::NoError));
|
||||
QVERIFY(!lookup.hostAddressRecords().isEmpty());
|
||||
QCOMPARE(lookup.hostAddressRecords().first().name(), domainName("aaaa-single"));
|
||||
QCOMPARE(lookup.hostAddressRecords().first().value(), QHostAddress("2001:db8::1"));
|
||||
}
|
||||
|
||||
|
||||
void tst_QDnsLookup::lookupAbortRetry()
|
||||
{
|
||||
QDnsLookup lookup;
|
||||
|
||||
// try and abort the lookup
|
||||
lookup.setType(QDnsLookup::A);
|
||||
lookup.setName(domainName("a-single"));
|
||||
lookup.lookup();
|
||||
lookup.abort();
|
||||
QTRY_VERIFY_WITH_TIMEOUT(lookup.isFinished(), Timeout);
|
||||
QCOMPARE(int(lookup.error()), int(QDnsLookup::OperationCancelledError));
|
||||
QVERIFY(lookup.hostAddressRecords().isEmpty());
|
||||
|
||||
// retry a different lookup
|
||||
lookup.setType(QDnsLookup::AAAA);
|
||||
lookup.setName(domainName("aaaa-single"));
|
||||
lookup.lookup();
|
||||
QTRY_VERIFY_WITH_TIMEOUT(lookup.isFinished(), Timeout);
|
||||
|
||||
#if defined(Q_OS_ANDROID)
|
||||
if (lookup.errorString() == QStringLiteral("Not yet supported on Android"))
|
||||
QEXPECT_FAIL("", "Not yet supported on Android", Abort);
|
||||
#endif
|
||||
|
||||
QCOMPARE(int(lookup.error()), int(QDnsLookup::NoError));
|
||||
QVERIFY(!lookup.hostAddressRecords().isEmpty());
|
||||
QCOMPARE(lookup.hostAddressRecords().first().name(), domainName("aaaa-single"));
|
||||
QCOMPARE(lookup.hostAddressRecords().first().value(), QHostAddress("2001:db8::1"));
|
||||
}
|
||||
|
||||
void tst_QDnsLookup::bindingsAndProperties()
|
||||
{
|
||||
QFETCH_GLOBAL(const QString, tld);
|
||||
if (tld == QStringLiteral("idn"))
|
||||
return;
|
||||
|
||||
QDnsLookup lookup;
|
||||
|
||||
lookup.setType(QDnsLookup::A);
|
||||
QProperty<QDnsLookup::Type> dnsTypeProp;
|
||||
lookup.bindableType().setBinding(Qt::makePropertyBinding(dnsTypeProp));
|
||||
const QSignalSpy typeChangeSpy(&lookup, &QDnsLookup::typeChanged);
|
||||
|
||||
dnsTypeProp = QDnsLookup::AAAA;
|
||||
QCOMPARE(typeChangeSpy.size(), 1);
|
||||
QCOMPARE(lookup.type(), QDnsLookup::AAAA);
|
||||
|
||||
dnsTypeProp.setBinding(lookup.bindableType().makeBinding());
|
||||
lookup.setType(QDnsLookup::A);
|
||||
QCOMPARE(dnsTypeProp.value(), QDnsLookup::A);
|
||||
|
||||
QProperty<QString> nameProp;
|
||||
lookup.bindableName().setBinding(Qt::makePropertyBinding(nameProp));
|
||||
const QSignalSpy nameChangeSpy(&lookup, &QDnsLookup::nameChanged);
|
||||
|
||||
nameProp = QStringLiteral("a-plus-aaaa");
|
||||
QCOMPARE(nameChangeSpy.size(), 1);
|
||||
QCOMPARE(lookup.name(), QStringLiteral("a-plus-aaaa"));
|
||||
|
||||
nameProp.setBinding(lookup.bindableName().makeBinding());
|
||||
lookup.setName(QStringLiteral("a-single"));
|
||||
QCOMPARE(nameProp.value(), QStringLiteral("a-single"));
|
||||
|
||||
QProperty<QHostAddress> nameserverProp;
|
||||
lookup.bindableNameserver().setBinding(Qt::makePropertyBinding(nameserverProp));
|
||||
const QSignalSpy nameserverChangeSpy(&lookup, &QDnsLookup::nameserverChanged);
|
||||
|
||||
nameserverProp = QHostAddress::LocalHost;
|
||||
QCOMPARE(nameserverChangeSpy.size(), 1);
|
||||
QCOMPARE(lookup.nameserver(), QHostAddress::LocalHost);
|
||||
|
||||
nameserverProp.setBinding(lookup.bindableNameserver().makeBinding());
|
||||
lookup.setNameserver(QHostAddress::Any);
|
||||
QCOMPARE(nameserverProp.value(), QHostAddress::Any);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QDnsLookup)
|
||||
#include "tst_qdnslookup.moc"
|
13
tests/auto/network/kernel/qdnslookup_appless/CMakeLists.txt
Normal file
13
tests/auto/network/kernel/qdnslookup_appless/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qdnslookup_appless Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qdnslookup_appless
|
||||
SOURCES
|
||||
tst_qdnslookup_appless.cpp
|
||||
LIBRARIES
|
||||
Qt::Network
|
||||
)
|
@ -0,0 +1,55 @@
|
||||
// Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QtCore/QCoreApplication>
|
||||
#include <QtNetwork/QDnsLookup>
|
||||
#include <QTest>
|
||||
#include <QTestEventLoop>
|
||||
|
||||
class tst_QDnsLookup_Appless : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void noApplication();
|
||||
void recreateApplication();
|
||||
void destroyApplicationDuringLookup();
|
||||
};
|
||||
|
||||
void tst_QDnsLookup_Appless::noApplication()
|
||||
{
|
||||
QTest::ignoreMessage(QtWarningMsg, "QDnsLookup requires a QCoreApplication");
|
||||
QDnsLookup dns(QDnsLookup::A, "a-single.test.qt-project.org");
|
||||
dns.lookup();
|
||||
}
|
||||
|
||||
void tst_QDnsLookup_Appless::recreateApplication()
|
||||
{
|
||||
int argc = 0;
|
||||
char **argv = 0;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
QCoreApplication app(argc, argv);
|
||||
QDnsLookup dns(QDnsLookup::A, "a-single.test.qt-project.org");
|
||||
dns.lookup();
|
||||
if (!dns.isFinished()) {
|
||||
QObject::connect(&dns, SIGNAL(finished()),
|
||||
&QTestEventLoop::instance(), SLOT(exitLoop()));
|
||||
QTestEventLoop::instance().enterLoop(10);
|
||||
}
|
||||
QVERIFY(dns.isFinished());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QDnsLookup_Appless::destroyApplicationDuringLookup()
|
||||
{
|
||||
int argc = 0;
|
||||
char **argv = 0;
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
QCoreApplication app(argc, argv);
|
||||
QDnsLookup dns(QDnsLookup::A, "a-single.test.macieira.info");
|
||||
dns.lookup();
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_QDnsLookup_Appless)
|
||||
#include "tst_qdnslookup_appless.moc"
|
21
tests/auto/network/kernel/qhostaddress/CMakeLists.txt
Normal file
21
tests/auto/network/kernel/qhostaddress/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qhostaddress Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qhostaddress
|
||||
SOURCES
|
||||
tst_qhostaddress.cpp
|
||||
LIBRARIES
|
||||
Qt::NetworkPrivate
|
||||
)
|
||||
|
||||
## Scopes:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_extend_target(tst_qhostaddress CONDITION WIN32
|
||||
LIBRARIES
|
||||
ws2_32
|
||||
)
|
763
tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp
Normal file
763
tests/auto/network/kernel/qhostaddress/tst_qhostaddress.cpp
Normal file
@ -0,0 +1,763 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// Copyright (C) 2016 Intel Corporation.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <qhostaddress.h>
|
||||
#include <private/qhostaddress_p.h>
|
||||
#include <qcoreapplication.h>
|
||||
#include <QTest>
|
||||
#include <qplatformdefs.h>
|
||||
#include <qdebug.h>
|
||||
#include <qhash.h>
|
||||
#include <qbytearray.h>
|
||||
#include <qdatastream.h>
|
||||
#ifdef Q_OS_WIN
|
||||
# include <qt_windows.h>
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_ANDROID) || defined(Q_OS_WASM)
|
||||
# include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
Q_DECLARE_METATYPE(AddressClassification)
|
||||
Q_DECLARE_METATYPE(QHostAddress::SpecialAddress)
|
||||
|
||||
class tst_QHostAddress : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
tst_QHostAddress();
|
||||
|
||||
private slots:
|
||||
void constructor_QString_data();
|
||||
void constructor_QString();
|
||||
void setAddress_QString_data();
|
||||
void setAddress_QString();
|
||||
void specialAddresses_data();
|
||||
void specialAddresses();
|
||||
void compare_data();
|
||||
void compare();
|
||||
void isEqual_data();
|
||||
void isEqual();
|
||||
void assignment();
|
||||
void scopeId();
|
||||
void hashKey();
|
||||
void streaming_data();
|
||||
void streaming();
|
||||
void parseSubnet_data();
|
||||
void parseSubnet();
|
||||
void isInSubnet_data();
|
||||
void isInSubnet();
|
||||
void classification_data();
|
||||
void classification();
|
||||
void convertv4v6_data();
|
||||
void convertv4v6();
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(QHostAddress)
|
||||
|
||||
tst_QHostAddress::tst_QHostAddress()
|
||||
{
|
||||
qRegisterMetaType<QHostAddress>("QHostAddress");
|
||||
}
|
||||
|
||||
void tst_QHostAddress::constructor_QString_data()
|
||||
{
|
||||
setAddress_QString_data();
|
||||
}
|
||||
|
||||
void tst_QHostAddress::constructor_QString()
|
||||
{
|
||||
QFETCH(QString, address);
|
||||
QFETCH(bool, ok);
|
||||
QFETCH(int, protocol);
|
||||
|
||||
QHostAddress hostAddr(address);
|
||||
|
||||
if (address == "0.0.0.0" || address == "::") {
|
||||
QVERIFY(ok);
|
||||
} else {
|
||||
QVERIFY(hostAddr.isNull() != ok);
|
||||
}
|
||||
|
||||
if (ok)
|
||||
QTEST(hostAddr.toString(), "resAddr");
|
||||
|
||||
if ( protocol == 4 ) {
|
||||
QVERIFY( hostAddr.protocol() == QHostAddress::IPv4Protocol || hostAddr.protocol() == QHostAddress::UnknownNetworkLayerProtocol );
|
||||
QVERIFY( hostAddr.protocol() != QHostAddress::IPv6Protocol );
|
||||
} else if ( protocol == 6 ) {
|
||||
QVERIFY( hostAddr.protocol() != QHostAddress::IPv4Protocol && hostAddr.protocol() != QHostAddress::UnknownNetworkLayerProtocol );
|
||||
QVERIFY( hostAddr.protocol() == QHostAddress::IPv6Protocol );
|
||||
} else {
|
||||
QVERIFY( hostAddr.isNull() );
|
||||
QVERIFY( hostAddr.protocol() == QHostAddress::UnknownNetworkLayerProtocol );
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QHostAddress::setAddress_QString_data()
|
||||
{
|
||||
QTest::addColumn<QString>("address");
|
||||
QTest::addColumn<bool>("ok");
|
||||
QTest::addColumn<QString>("resAddr");
|
||||
QTest::addColumn<int>("protocol"); // 4: IPv4, 6: IPv6, other: undefined
|
||||
|
||||
//next we fill it with data
|
||||
QTest::newRow("ip4_00") << QString("127.0.0.1") << true << QString("127.0.0.1") << 4;
|
||||
QTest::newRow("ip4_01") << QString("255.3.2.1") << true << QString("255.3.2.1") << 4;
|
||||
QTest::newRow("ip4_03") << QString(" 255.3.2.1") << true << QString("255.3.2.1") << 4;
|
||||
QTest::newRow("ip4_04") << QString("255.3.2.1\r ") << true << QString("255.3.2.1") << 4;
|
||||
QTest::newRow("ip4_05") << QString("0.0.0.0") << true << QString("0.0.0.0") << 4;
|
||||
QTest::newRow("ip4_06") << QString("123.0.0") << true << QString("123.0.0.0") << 4;
|
||||
|
||||
// for the format of IPv6 addresses see also RFC 5952
|
||||
// rule 4.1: Leading zeros MUST be suppressed
|
||||
// rule 4.2.1: Shorten as Much as Possible
|
||||
// rule 4.2.2: The symbol "::" MUST NOT be used to shorten just one 16-bit 0 field.
|
||||
// rule 4.2.3: the longest run of consecutive 16-bit 0 fields MUST be shortened
|
||||
// When the length of the consecutive 16-bit 0 fields, the first sequence
|
||||
// of zero bits MUST be shortened
|
||||
// rule 4.3: The characters "a", "b", "c", "d", "e", and "f" in an IPv6 address
|
||||
// MUST be represented in lowercase
|
||||
QTest::newRow("ip6_00") << QString("FEDC:BA98:7654:3210:FEDC:BA98:7654:3210") << true << QString("fedc:ba98:7654:3210:fedc:ba98:7654:3210") << 6; // 4.3
|
||||
QTest::newRow("ip6_01") << QString("1080:0000:0000:0000:0008:0800:200C:417A") << true << QString("1080::8:800:200c:417a") << 6; // 4.1, 4.2.1
|
||||
QTest::newRow("ip6_02") << QString("1080:0:0:0:8:800:200C:417A") << true << QString("1080::8:800:200c:417a") << 6;
|
||||
QTest::newRow("ip6_03") << QString("1080::8:800:200C:417A") << true << QString("1080::8:800:200c:417a") << 6;
|
||||
QTest::newRow("ip6_04") << QString("FF01::43") << true << QString("ff01::43") << 6;
|
||||
QTest::newRow("ip6_05") << QString("::1") << true << QString("::1") << 6;
|
||||
QTest::newRow("ip6_06") << QString("1::") << true << QString("1::") << 6;
|
||||
QTest::newRow("ip6_07") << QString("::") << true << QString("::") << 6;
|
||||
QTest::newRow("ip6_08") << QString("0:0:0:0:0:0:13.1.68.3") << true << QString("::13.1.68.3") << 6;
|
||||
QTest::newRow("ip6_09") << QString("::13.1.68.3") << true << QString("::13.1.68.3") << 6;
|
||||
QTest::newRow("ip6_10") << QString("0:0:0:0:0:FFFF:129.144.52.38") << true << QString("::ffff:129.144.52.38") << 6;
|
||||
QTest::newRow("ip6_11") << QString("::FFFF:129.144.52.38") << true << QString("::ffff:129.144.52.38") << 6;
|
||||
QTest::newRow("ip6_12") << QString("1::FFFF:129.144.52.38") << true << QString("1::ffff:8190:3426") << 6;
|
||||
QTest::newRow("ip6_13") << QString("A:B::D:E") << true << QString("a:b::d:e") << 6;
|
||||
QTest::newRow("ip6_14") << QString("1080:0:1:0:8:800:200C:417A") << true << QString("1080:0:1:0:8:800:200c:417a") << 6; // 4.2.2
|
||||
QTest::newRow("ip6_15") << QString("1080:0:1:0:8:800:200C:0") << true << QString("1080:0:1:0:8:800:200c:0") << 6;
|
||||
QTest::newRow("ip6_16") << QString("1080:0:1:0:8:800:0:0") << true << QString("1080:0:1:0:8:800::") << 6;
|
||||
QTest::newRow("ip6_17a") << QString("1080:0:0:8:800:0:0:0") << true << QString("1080:0:0:8:800::") << 6; // 4.2.3a
|
||||
QTest::newRow("ip6_17b") << QString("1080:0:0:0:8:0:0:0") << true << QString("1080::8:0:0:0") << 6; // 4.2.3b
|
||||
QTest::newRow("ip6_18") << QString("0:1:1:1:8:800:0:0") << true << QString("0:1:1:1:8:800::") << 6;
|
||||
QTest::newRow("ip6_19") << QString("0:1:1:1:8:800:0:1") << true << QString("0:1:1:1:8:800:0:1") << 6;
|
||||
|
||||
QTest::newRow("error_00") << QString("foobarcom") << false << QString() << 0;
|
||||
QTest::newRow("error_01") << QString("foo.bar.com") << false << QString() << 0;
|
||||
QTest::newRow("error_02") << QString("") << false << QString() << 0;
|
||||
QTest::newRow("error_03") << QString() << false << QString() << 0;
|
||||
QTest::newRow("error_04") << QString(" \t\r") << false << QString() << 0;
|
||||
|
||||
QTest::newRow("error_ip4_00") << QString("256.9.9.9") << false << QString() << 0;
|
||||
QTest::newRow("error_ip4_01") << QString("-1.9.9.9") << false << QString() << 0;
|
||||
//QTest::newRow("error_ip4_02") << QString("123.0.0") << false << QString() << 0; // no longer invalid in Qt5
|
||||
QTest::newRow("error_ip4_02") << QString("123.0.0.") << false << QString() << 0;
|
||||
QTest::newRow("error_ip4_03") << QString("123.0.0.0.0") << false << QString() << 0;
|
||||
QTest::newRow("error_ip4_04") << QString("255.2 3.2.1") << false << QString() << 0;
|
||||
|
||||
QTest::newRow("error_ip6_00") << QString(":") << false << QString() << 0;
|
||||
QTest::newRow("error_ip6_01") << QString(":::") << false << QString() << 0;
|
||||
QTest::newRow("error_ip6_02") << QString("::AAAA:") << false << QString() << 0;
|
||||
QTest::newRow("error_ip6_03") << QString(":AAAA::") << false << QString() << 0;
|
||||
QTest::newRow("error_ip6_04") << QString("FFFF:::129.144.52.38") << false << QString() << 0;
|
||||
QTest::newRow("error_ip6_05") << QString("FEDC:BA98:7654:3210:FEDC:BA98:7654:3210:1234") << false << QString() << 0;
|
||||
QTest::newRow("error_ip6_06") << QString("129.144.52.38::") << false << QString() << 0;
|
||||
QTest::newRow("error_ip6_07") << QString("::129.144.52.38:129.144.52.38") << false << QString() << 0;
|
||||
QTest::newRow("error_ip6_08") << QString(":::129.144.52.38") << false << QString() << 0;
|
||||
QTest::newRow("error_ip6_09") << QString("1FEDC:BA98:7654:3210:FEDC:BA98:7654:3210") << false << QString() << 0;
|
||||
QTest::newRow("error_ip6_10") << QString("::FFFFFFFF") << false << QString() << 0;
|
||||
QTest::newRow("error_ip6_11") << QString("::EFGH") << false << QString() << 0;
|
||||
QTest::newRow("error_ip6_12") << QString("ABCD:ABCD:ABCD") << false << QString() << 0;
|
||||
QTest::newRow("error_ip6_13") << QString("::ABCD:ABCD::") << false << QString() << 0;
|
||||
QTest::newRow("error_ip6_14") << QString("1::2::3") << false << QString() << 0;
|
||||
QTest::newRow("error_ip6_15") << QString("1:2:::") << false << QString() << 0;
|
||||
QTest::newRow("error_ip6_16") << QString(":::1:2") << false << QString() << 0;
|
||||
QTest::newRow("error_ip6_17") << QString("1:::2") << false << QString() << 0;
|
||||
QTest::newRow("error_ip6_18") << QString("FEDC::7654:3210:FEDC:BA98::3210") << false << QString() << 0;
|
||||
QTest::newRow("error_ip6_19") << QString("ABCD:ABCD:ABCD:1.2.3.4") << false << QString() << 0;
|
||||
QTest::newRow("error_ip6_20") << QString("ABCD::ABCD::ABCD:1.2.3.4") << false << QString() << 0;
|
||||
|
||||
}
|
||||
|
||||
void tst_QHostAddress::setAddress_QString()
|
||||
{
|
||||
QFETCH(QString, address);
|
||||
QFETCH(bool, ok);
|
||||
QFETCH(int, protocol);
|
||||
|
||||
QHostAddress hostAddr;
|
||||
QCOMPARE(hostAddr.setAddress(address), ok);
|
||||
|
||||
if (ok)
|
||||
QTEST(hostAddr.toString(), "resAddr");
|
||||
|
||||
if ( protocol == 4 ) {
|
||||
QVERIFY( hostAddr.protocol() == QHostAddress::IPv4Protocol || hostAddr.protocol() == QHostAddress::UnknownNetworkLayerProtocol );
|
||||
QVERIFY( hostAddr.protocol() != QHostAddress::IPv6Protocol );
|
||||
} else if ( protocol == 6 ) {
|
||||
QVERIFY( hostAddr.protocol() != QHostAddress::IPv4Protocol && hostAddr.protocol() != QHostAddress::UnknownNetworkLayerProtocol );
|
||||
QVERIFY( hostAddr.protocol() == QHostAddress::IPv6Protocol );
|
||||
} else {
|
||||
QVERIFY( hostAddr.isNull() );
|
||||
QVERIFY( hostAddr.protocol() == QHostAddress::UnknownNetworkLayerProtocol );
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QHostAddress::specialAddresses_data()
|
||||
{
|
||||
QTest::addColumn<QString>("text");
|
||||
QTest::addColumn<QHostAddress::SpecialAddress>("address");
|
||||
QTest::addColumn<bool>("result");
|
||||
|
||||
QTest::newRow("localhost_1") << QString("127.0.0.1") << QHostAddress::LocalHost << true;
|
||||
QTest::newRow("localhost_2") << QString("127.0.0.2") << QHostAddress::LocalHost << false;
|
||||
QTest::newRow("localhost_3") << QString("127.0.0.2") << QHostAddress::LocalHostIPv6 << false;
|
||||
|
||||
QTest::newRow("localhost_ipv6_4") << QString("::1") << QHostAddress::LocalHostIPv6 << true;
|
||||
QTest::newRow("localhost_ipv6_5") << QString("::2") << QHostAddress::LocalHostIPv6 << false;
|
||||
QTest::newRow("localhost_ipv6_6") << QString("::1") << QHostAddress::LocalHost << false;
|
||||
|
||||
QTest::newRow("null_1") << QString("") << QHostAddress::Null << true;
|
||||
QTest::newRow("null_2") << QString("bjarne") << QHostAddress::Null << true;
|
||||
|
||||
QTest::newRow("compare_from_null") << QString("") << QHostAddress::Broadcast << false;
|
||||
|
||||
QTest::newRow("broadcast_1") << QString("255.255.255.255") << QHostAddress::Any << false;
|
||||
QTest::newRow("broadcast_2") << QString("255.255.255.255") << QHostAddress::Broadcast << true;
|
||||
|
||||
QTest::newRow("any_ipv6") << QString("::") << QHostAddress::AnyIPv6 << true;
|
||||
QTest::newRow("any_ipv4") << QString("0.0.0.0") << QHostAddress::AnyIPv4 << true;
|
||||
|
||||
QTest::newRow("dual_not_ipv6") << QString("::") << QHostAddress::Any << false;
|
||||
QTest::newRow("dual_not_ipv4") << QString("0.0.0.0") << QHostAddress::Any << false;
|
||||
}
|
||||
|
||||
|
||||
void tst_QHostAddress::specialAddresses()
|
||||
{
|
||||
QFETCH(QString, text);
|
||||
QFETCH(QHostAddress::SpecialAddress, address);
|
||||
QFETCH(bool, result);
|
||||
QCOMPARE(QHostAddress(text) == address, result);
|
||||
|
||||
//check special address equal to itself (QTBUG-22898), note two overloads of operator==
|
||||
QVERIFY(QHostAddress(address) == QHostAddress(address));
|
||||
QVERIFY(QHostAddress(address) == address);
|
||||
QVERIFY(address == QHostAddress(address));
|
||||
QVERIFY(!(QHostAddress(address) != QHostAddress(address)));
|
||||
QVERIFY(!(QHostAddress(address) != address));
|
||||
QVERIFY(!(address != QHostAddress(address)));
|
||||
|
||||
{
|
||||
QHostAddress ha;
|
||||
ha.setAddress(address);
|
||||
QVERIFY(ha == address);
|
||||
}
|
||||
|
||||
QHostAddress setter;
|
||||
setter.setAddress(text);
|
||||
QCOMPARE(setter == address, result);
|
||||
}
|
||||
|
||||
|
||||
void tst_QHostAddress::compare_data()
|
||||
{
|
||||
QTest::addColumn<QHostAddress>("first");
|
||||
QTest::addColumn<QHostAddress>("second");
|
||||
QTest::addColumn<bool>("result");
|
||||
|
||||
QTest::newRow("1") << QHostAddress() << QHostAddress() << true;
|
||||
QTest::newRow("2") << QHostAddress(QHostAddress::Any) << QHostAddress(QHostAddress::Any) << true;
|
||||
QTest::newRow("3") << QHostAddress(QHostAddress::AnyIPv6) << QHostAddress(QHostAddress::AnyIPv6) << true;
|
||||
QTest::newRow("4") << QHostAddress(QHostAddress::Broadcast) << QHostAddress(QHostAddress::Broadcast) << true;
|
||||
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;
|
||||
QTest::newRow("v4-v4mapped-2") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(localhostv4mapped) << false;
|
||||
}
|
||||
|
||||
void tst_QHostAddress::compare()
|
||||
{
|
||||
QFETCH(QHostAddress, first);
|
||||
QFETCH(QHostAddress, second);
|
||||
QFETCH(bool, result);
|
||||
|
||||
QCOMPARE(first == second, result);
|
||||
QCOMPARE(second == first, result);
|
||||
if (result == true)
|
||||
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;
|
||||
QTest::newRow("localhostv6-any-tolerant") << QHostAddress(QHostAddress::LocalHostIPv6) << QHostAddress(QHostAddress::Any) << (int)QHostAddress::TolerantConversion << false;
|
||||
QTest::newRow("localhostv4-any-tolerant") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::Any) << (int)QHostAddress::TolerantConversion << false;
|
||||
QTest::newRow("localhostv6-anyv6-tolerant") << QHostAddress(QHostAddress::LocalHostIPv6) << QHostAddress(QHostAddress::AnyIPv6) << (int)QHostAddress::TolerantConversion << false;
|
||||
QTest::newRow("localhostv4-anyv6-tolerant") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::AnyIPv6) << (int)QHostAddress::TolerantConversion << false;
|
||||
QTest::newRow("localhostv6-anyv4-tolerant") << QHostAddress(QHostAddress::LocalHostIPv6) << QHostAddress(QHostAddress::AnyIPv4) << (int)QHostAddress::TolerantConversion << false;
|
||||
QTest::newRow("localhostv4-anyv4-tolerant") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::AnyIPv4) << (int)QHostAddress::TolerantConversion << 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;
|
||||
QHostAddress addr("4.2.2.1");
|
||||
sockaddr_in sockAddr;
|
||||
sockAddr.sin_family = AF_INET;
|
||||
sockAddr.sin_addr.s_addr = htonl(addr.toIPv4Address());
|
||||
address.setAddress((sockaddr *)&sockAddr);
|
||||
QCOMPARE(address, addr);
|
||||
}
|
||||
|
||||
void tst_QHostAddress::scopeId()
|
||||
{
|
||||
QHostAddress address("fe80::2e0:4cff:fefb:662a%eth0");
|
||||
QCOMPARE(address.scopeId(), QString("eth0"));
|
||||
QCOMPARE(address.toString().toLower(), QString("fe80::2e0:4cff:fefb:662a%eth0"));
|
||||
|
||||
QHostAddress address2("fe80::2e0:4cff:fefb:662a");
|
||||
QCOMPARE(address2.scopeId(), QString());
|
||||
address2.setScopeId(QString("en0"));
|
||||
QCOMPARE(address2.toString().toLower(), QString("fe80::2e0:4cff:fefb:662a%en0"));
|
||||
|
||||
address2 = address;
|
||||
QCOMPARE(address2.scopeId(), QString("eth0"));
|
||||
QCOMPARE(address2.toString().toLower(), QString("fe80::2e0:4cff:fefb:662a%eth0"));
|
||||
}
|
||||
|
||||
void tst_QHostAddress::hashKey()
|
||||
{
|
||||
QHash<QHostAddress, QString> hostHash;
|
||||
hostHash.insert(QHostAddress(), "ole");
|
||||
}
|
||||
|
||||
void tst_QHostAddress::streaming_data()
|
||||
{
|
||||
QTest::addColumn<QHostAddress>("address");
|
||||
QTest::newRow("1") << QHostAddress();
|
||||
QTest::newRow("2") << QHostAddress(0xDEADBEEF);
|
||||
QTest::newRow("3") << QHostAddress("127.128.129.130");
|
||||
QTest::newRow("4") << QHostAddress("1080:0000:0000:0000:0008:0800:200C:417A");
|
||||
QTest::newRow("5") << QHostAddress("fe80::2e0:4cff:fefb:662a%eth0");
|
||||
QTest::newRow("6") << QHostAddress(QHostAddress::Null);
|
||||
QTest::newRow("7") << QHostAddress(QHostAddress::LocalHost);
|
||||
QTest::newRow("8") << QHostAddress(QHostAddress::LocalHostIPv6);
|
||||
QTest::newRow("9") << QHostAddress(QHostAddress::Broadcast);
|
||||
QTest::newRow("10") << QHostAddress(QHostAddress::Any);
|
||||
QTest::newRow("11") << QHostAddress(QHostAddress::AnyIPv4);
|
||||
QTest::newRow("12") << QHostAddress(QHostAddress::AnyIPv6);
|
||||
QTest::newRow("13") << QHostAddress("foo.bar.com");
|
||||
}
|
||||
|
||||
void tst_QHostAddress::streaming()
|
||||
{
|
||||
QFETCH(QHostAddress, address);
|
||||
QByteArray ba;
|
||||
QDataStream ds1(&ba, QIODevice::WriteOnly);
|
||||
ds1 << address;
|
||||
QCOMPARE(ds1.status(), QDataStream::Ok);
|
||||
QDataStream ds2(&ba, QIODevice::ReadOnly);
|
||||
QHostAddress address2;
|
||||
ds2 >> address2;
|
||||
QCOMPARE(ds2.status(), QDataStream::Ok);
|
||||
QCOMPARE(address, address2);
|
||||
}
|
||||
|
||||
void tst_QHostAddress::parseSubnet_data()
|
||||
{
|
||||
QTest::addColumn<QString>("subnet");
|
||||
QTest::addColumn<QHostAddress>("prefix");
|
||||
QTest::addColumn<int>("prefixLength");
|
||||
|
||||
// invalid/error values
|
||||
QTest::newRow("empty") << QString() << QHostAddress() << -1;
|
||||
QTest::newRow("invalid_01") << "foobar" << QHostAddress() << -1;
|
||||
QTest::newRow("invalid_02") << " " << QHostAddress() << -1;
|
||||
QTest::newRow("invalid_03") << "1.2.3.a" << QHostAddress() << -1;
|
||||
QTest::newRow("invalid_04") << "1.2.3.4.5" << QHostAddress() << -1;
|
||||
QTest::newRow("invalid_05") << "1.2.3.4:80" << QHostAddress() << -1;
|
||||
QTest::newRow("invalid_06") << "1.2.3.4/33" << QHostAddress() << -1;
|
||||
QTest::newRow("invalid_07") << "1.2.3.4/-1" << QHostAddress() << -1;
|
||||
QTest::newRow("invalid_08") << "1.2.3.4/256.0.0.0" << QHostAddress() << -1;
|
||||
QTest::newRow("invalid_09") << "1.2.3.4/255.253.0.0" << QHostAddress() << -1;
|
||||
QTest::newRow("invalid_10") << "1.2.3.4/255.0.0.255" << QHostAddress() << -1;
|
||||
QTest::newRow("invalid_11") << "1.2.3.4." << QHostAddress() << -1;
|
||||
QTest::newRow("invalid_20") << "ffff::/-1" << QHostAddress() << -1;
|
||||
QTest::newRow("invalid_21") << "ffff::/129" << QHostAddress() << -1;
|
||||
QTest::newRow("invalid_22") << "ffff::/255.255.0.0" << QHostAddress() << -1;
|
||||
QTest::newRow("invalid_23") << "ffff::/ff00::" << QHostAddress() << -1;
|
||||
|
||||
// correct IPv4 with netmask
|
||||
QTest::newRow("netmask_0") << "0.0.0.0/0.0.0.0" << QHostAddress(QHostAddress::AnyIPv4) << 0;
|
||||
QTest::newRow("netmask_1") << "0.0.0.0/255.128.0.0" << QHostAddress(QHostAddress::AnyIPv4) << 9;
|
||||
QTest::newRow("netmask_2") << "0.0.0.0/255.192.0.0" << QHostAddress(QHostAddress::AnyIPv4) << 10;
|
||||
QTest::newRow("netmask_3") << "0.0.0.0/255.224.0.0" << QHostAddress(QHostAddress::AnyIPv4) << 11;
|
||||
QTest::newRow("netmask_4") << "0.0.0.0/255.240.0.0" << QHostAddress(QHostAddress::AnyIPv4) << 12;
|
||||
QTest::newRow("netmask_5") << "0.0.0.0/255.248.0.0" << QHostAddress(QHostAddress::AnyIPv4) << 13;
|
||||
QTest::newRow("netmask_6") << "0.0.0.0/255.252.0.0" << QHostAddress(QHostAddress::AnyIPv4) << 14;
|
||||
QTest::newRow("netmask_7") << "0.0.0.0/255.254.0.0" << QHostAddress(QHostAddress::AnyIPv4) << 15;
|
||||
QTest::newRow("netmask_8") << "0.0.0.0/255.255.0.0" << QHostAddress(QHostAddress::AnyIPv4) << 16;
|
||||
QTest::newRow("netmask_16") << "0.0.0.0/255.255.0.0" << QHostAddress(QHostAddress::AnyIPv4) << 16;
|
||||
QTest::newRow("netmask_24") << "0.0.0.0/255.255.255.0" << QHostAddress(QHostAddress::AnyIPv4) << 24;
|
||||
QTest::newRow("netmask_31") << "0.0.0.0/255.255.255.254" << QHostAddress(QHostAddress::AnyIPv4) << 31;
|
||||
QTest::newRow("netmask_32") << "0.0.0.0/255.255.255.255" << QHostAddress(QHostAddress::AnyIPv4) << 32;
|
||||
|
||||
// correct IPv4 with prefix
|
||||
QTest::newRow("prefix_0") << "0.0.0.0/0" << QHostAddress(QHostAddress::AnyIPv4) << 0;
|
||||
QTest::newRow("prefix_1") << "0.0.0.0/1" << QHostAddress(QHostAddress::AnyIPv4) << 1;
|
||||
QTest::newRow("prefix_9") << "0.0.0.0/9" << QHostAddress(QHostAddress::AnyIPv4) << 9;
|
||||
QTest::newRow("prefix_31") << "0.0.0.0/31" << QHostAddress(QHostAddress::AnyIPv4) << 31;
|
||||
QTest::newRow("prefix_32") << "0.0.0.0/32" << QHostAddress(QHostAddress::AnyIPv4) << 32;
|
||||
|
||||
// correct IPv4 without prefix or netmask
|
||||
QTest::newRow("classA") << "10" << QHostAddress("10.0.0.0") << 8;
|
||||
QTest::newRow("classA+dot") << "10." << QHostAddress("10.0.0.0") << 8;
|
||||
QTest::newRow("classB") << "172.16" << QHostAddress("172.16.0.0") << 16;
|
||||
QTest::newRow("classB+dot") << "172.16." << QHostAddress("172.16.0.0") << 16;
|
||||
QTest::newRow("classC") << "192.168.0" << QHostAddress("192.168.0.0") << 24;
|
||||
QTest::newRow("classC+dot") << "192.168.0" << QHostAddress("192.168.0.0") << 24;
|
||||
QTest::newRow("full-ipv4") << "192.168.0.1" << QHostAddress("192.168.0.1") << 32;
|
||||
|
||||
// correct IPv6 with prefix
|
||||
QTest::newRow("ipv6_01") << "::/0" << QHostAddress(QHostAddress::AnyIPv6) << 0;
|
||||
QTest::newRow("ipv6_03") << "::/3" << QHostAddress(QHostAddress::AnyIPv6) << 3;
|
||||
QTest::newRow("ipv6_16") << "::/16" << QHostAddress(QHostAddress::AnyIPv6) << 16;
|
||||
QTest::newRow("ipv6_48") << "::/48" << QHostAddress(QHostAddress::AnyIPv6) << 48;
|
||||
QTest::newRow("ipv6_127") << "::/127" << QHostAddress(QHostAddress::AnyIPv6) << 127;
|
||||
QTest::newRow("ipv6_128") << "::/128" << QHostAddress(QHostAddress::AnyIPv6) << 128;
|
||||
|
||||
// tail bit clearing:
|
||||
QTest::newRow("clear_01") << "255.255.255.255/31" << QHostAddress("255.255.255.254") << 31;
|
||||
QTest::newRow("clear_08") << "255.255.255.255/24" << QHostAddress("255.255.255.0") << 24;
|
||||
QTest::newRow("clear_09") << "255.255.255.255/23" << QHostAddress("255.255.254.0") << 23;
|
||||
QTest::newRow("clear_10") << "255.255.255.255/22" << QHostAddress("255.255.252.0") << 22;
|
||||
QTest::newRow("clear_11") << "255.255.255.255/21" << QHostAddress("255.255.248.0") << 21;
|
||||
QTest::newRow("clear_12") << "255.255.255.255/20" << QHostAddress("255.255.240.0") << 20;
|
||||
QTest::newRow("clear_13") << "255.255.255.255/19" << QHostAddress("255.255.224.0") << 19;
|
||||
QTest::newRow("clear_14") << "255.255.255.255/18" << QHostAddress("255.255.192.0") << 18;
|
||||
QTest::newRow("clear_15") << "255.255.255.255/17" << QHostAddress("255.255.128.0") << 17;
|
||||
QTest::newRow("clear_16") << "255.255.255.255/16" << QHostAddress("255.255.0.0") << 16;
|
||||
QTest::newRow("clear_24") << "255.255.255.255/8" << QHostAddress("255.0.0.0") << 8;
|
||||
QTest::newRow("clear_31") << "255.255.255.255/1" << QHostAddress("128.0.0.0") << 1;
|
||||
QTest::newRow("clear_32") << "255.255.255.255/0" << QHostAddress("0.0.0.0") << 0;
|
||||
|
||||
// same for IPv6:
|
||||
QTest::newRow("ipv6_clear_01") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/127"
|
||||
<< QHostAddress("ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe")
|
||||
<< 127;
|
||||
QTest::newRow("ipv6_clear_07") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/121"
|
||||
<< QHostAddress("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff80")
|
||||
<< 121;
|
||||
QTest::newRow("ipv6_clear_08") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/120"
|
||||
<< QHostAddress("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ff00")
|
||||
<< 120;
|
||||
QTest::newRow("ipv6_clear_16") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/112"
|
||||
<< QHostAddress("ffff:ffff:ffff:ffff:ffff:ffff:ffff:0")
|
||||
<< 112;
|
||||
QTest::newRow("ipv6_clear_80") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/48"
|
||||
<< QHostAddress("ffff:ffff:ffff::")
|
||||
<< 48;
|
||||
QTest::newRow("ipv6_clear_81") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/47"
|
||||
<< QHostAddress("ffff:ffff:fffe::")
|
||||
<< 47;
|
||||
QTest::newRow("ipv6_clear_82") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/46"
|
||||
<< QHostAddress("ffff:ffff:fffc::")
|
||||
<< 46;
|
||||
QTest::newRow("ipv6_clear_83") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/45"
|
||||
<< QHostAddress("ffff:ffff:fff8::")
|
||||
<< 45;
|
||||
QTest::newRow("ipv6_clear_84") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/44"
|
||||
<< QHostAddress("ffff:ffff:fff0::")
|
||||
<< 44;
|
||||
QTest::newRow("ipv6_clear_85") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/43"
|
||||
<< QHostAddress("ffff:ffff:ffe0::")
|
||||
<< 43;
|
||||
QTest::newRow("ipv6_clear_86") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/42"
|
||||
<< QHostAddress("ffff:ffff:ffc0::")
|
||||
<< 42;
|
||||
QTest::newRow("ipv6_clear_87") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/41"
|
||||
<< QHostAddress("ffff:ffff:ff80::")
|
||||
<< 41;
|
||||
QTest::newRow("ipv6_clear_88") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/40"
|
||||
<< QHostAddress("ffff:ffff:ff00::")
|
||||
<< 40;
|
||||
QTest::newRow("ipv6_clear_125") << "3fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/3"
|
||||
<< QHostAddress("2000::")
|
||||
<< 3;
|
||||
QTest::newRow("ipv6_clear_127") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/1"
|
||||
<< QHostAddress("8000::")
|
||||
<< 1;
|
||||
QTest::newRow("ipv6_clear_128") << "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/0"
|
||||
<< QHostAddress(QHostAddress::AnyIPv6)
|
||||
<< 0;
|
||||
}
|
||||
|
||||
void tst_QHostAddress::parseSubnet()
|
||||
{
|
||||
QFETCH(QString, subnet);
|
||||
QFETCH(QHostAddress, prefix);
|
||||
QFETCH(int, prefixLength);
|
||||
|
||||
QPair<QHostAddress, int> result = QHostAddress::parseSubnet(subnet);
|
||||
QCOMPARE(result.first, prefix);
|
||||
QCOMPARE(result.second, prefixLength);
|
||||
}
|
||||
|
||||
void tst_QHostAddress::isInSubnet_data()
|
||||
{
|
||||
QTest::addColumn<QHostAddress>("address");
|
||||
QTest::addColumn<QHostAddress>("prefix");
|
||||
QTest::addColumn<int>("prefixLength");
|
||||
QTest::addColumn<bool>("result");
|
||||
|
||||
// invalid QHostAddresses are never in any subnets
|
||||
QTest::newRow("invalid_01") << QHostAddress() << QHostAddress() << 32 << false;
|
||||
QTest::newRow("invalid_02") << QHostAddress() << QHostAddress(QHostAddress::AnyIPv4) << 32 << false;
|
||||
QTest::newRow("invalid_03") << QHostAddress() << QHostAddress(QHostAddress::AnyIPv4) << 8 << false;
|
||||
QTest::newRow("invalid_04") << QHostAddress() << QHostAddress(QHostAddress::AnyIPv4) << 0 << false;
|
||||
QTest::newRow("invalid_05") << QHostAddress() << QHostAddress("255.255.255.0") << 24 << false;
|
||||
QTest::newRow("invalid_06") << QHostAddress() << QHostAddress(QHostAddress::AnyIPv6) << 0 << false;
|
||||
QTest::newRow("invalid_07") << QHostAddress() << QHostAddress(QHostAddress::AnyIPv6) << 32 << false;
|
||||
QTest::newRow("invalid_08") << QHostAddress() << QHostAddress(QHostAddress::AnyIPv6) << 128<< false;
|
||||
|
||||
// and no host address can be in a subnet whose prefix is invalid
|
||||
QTest::newRow("invalid_20") << QHostAddress(QHostAddress::AnyIPv4) << QHostAddress() << 16 << false;
|
||||
QTest::newRow("invalid_21") << QHostAddress(QHostAddress::AnyIPv6) << QHostAddress() << 16 << false;
|
||||
QTest::newRow("invalid_22") << QHostAddress(QHostAddress::LocalHost) << QHostAddress() << 16 << false;
|
||||
QTest::newRow("invalid_23") << QHostAddress(QHostAddress::LocalHostIPv6) << QHostAddress() << 16 << false;
|
||||
|
||||
// negative netmasks don't make sense:
|
||||
QTest::newRow("invalid_30") << QHostAddress(QHostAddress::AnyIPv4) << QHostAddress(QHostAddress::Any) << -1 << false;
|
||||
QTest::newRow("invalid_31") << QHostAddress(QHostAddress::AnyIPv6) << QHostAddress(QHostAddress::AnyIPv6) << -1 << false;
|
||||
|
||||
// we don't support IPv4 belonging in an IPv6 netmask and vice-versa
|
||||
QTest::newRow("v4-in-v6") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::AnyIPv6) << 0 << false;
|
||||
QTest::newRow("v6-in-v4") << QHostAddress(QHostAddress::LocalHostIPv6) << QHostAddress(QHostAddress::Any) << 0 << false;
|
||||
QTest::newRow("v4-in-v6mapped") << QHostAddress(QHostAddress::LocalHost) << QHostAddress("ffff:ffff:ffff:ffff:ffff:ffff:255.0.0.0") << 113 << false;
|
||||
QTest::newRow("v4-in-v6mapped2") << QHostAddress(QHostAddress::LocalHost) << QHostAddress("::ffff:255.0.0.0") << 113 << false;
|
||||
|
||||
// IPv4 correct ones
|
||||
QTest::newRow("netmask_0") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::AnyIPv4) << 0 << true;
|
||||
QTest::newRow("netmask_0bis") << QHostAddress(QHostAddress::LocalHost) << QHostAddress("255.255.0.0") << 0 << true;
|
||||
QTest::newRow("netmask_0ter") << QHostAddress(QHostAddress::LocalHost) << QHostAddress("1.2.3.4") << 0 << true;
|
||||
QTest::newRow("netmask_1") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::AnyIPv4) << 1 << true;
|
||||
QTest::newRow("~netmask_1") << QHostAddress(QHostAddress::LocalHost) << QHostAddress("128.0.0.0") << 1 << false;
|
||||
QTest::newRow("netmask_1bis") << QHostAddress("224.0.0.1") << QHostAddress("128.0.0.0") << 1 << true;
|
||||
QTest::newRow("~netmask_1bis") << QHostAddress("224.0.0.1") << QHostAddress("0.0.0.0") << 1 << false;
|
||||
QTest::newRow("netmask_8") << QHostAddress(QHostAddress::LocalHost) << QHostAddress("127.0.0.0") << 8 << true;
|
||||
QTest::newRow("~netmask_8") << QHostAddress(QHostAddress::LocalHost) << QHostAddress("126.0.0.0") << 8 << false;
|
||||
QTest::newRow("netmask_15") << QHostAddress("10.0.1.255") << QHostAddress("10.0.0.0") << 15 << true;
|
||||
QTest::newRow("netmask_16") << QHostAddress("172.16.0.1") << QHostAddress("172.16.0.0") << 16 << true;
|
||||
|
||||
// the address is always in the subnet containing its address, regardless of length:
|
||||
QTest::newRow("same_01") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::LocalHost) << 1 << true;
|
||||
QTest::newRow("same_07") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::LocalHost) << 7 << true;
|
||||
QTest::newRow("same_8") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::LocalHost) << 8 << true;
|
||||
QTest::newRow("same_24") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::LocalHost) << 23 << true;
|
||||
QTest::newRow("same_31") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::LocalHost) << 31 << true;
|
||||
QTest::newRow("same_32") << QHostAddress(QHostAddress::LocalHost) << QHostAddress(QHostAddress::LocalHost) << 32 << true;
|
||||
|
||||
// IPv6 correct ones:
|
||||
QTest::newRow("ipv6_netmask_0") << QHostAddress(QHostAddress::LocalHostIPv6) << QHostAddress(QHostAddress::AnyIPv6) << 0 << true;
|
||||
QTest::newRow("ipv6_netmask_0bis") << QHostAddress(QHostAddress::LocalHostIPv6) << QHostAddress(QHostAddress::LocalHostIPv6) << 0 << true;
|
||||
QTest::newRow("ipv6_netmask_0ter") << QHostAddress(QHostAddress::LocalHostIPv6) << QHostAddress("ffff::") << 0 << true;
|
||||
QTest::newRow("ipv6_netmask_1") << QHostAddress(QHostAddress::LocalHostIPv6) << QHostAddress(QHostAddress::AnyIPv6) << 1 << true;
|
||||
QTest::newRow("ipv6_netmask_1bis") << QHostAddress("fec0::1") << QHostAddress("8000::") << 1 << true;
|
||||
QTest::newRow("~ipv6_netmask_1") << QHostAddress(QHostAddress::LocalHostIPv6) << QHostAddress("8000::") << 1 << false;
|
||||
QTest::newRow("~ipv6_netmask_1bis") << QHostAddress("fec0::1") << QHostAddress("::") << 1 << false;
|
||||
QTest::newRow("ipv6_netmask_47") << QHostAddress("2:3:5::1") << QHostAddress("2:3:4::") << 47 << true;
|
||||
QTest::newRow("ipv6_netmask_48") << QHostAddress("2:3:4::1") << QHostAddress("2:3:4::") << 48 << true;
|
||||
QTest::newRow("~ipv6_netmask_48") << QHostAddress("2:3:5::1") << QHostAddress("2:3:4::") << 48 << false;
|
||||
QTest::newRow("ipv6_netmask_127") << QHostAddress("2:3:4:5::1") << QHostAddress("2:3:4:5::") << 127 << true;
|
||||
QTest::newRow("ipv6_netmask_128") << QHostAddress("2:3:4:5::1") << QHostAddress("2:3:4:5::1") << 128 << true;
|
||||
QTest::newRow("~ipv6_netmask_128") << QHostAddress("2:3:4:5::1") << QHostAddress("2:3:4:5::0") << 128 << false;
|
||||
}
|
||||
|
||||
void tst_QHostAddress::isInSubnet()
|
||||
{
|
||||
QFETCH(QHostAddress, address);
|
||||
QFETCH(QHostAddress, prefix);
|
||||
QFETCH(int, prefixLength);
|
||||
|
||||
QTEST(address.isInSubnet(prefix, prefixLength), "result");
|
||||
}
|
||||
|
||||
void tst_QHostAddress::classification_data()
|
||||
{
|
||||
QTest::addColumn<QHostAddress>("address");
|
||||
QTest::addColumn<AddressClassification>("result");
|
||||
|
||||
QTest::newRow("default") << QHostAddress() << UnknownAddress;
|
||||
QTest::newRow("invalid") << QHostAddress("&&&") << UnknownAddress;
|
||||
|
||||
QTest::newRow("Any") << QHostAddress(QHostAddress::Any) << LocalNetAddress;
|
||||
QTest::newRow("Null") << QHostAddress(QHostAddress::Null) << UnknownAddress;
|
||||
|
||||
// IPv6 address space
|
||||
auto addV6 = [](const char *str, AddressClassification cl) {
|
||||
QTest::newRow(str) << QHostAddress(str) << cl;
|
||||
};
|
||||
QTest::newRow("AnyIPv6") << QHostAddress(QHostAddress::AnyIPv6) << LocalNetAddress;
|
||||
QTest::newRow("ipv6_loop") << QHostAddress(QHostAddress::LocalHostIPv6) << LoopbackAddress;
|
||||
addV6("::", LocalNetAddress);
|
||||
addV6("::1", LoopbackAddress);
|
||||
addV6("::2", GlobalAddress);
|
||||
addV6("2000::", GlobalAddress);
|
||||
addV6("3fff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", GlobalAddress);
|
||||
addV6("fbff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", GlobalAddress);
|
||||
addV6("fc00::", UniqueLocalAddress);
|
||||
addV6("fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", UniqueLocalAddress);
|
||||
addV6("fe00::", UnknownAddress);
|
||||
addV6("fe7f:ffff:ffff:ffff:ffff:ffff:ffff:ffff", UnknownAddress);
|
||||
addV6("fe80::", LinkLocalAddress);
|
||||
addV6("febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff", LinkLocalAddress);
|
||||
addV6("fec0::", SiteLocalAddress);
|
||||
addV6("feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", SiteLocalAddress);
|
||||
addV6("ff00::", MulticastAddress);
|
||||
addV6("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", MulticastAddress);
|
||||
|
||||
// IPv4 address space
|
||||
auto addV4 = [](const char *str, AddressClassification cl) {
|
||||
QTest::newRow(str) << QHostAddress(str) << cl;
|
||||
QByteArray v6 = "::ffff:";
|
||||
v6 += str;
|
||||
QTest::newRow(v6.constData()) << QHostAddress(QString::fromLatin1(v6)) << cl;
|
||||
};
|
||||
QTest::newRow("AnyIPv4") << QHostAddress(QHostAddress::AnyIPv4) << LocalNetAddress;
|
||||
QTest::newRow("ipv4_loop") << QHostAddress(QHostAddress::LocalHost) << LoopbackAddress;
|
||||
QTest::newRow("Broadcast") << QHostAddress(QHostAddress::Broadcast) << BroadcastAddress;
|
||||
addV4("0.0.0.0", LocalNetAddress);
|
||||
addV4("0.0.0.1", LocalNetAddress);
|
||||
addV4("0.255.255.255", LocalNetAddress);
|
||||
addV4("1.0.0.0", GlobalAddress);
|
||||
addV4("1.2.3.4", GlobalAddress);
|
||||
addV4("10.0.0.4", PrivateNetworkAddress);
|
||||
addV4("127.0.0.1", LoopbackAddress);
|
||||
addV4("127.0.0.2", LoopbackAddress);
|
||||
addV4("127.255.255.255", LoopbackAddress);
|
||||
addV4("192.168.3.4", PrivateNetworkAddress);
|
||||
addV4("223.255.255.255", GlobalAddress);
|
||||
addV4("224.0.0.0", MulticastAddress);
|
||||
addV4("239.255.255.255", MulticastAddress);
|
||||
addV4("240.0.0.0", UnknownAddress);
|
||||
addV4("255.255.255.254", UnknownAddress);
|
||||
addV4("255.255.255.255", BroadcastAddress);
|
||||
}
|
||||
|
||||
void tst_QHostAddress::classification()
|
||||
{
|
||||
QFETCH(QHostAddress, address);
|
||||
QFETCH(AddressClassification, result);
|
||||
|
||||
bool isLoopback = (result == LoopbackAddress);
|
||||
bool isGlobal = (result & GlobalAddress); // GlobalAddress is a bit
|
||||
bool isLinkLocal = (result == LinkLocalAddress);
|
||||
bool isSiteLocal = (result == SiteLocalAddress);
|
||||
bool isUniqueLocalAddress = (result == UniqueLocalAddress);
|
||||
bool isMulticast = (result == MulticastAddress);
|
||||
bool isBroadcast = (result == BroadcastAddress);
|
||||
|
||||
QCOMPARE(address.isLoopback(), isLoopback);
|
||||
QCOMPARE(address.isGlobal(), isGlobal);
|
||||
QCOMPARE(address.isLinkLocal(), isLinkLocal);
|
||||
QCOMPARE(address.isSiteLocal(), isSiteLocal);
|
||||
QCOMPARE(address.isUniqueLocalUnicast(), isUniqueLocalAddress);
|
||||
QCOMPARE(address.isMulticast(), isMulticast);
|
||||
QCOMPARE(address.isBroadcast(), isBroadcast);
|
||||
}
|
||||
|
||||
void tst_QHostAddress::convertv4v6_data()
|
||||
{
|
||||
QTest::addColumn<QHostAddress>("source");
|
||||
QTest::addColumn<int>("protocol");
|
||||
QTest::addColumn<QHostAddress>("result");
|
||||
|
||||
QTest::newRow("any-to-v4") << QHostAddress(QHostAddress::Any) << 4 << QHostAddress(QHostAddress::AnyIPv4);
|
||||
QTest::newRow("any-to-v6") << QHostAddress(QHostAddress::Any) << 6 << QHostAddress(QHostAddress::AnyIPv6);
|
||||
QTest::newRow("anyv4-to-v6") << QHostAddress(QHostAddress::AnyIPv4) << 6 << QHostAddress(QHostAddress::AnyIPv6);
|
||||
QTest::newRow("anyv6-to-v4") << QHostAddress(QHostAddress::AnyIPv6) << 4 << QHostAddress(QHostAddress::AnyIPv4);
|
||||
|
||||
QTest::newRow("v4mapped-to-v4") << QHostAddress("::ffff:192.0.2.1") << 4 << QHostAddress("192.0.2.1");
|
||||
QTest::newRow("v4-to-v4mapped") << QHostAddress("192.0.2.1") << 6 << QHostAddress("::ffff:192.0.2.1");
|
||||
|
||||
// we won't convert 127.0.0.1 to ::1 or vice-versa:
|
||||
// you can connect to a v4 server socket with ::ffff:127.0.0.1, but not with ::1
|
||||
QTest::newRow("localhost-to-v4mapped") << QHostAddress(QHostAddress::LocalHost) << 6 << QHostAddress("::ffff:127.0.0.1");
|
||||
QTest::newRow("v4mapped-to-localhost") << QHostAddress("::ffff:127.0.0.1") << 4 << QHostAddress(QHostAddress::LocalHost);
|
||||
|
||||
// in turn, that means localhost6 doesn't convert to v4
|
||||
QTest::newRow("localhost6-to-v4") << QHostAddress(QHostAddress::LocalHostIPv6) << 4 << QHostAddress();
|
||||
|
||||
// some other v6 addresses that won't convert to v4
|
||||
QTest::newRow("v4compat-to-v4") << QHostAddress("::192.0.2.1") << 4 << QHostAddress();
|
||||
QTest::newRow("localhostv4compat-to-v4") << QHostAddress("::127.0.0.1") << 4 << QHostAddress();
|
||||
QTest::newRow("v6global-to-v4") << QHostAddress("2001:db8::1") << 4 << QHostAddress();
|
||||
QTest::newRow("v6multicast-to-v4") << QHostAddress("ff02::1") << 4 << QHostAddress();
|
||||
}
|
||||
|
||||
void tst_QHostAddress::convertv4v6()
|
||||
{
|
||||
QFETCH(QHostAddress, source);
|
||||
QFETCH(int, protocol);
|
||||
QFETCH(QHostAddress, result);
|
||||
|
||||
if (protocol == 4) {
|
||||
bool ok;
|
||||
quint32 v4 = source.toIPv4Address(&ok);
|
||||
QCOMPARE(ok, result.protocol() == QHostAddress::IPv4Protocol);
|
||||
if (ok)
|
||||
QCOMPARE(QHostAddress(v4), result);
|
||||
} else if (protocol == 6) {
|
||||
QCOMPARE(QHostAddress(source.toIPv6Address()), result);
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QHostAddress)
|
||||
#include "tst_qhostaddress.moc"
|
6
tests/auto/network/kernel/qhostinfo/BLACKLIST
Normal file
6
tests/auto/network/kernel/qhostinfo/BLACKLIST
Normal file
@ -0,0 +1,6 @@
|
||||
# These tests fail due to a DNS server issue
|
||||
# (this is not a Qt bug)
|
||||
[lookupIPv6:a-plus-aaaa]
|
||||
windows ci
|
||||
[blockingLookup:a-plus-aaaa]
|
||||
windows ci
|
26
tests/auto/network/kernel/qhostinfo/CMakeLists.txt
Normal file
26
tests/auto/network/kernel/qhostinfo/CMakeLists.txt
Normal file
@ -0,0 +1,26 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
if(NOT QT_FEATURE_private_tests)
|
||||
return()
|
||||
endif()
|
||||
|
||||
#####################################################################
|
||||
## tst_qhostinfo Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qhostinfo
|
||||
SOURCES
|
||||
tst_qhostinfo.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::NetworkPrivate
|
||||
)
|
||||
|
||||
## Scopes:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_extend_target(tst_qhostinfo CONDITION WIN32
|
||||
LIBRARIES
|
||||
ws2_32
|
||||
)
|
713
tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp
Normal file
713
tests/auto/network/kernel/qhostinfo/tst_qhostinfo.cpp
Normal file
@ -0,0 +1,713 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// Copyright (C) 2016 Intel Corporation.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
|
||||
// When using WinSock2 on Windows, it's the first thing that can be included
|
||||
// (except qglobal.h), or else you'll get tons of compile errors
|
||||
#include <qglobal.h>
|
||||
|
||||
// To prevent windows system header files from re-defining min/max
|
||||
#define NOMINMAX 1
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
# include <winsock2.h>
|
||||
# include <ws2tcpip.h>
|
||||
#endif
|
||||
|
||||
#include <QTest>
|
||||
#include <QTestEventLoop>
|
||||
#include <QProcess>
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QTcpSocket>
|
||||
#include <QTcpServer>
|
||||
|
||||
#include <private/qthread_p.h>
|
||||
|
||||
#include <time.h>
|
||||
#if defined(Q_OS_WIN)
|
||||
#include <qt_windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#endif
|
||||
|
||||
#include <qhostinfo.h>
|
||||
#include "private/qhostinfo_p.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#if defined(Q_OS_UNIX)
|
||||
# include <sys/socket.h>
|
||||
# include <netdb.h>
|
||||
#endif
|
||||
|
||||
#include "../../../network-settings.h"
|
||||
|
||||
#define TEST_DOMAIN ".test.qt-project.org"
|
||||
|
||||
|
||||
class tst_QHostInfo : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void init();
|
||||
void initTestCase();
|
||||
void swapFunction();
|
||||
void moveOperator();
|
||||
void getSetCheck();
|
||||
void staticInformation();
|
||||
void lookupIPv4_data();
|
||||
void lookupIPv4();
|
||||
void lookupIPv6_data();
|
||||
void lookupIPv6();
|
||||
void lookupConnectToFunctionPointer_data();
|
||||
void lookupConnectToFunctionPointer();
|
||||
void lookupConnectToFunctionPointerDeleted();
|
||||
void lookupConnectToLambda_data();
|
||||
void lookupConnectToLambda();
|
||||
void reverseLookup_data();
|
||||
void reverseLookup();
|
||||
|
||||
void blockingLookup_data();
|
||||
void blockingLookup();
|
||||
|
||||
void raceCondition();
|
||||
void threadSafety();
|
||||
void threadSafetyAsynchronousAPI();
|
||||
|
||||
void multipleSameLookups();
|
||||
void multipleDifferentLookups_data();
|
||||
void multipleDifferentLookups();
|
||||
|
||||
void cache();
|
||||
|
||||
void abortHostLookup();
|
||||
protected slots:
|
||||
void resultsReady(const QHostInfo &);
|
||||
|
||||
private:
|
||||
bool ipv6LookupsAvailable;
|
||||
bool ipv6Available;
|
||||
bool lookupDone;
|
||||
int lookupsDoneCounter;
|
||||
QHostInfo lookupResults;
|
||||
};
|
||||
|
||||
void tst_QHostInfo::swapFunction()
|
||||
{
|
||||
QHostInfo obj1, obj2;
|
||||
obj1.setError(QHostInfo::HostInfoError(0));
|
||||
obj2.setError(QHostInfo::HostInfoError(1));
|
||||
obj1.swap(obj2);
|
||||
QCOMPARE(QHostInfo::HostInfoError(0), obj2.error());
|
||||
QCOMPARE(QHostInfo::HostInfoError(1), obj1.error());
|
||||
}
|
||||
|
||||
void tst_QHostInfo::moveOperator()
|
||||
{
|
||||
QHostInfo obj1, obj2, obj3(1);
|
||||
obj1.setError(QHostInfo::HostInfoError(0));
|
||||
obj2.setError(QHostInfo::HostInfoError(1));
|
||||
obj1 = std::move(obj2);
|
||||
obj2 = obj3;
|
||||
QCOMPARE(QHostInfo::HostInfoError(1), obj1.error());
|
||||
QCOMPARE(obj3.lookupId(), obj2.lookupId());
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Testing get/set functions
|
||||
void tst_QHostInfo::getSetCheck()
|
||||
{
|
||||
QHostInfo obj1;
|
||||
// HostInfoError QHostInfo::error()
|
||||
// void QHostInfo::setError(HostInfoError)
|
||||
obj1.setError(QHostInfo::HostInfoError(0));
|
||||
QCOMPARE(QHostInfo::HostInfoError(0), obj1.error());
|
||||
obj1.setError(QHostInfo::HostInfoError(1));
|
||||
QCOMPARE(QHostInfo::HostInfoError(1), obj1.error());
|
||||
|
||||
// int QHostInfo::lookupId()
|
||||
// void QHostInfo::setLookupId(int)
|
||||
obj1.setLookupId(0);
|
||||
QCOMPARE(0, obj1.lookupId());
|
||||
obj1.setLookupId(INT_MIN);
|
||||
QCOMPARE(INT_MIN, obj1.lookupId());
|
||||
obj1.setLookupId(INT_MAX);
|
||||
QCOMPARE(INT_MAX, obj1.lookupId());
|
||||
}
|
||||
|
||||
void tst_QHostInfo::staticInformation()
|
||||
{
|
||||
qDebug() << "Hostname:" << QHostInfo::localHostName();
|
||||
qDebug() << "Domain name:" << QHostInfo::localDomainName();
|
||||
}
|
||||
|
||||
void tst_QHostInfo::initTestCase()
|
||||
{
|
||||
ipv6Available = false;
|
||||
ipv6LookupsAvailable = false;
|
||||
|
||||
QTcpServer server;
|
||||
if (server.listen(QHostAddress("::1"))) {
|
||||
// We have IPv6 support
|
||||
ipv6Available = true;
|
||||
}
|
||||
|
||||
// check if the system getaddrinfo can do IPv6 lookups
|
||||
struct addrinfo hint, *result = 0;
|
||||
memset(&hint, 0, sizeof hint);
|
||||
hint.ai_family = AF_UNSPEC;
|
||||
#ifdef AI_ADDRCONFIG
|
||||
hint.ai_flags = AI_ADDRCONFIG;
|
||||
#endif
|
||||
|
||||
int res = getaddrinfo("::1", "80", &hint, &result);
|
||||
if (res == 0) {
|
||||
// this test worked
|
||||
freeaddrinfo(result);
|
||||
res = getaddrinfo("aaaa-single" TEST_DOMAIN, "80", &hint, &result);
|
||||
if (res == 0 && result != 0 && result->ai_family != AF_INET) {
|
||||
freeaddrinfo(result);
|
||||
ipv6LookupsAvailable = true;
|
||||
}
|
||||
}
|
||||
|
||||
// run each testcase with and without test enabled
|
||||
QTest::addColumn<bool>("cache");
|
||||
QTest::newRow("WithCache") << true;
|
||||
QTest::newRow("WithoutCache") << false;
|
||||
}
|
||||
|
||||
void tst_QHostInfo::init()
|
||||
{
|
||||
// delete the cache so inidividual testcase results are independent from each other
|
||||
qt_qhostinfo_clear_cache();
|
||||
|
||||
QFETCH_GLOBAL(bool, cache);
|
||||
qt_qhostinfo_enable_cache(cache);
|
||||
}
|
||||
|
||||
void tst_QHostInfo::lookupIPv4_data()
|
||||
{
|
||||
QTest::addColumn<QString>("hostname");
|
||||
QTest::addColumn<QString>("addresses");
|
||||
QTest::addColumn<int>("err");
|
||||
|
||||
QTest::newRow("empty") << "" << "" << int(QHostInfo::HostNotFound);
|
||||
|
||||
QTest::newRow("single_ip4") << "a-single" TEST_DOMAIN << "192.0.2.1" << int(QHostInfo::NoError);
|
||||
QTest::newRow("multiple_ip4") << "a-multi" TEST_DOMAIN << "192.0.2.1 192.0.2.2 192.0.2.3" << int(QHostInfo::NoError);
|
||||
QTest::newRow("literal_ip4") << "192.0.2.1" << "192.0.2.1" << int(QHostInfo::NoError);
|
||||
|
||||
QTest::newRow("notfound") << "invalid" TEST_DOMAIN << "" << int(QHostInfo::HostNotFound);
|
||||
|
||||
QTest::newRow("idn-ace") << "a-single.xn--alqualond-34a" TEST_DOMAIN << "192.0.2.1" << int(QHostInfo::NoError);
|
||||
QTest::newRow("idn-unicode") << QString::fromLatin1("a-single.alqualond\353" TEST_DOMAIN) << "192.0.2.1" << int(QHostInfo::NoError);
|
||||
}
|
||||
|
||||
void tst_QHostInfo::lookupIPv4()
|
||||
{
|
||||
QFETCH(QString, hostname);
|
||||
QFETCH(int, err);
|
||||
QFETCH(QString, addresses);
|
||||
|
||||
lookupDone = false;
|
||||
QHostInfo::lookupHost(hostname, this, SLOT(resultsReady(QHostInfo)));
|
||||
|
||||
QTestEventLoop::instance().enterLoop(10);
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
QVERIFY(lookupDone);
|
||||
|
||||
if ((int)lookupResults.error() != (int)err) {
|
||||
qWarning() << hostname << "=>" << lookupResults.errorString();
|
||||
}
|
||||
QCOMPARE((int)lookupResults.error(), (int)err);
|
||||
|
||||
QStringList tmp;
|
||||
for (int i = 0; i < lookupResults.addresses().size(); ++i)
|
||||
tmp.append(lookupResults.addresses().at(i).toString());
|
||||
tmp.sort();
|
||||
|
||||
QStringList expected = addresses.split(' ');
|
||||
expected.sort();
|
||||
|
||||
QCOMPARE(tmp.join(' '), expected.join(' '));
|
||||
}
|
||||
|
||||
void tst_QHostInfo::lookupIPv6_data()
|
||||
{
|
||||
QTest::addColumn<QString>("hostname");
|
||||
QTest::addColumn<QString>("addresses");
|
||||
QTest::addColumn<int>("err");
|
||||
|
||||
QTest::newRow("aaaa-single") << "aaaa-single" TEST_DOMAIN << "2001:db8::1" << int(QHostInfo::NoError);
|
||||
QTest::newRow("aaaa-multi") << "aaaa-multi" TEST_DOMAIN << "2001:db8::1 2001:db8::2 2001:db8::3" << int(QHostInfo::NoError);
|
||||
QTest::newRow("a-plus-aaaa") << "a-plus-aaaa" TEST_DOMAIN << "198.51.100.1 2001:db8::1:1" << int(QHostInfo::NoError);
|
||||
|
||||
// avoid using real IPv6 addresses here because this will do a DNS query
|
||||
// real addresses are between 2000:: and 3fff:ffff:ffff:ffff:ffff:ffff:ffff
|
||||
QTest::newRow("literal_ip6") << "f001:6b0:1:ea:202:a5ff:fecd:13a6" << "f001:6b0:1:ea:202:a5ff:fecd:13a6" << int(QHostInfo::NoError);
|
||||
QTest::newRow("literal_shortip6") << "f001:618:1401::4" << "f001:618:1401::4" << int(QHostInfo::NoError);
|
||||
}
|
||||
|
||||
void tst_QHostInfo::lookupIPv6()
|
||||
{
|
||||
QFETCH(QString, hostname);
|
||||
QFETCH(int, err);
|
||||
QFETCH(QString, addresses);
|
||||
|
||||
if (!ipv6LookupsAvailable)
|
||||
QSKIP("This platform does not support IPv6 lookups");
|
||||
|
||||
lookupDone = false;
|
||||
QHostInfo::lookupHost(hostname, this, SLOT(resultsReady(QHostInfo)));
|
||||
|
||||
QTestEventLoop::instance().enterLoop(10);
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
QVERIFY(lookupDone);
|
||||
|
||||
QCOMPARE((int)lookupResults.error(), (int)err);
|
||||
|
||||
QStringList tmp;
|
||||
for (int i = 0; i < lookupResults.addresses().size(); ++i)
|
||||
tmp.append(lookupResults.addresses().at(i).toString());
|
||||
tmp.sort();
|
||||
|
||||
QStringList expected = addresses.split(' ');
|
||||
expected.sort();
|
||||
|
||||
QCOMPARE(tmp.join(' ').toLower(), expected.join(' ').toLower());
|
||||
}
|
||||
|
||||
void tst_QHostInfo::lookupConnectToFunctionPointer_data()
|
||||
{
|
||||
lookupIPv4_data();
|
||||
}
|
||||
|
||||
void tst_QHostInfo::lookupConnectToFunctionPointer()
|
||||
{
|
||||
QFETCH(QString, hostname);
|
||||
QFETCH(int, err);
|
||||
QFETCH(QString, addresses);
|
||||
|
||||
lookupDone = false;
|
||||
QHostInfo::lookupHost(hostname, this, &tst_QHostInfo::resultsReady);
|
||||
|
||||
QTestEventLoop::instance().enterLoop(10);
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
QVERIFY(lookupDone);
|
||||
|
||||
if (int(lookupResults.error()) != int(err))
|
||||
qWarning() << hostname << "=>" << lookupResults.errorString();
|
||||
QCOMPARE(int(lookupResults.error()), int(err));
|
||||
|
||||
QStringList tmp;
|
||||
for (const auto &result : lookupResults.addresses())
|
||||
tmp.append(result.toString());
|
||||
tmp.sort();
|
||||
|
||||
QStringList expected = addresses.split(' ');
|
||||
expected.sort();
|
||||
|
||||
QCOMPARE(tmp.join(' '), expected.join(' '));
|
||||
}
|
||||
|
||||
void tst_QHostInfo::lookupConnectToFunctionPointerDeleted()
|
||||
{
|
||||
{
|
||||
QObject contextObject;
|
||||
QHostInfo::lookupHost("localhost", &contextObject, [](const QHostInfo){
|
||||
QFAIL("This should never be called!");
|
||||
});
|
||||
}
|
||||
QTestEventLoop::instance().enterLoop(3);
|
||||
}
|
||||
|
||||
void tst_QHostInfo::lookupConnectToLambda_data()
|
||||
{
|
||||
lookupIPv4_data();
|
||||
}
|
||||
|
||||
void tst_QHostInfo::lookupConnectToLambda()
|
||||
{
|
||||
QFETCH(QString, hostname);
|
||||
QFETCH(int, err);
|
||||
QFETCH(QString, addresses);
|
||||
|
||||
lookupDone = false;
|
||||
QHostInfo::lookupHost(hostname, [this](const QHostInfo &hostInfo) {
|
||||
resultsReady(hostInfo);
|
||||
});
|
||||
|
||||
QTestEventLoop::instance().enterLoop(10);
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
QVERIFY(lookupDone);
|
||||
|
||||
if (int(lookupResults.error()) != int(err))
|
||||
qWarning() << hostname << "=>" << lookupResults.errorString();
|
||||
QCOMPARE(int(lookupResults.error()), int(err));
|
||||
|
||||
QStringList tmp;
|
||||
for (int i = 0; i < lookupResults.addresses().size(); ++i)
|
||||
tmp.append(lookupResults.addresses().at(i).toString());
|
||||
tmp.sort();
|
||||
|
||||
QStringList expected = addresses.split(' ');
|
||||
expected.sort();
|
||||
|
||||
QCOMPARE(tmp.join(' '), expected.join(' '));
|
||||
}
|
||||
|
||||
static QStringList reverseLookupHelper(const QString &ip)
|
||||
{
|
||||
QStringList results;
|
||||
|
||||
const QString pythonCode =
|
||||
"import socket;"
|
||||
"import sys;"
|
||||
"print (socket.getnameinfo((sys.argv[1], 0), 0)[0]);";
|
||||
|
||||
QList<QByteArray> lines;
|
||||
QProcess python;
|
||||
python.setProcessChannelMode(QProcess::ForwardedErrorChannel);
|
||||
python.start("python", QStringList() << QString("-c") << pythonCode << ip);
|
||||
if (python.waitForFinished()) {
|
||||
if (python.exitStatus() == QProcess::NormalExit && python.exitCode() == 0)
|
||||
lines = python.readAllStandardOutput().split('\n');
|
||||
for (QByteArray line : lines) {
|
||||
if (!line.isEmpty())
|
||||
results << line.trimmed();
|
||||
}
|
||||
if (!results.isEmpty())
|
||||
return results;
|
||||
}
|
||||
|
||||
qDebug() << "Python failed, falling back to nslookup";
|
||||
QProcess lookup;
|
||||
lookup.setProcessChannelMode(QProcess::ForwardedErrorChannel);
|
||||
lookup.start("nslookup", QStringList(ip));
|
||||
if (!lookup.waitForFinished()) {
|
||||
results << "nslookup failure";
|
||||
qDebug() << "nslookup failure";
|
||||
return results;
|
||||
}
|
||||
lines = lookup.readAllStandardOutput().split('\n');
|
||||
|
||||
QByteArray name;
|
||||
|
||||
const QByteArray nameMarkerNix("name =");
|
||||
const QByteArray nameMarkerWin("Name:");
|
||||
const QByteArray addressMarkerWin("Address:");
|
||||
|
||||
for (QByteArray line : lines) {
|
||||
int index = -1;
|
||||
if ((index = line.indexOf(nameMarkerNix)) != -1) { // Linux and macOS
|
||||
name = line.mid(index + nameMarkerNix.size()).chopped(1).trimmed();
|
||||
results << name;
|
||||
} else if (line.startsWith(nameMarkerWin)) { // Windows formatting
|
||||
name = line.mid(line.lastIndexOf(" ")).trimmed();
|
||||
} else if (line.startsWith(addressMarkerWin)) {
|
||||
QByteArray address = line.mid(addressMarkerWin.size()).trimmed();
|
||||
if (address == ip.toUtf8()) {
|
||||
results << name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (results.isEmpty()) {
|
||||
qDebug() << "Failure to parse nslookup output: " << lines;
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
void tst_QHostInfo::reverseLookup_data()
|
||||
{
|
||||
QTest::addColumn<QString>("address");
|
||||
QTest::addColumn<QStringList>("hostNames");
|
||||
QTest::addColumn<int>("err");
|
||||
QTest::addColumn<bool>("ipv6");
|
||||
|
||||
QTest::newRow("dns.google") << QString("8.8.8.8") << reverseLookupHelper("8.8.8.8") << 0 << false;
|
||||
QTest::newRow("one.one.one.one") << QString("1.1.1.1") << reverseLookupHelper("1.1.1.1") << 0 << false;
|
||||
QTest::newRow("dns.google IPv6") << QString("2001:4860:4860::8888") << reverseLookupHelper("2001:4860:4860::8888") << 0 << true;
|
||||
QTest::newRow("cloudflare IPv6") << QString("2606:4700:4700::1111") << reverseLookupHelper("2606:4700:4700::1111") << 0 << true;
|
||||
QTest::newRow("bogus-name IPv6") << QString("1::2::3::4") << QStringList() << 1 << true;
|
||||
}
|
||||
|
||||
void tst_QHostInfo::reverseLookup()
|
||||
{
|
||||
QFETCH(QString, address);
|
||||
QFETCH(QStringList, hostNames);
|
||||
QFETCH(int, err);
|
||||
QFETCH(bool, ipv6);
|
||||
|
||||
if (ipv6 && !ipv6LookupsAvailable) {
|
||||
QSKIP("IPv6 reverse lookups are not supported on this platform");
|
||||
}
|
||||
|
||||
QHostInfo info = QHostInfo::fromName(address);
|
||||
|
||||
if (err == 0) {
|
||||
if (!hostNames.contains(info.hostName()))
|
||||
qDebug() << "Failure: expecting" << hostNames << ",got " << info.hostName();
|
||||
QVERIFY(hostNames.contains(info.hostName()));
|
||||
QCOMPARE(info.addresses().first(), QHostAddress(address));
|
||||
} else {
|
||||
QCOMPARE(info.hostName(), address);
|
||||
QCOMPARE(info.error(), QHostInfo::HostNotFound);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void tst_QHostInfo::blockingLookup_data()
|
||||
{
|
||||
lookupIPv4_data();
|
||||
if (ipv6LookupsAvailable)
|
||||
lookupIPv6_data();
|
||||
}
|
||||
|
||||
void tst_QHostInfo::blockingLookup()
|
||||
{
|
||||
QFETCH(QString, hostname);
|
||||
QFETCH(int, err);
|
||||
QFETCH(QString, addresses);
|
||||
|
||||
QHostInfo hostInfo = QHostInfo::fromName(hostname);
|
||||
QStringList tmp;
|
||||
for (int i = 0; i < hostInfo.addresses().size(); ++i)
|
||||
tmp.append(hostInfo.addresses().at(i).toString());
|
||||
tmp.sort();
|
||||
|
||||
if ((int)hostInfo.error() != (int)err) {
|
||||
qWarning() << hostname << "=>" << lookupResults.errorString();
|
||||
}
|
||||
QCOMPARE((int)hostInfo.error(), (int)err);
|
||||
|
||||
QStringList expected = addresses.split(' ');
|
||||
expected.sort();
|
||||
|
||||
QCOMPARE(tmp.join(' ').toUpper(), expected.join(' ').toUpper());
|
||||
}
|
||||
|
||||
void tst_QHostInfo::raceCondition()
|
||||
{
|
||||
for (int i = 0; i < 1000; ++i) {
|
||||
QTcpSocket socket;
|
||||
socket.connectToHost("invalid" TEST_DOMAIN, 80);
|
||||
}
|
||||
}
|
||||
|
||||
class LookupThread : public QThread
|
||||
{
|
||||
protected:
|
||||
inline void run() override
|
||||
{
|
||||
QHostInfo info = QHostInfo::fromName("a-single" TEST_DOMAIN);
|
||||
QCOMPARE(info.error(), QHostInfo::NoError);
|
||||
QVERIFY(info.addresses().size() > 0);
|
||||
QCOMPARE(info.addresses().at(0).toString(), QString("192.0.2.1"));
|
||||
}
|
||||
};
|
||||
|
||||
void tst_QHostInfo::threadSafety()
|
||||
{
|
||||
const int nattempts = 5;
|
||||
const int runs = 100;
|
||||
LookupThread thr[nattempts];
|
||||
for (int j = 0; j < runs; ++j) {
|
||||
for (int i = 0; i < nattempts; ++i)
|
||||
thr[i].start();
|
||||
for (int k = nattempts - 1; k >= 0; --k)
|
||||
thr[k].wait();
|
||||
}
|
||||
}
|
||||
|
||||
class LookupReceiver : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public slots:
|
||||
void start();
|
||||
void resultsReady(const QHostInfo&);
|
||||
public:
|
||||
QHostInfo result;
|
||||
int numrequests;
|
||||
};
|
||||
|
||||
void LookupReceiver::start()
|
||||
{
|
||||
for (int i=0;i<numrequests;i++)
|
||||
QHostInfo::lookupHost(QString("a-single" TEST_DOMAIN), this, SLOT(resultsReady(QHostInfo)));
|
||||
}
|
||||
|
||||
void LookupReceiver::resultsReady(const QHostInfo &info)
|
||||
{
|
||||
result = info;
|
||||
numrequests--;
|
||||
if (numrequests == 0 || info.error() != QHostInfo::NoError)
|
||||
QThread::currentThread()->quit();
|
||||
}
|
||||
|
||||
void tst_QHostInfo::threadSafetyAsynchronousAPI()
|
||||
{
|
||||
const int nattempts = 10;
|
||||
const int lookupsperthread = 10;
|
||||
QList<QThread*> threads;
|
||||
QList<LookupReceiver*> receivers;
|
||||
for (int i = 0; i < nattempts; ++i) {
|
||||
QThread* thread = new QThread;
|
||||
LookupReceiver* receiver = new LookupReceiver;
|
||||
receiver->numrequests = lookupsperthread;
|
||||
receivers.append(receiver);
|
||||
receiver->moveToThread(thread);
|
||||
connect(thread, SIGNAL(started()), receiver, SLOT(start()));
|
||||
thread->start();
|
||||
threads.append(thread);
|
||||
}
|
||||
for (int k = threads.size() - 1; k >= 0; --k)
|
||||
QVERIFY(threads.at(k)->wait(60000));
|
||||
foreach (LookupReceiver* receiver, receivers) {
|
||||
QCOMPARE(receiver->result.error(), QHostInfo::NoError);
|
||||
QCOMPARE(receiver->result.addresses().at(0).toString(), QString("192.0.2.1"));
|
||||
QCOMPARE(receiver->numrequests, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// this test is for the multi-threaded QHostInfo rewrite. It is about getting results at all,
|
||||
// not about getting correct IPs
|
||||
void tst_QHostInfo::multipleSameLookups()
|
||||
{
|
||||
const int COUNT = 10;
|
||||
lookupsDoneCounter = 0;
|
||||
|
||||
for (int i = 0; i < COUNT; i++)
|
||||
QHostInfo::lookupHost("localhost", this, SLOT(resultsReady(QHostInfo)));
|
||||
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
while (timer.elapsed() < 10000 && lookupsDoneCounter < COUNT) {
|
||||
QTestEventLoop::instance().enterLoop(2);
|
||||
}
|
||||
QCOMPARE(lookupsDoneCounter, COUNT);
|
||||
}
|
||||
|
||||
// this test is for the multi-threaded QHostInfo rewrite. It is about getting results at all,
|
||||
// not about getting correct IPs
|
||||
void tst_QHostInfo::multipleDifferentLookups_data()
|
||||
{
|
||||
QTest::addColumn<int>("repeats");
|
||||
QTest::newRow("1") << 1;
|
||||
QTest::newRow("2") << 2;
|
||||
QTest::newRow("5") << 5;
|
||||
QTest::newRow("10") << 10;
|
||||
}
|
||||
|
||||
void tst_QHostInfo::multipleDifferentLookups()
|
||||
{
|
||||
QStringList hostnameList;
|
||||
hostnameList << "a-single" TEST_DOMAIN
|
||||
<< "a-multi" TEST_DOMAIN
|
||||
<< "aaaa-single" TEST_DOMAIN
|
||||
<< "aaaa-multi" TEST_DOMAIN
|
||||
<< "a-plus-aaaa" TEST_DOMAIN
|
||||
<< "multi" TEST_DOMAIN
|
||||
<< "localhost" TEST_DOMAIN
|
||||
<< "cname" TEST_DOMAIN
|
||||
<< "127.0.0.1" << "----";
|
||||
|
||||
QFETCH(int, repeats);
|
||||
const int COUNT = hostnameList.size();
|
||||
lookupsDoneCounter = 0;
|
||||
|
||||
for (int i = 0; i < hostnameList.size(); i++)
|
||||
for (int j = 0; j < repeats; ++j)
|
||||
QHostInfo::lookupHost(hostnameList.at(i), this, SLOT(resultsReady(QHostInfo)));
|
||||
|
||||
QElapsedTimer timer;
|
||||
timer.start();
|
||||
while (timer.elapsed() < 60000 && lookupsDoneCounter < repeats*COUNT) {
|
||||
QTestEventLoop::instance().enterLoop(2);
|
||||
//qDebug() << "t:" << timer.elapsed();
|
||||
}
|
||||
QCOMPARE(lookupsDoneCounter, repeats*COUNT);
|
||||
}
|
||||
|
||||
void tst_QHostInfo::cache()
|
||||
{
|
||||
QFETCH_GLOBAL(bool, cache);
|
||||
if (!cache)
|
||||
return; // test makes only sense when cache enabled
|
||||
|
||||
// reset slot counter
|
||||
lookupsDoneCounter = 0;
|
||||
|
||||
// lookup once, wait in event loop, result should not come directly.
|
||||
bool valid = true;
|
||||
int id = -1;
|
||||
QHostInfo result = qt_qhostinfo_lookup("localhost", this, SLOT(resultsReady(QHostInfo)), &valid, &id);
|
||||
QTestEventLoop::instance().enterLoop(5);
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
QVERIFY(!valid);
|
||||
QVERIFY(result.addresses().isEmpty());
|
||||
|
||||
// loopkup second time, result should come directly
|
||||
valid = false;
|
||||
result = qt_qhostinfo_lookup("localhost", this, SLOT(resultsReady(QHostInfo)), &valid, &id);
|
||||
QVERIFY(valid);
|
||||
QVERIFY(!result.addresses().isEmpty());
|
||||
|
||||
// clear the cache
|
||||
qt_qhostinfo_clear_cache();
|
||||
|
||||
// lookup third time, result should not come directly.
|
||||
valid = true;
|
||||
result = qt_qhostinfo_lookup("localhost", this, SLOT(resultsReady(QHostInfo)), &valid, &id);
|
||||
QTestEventLoop::instance().enterLoop(5);
|
||||
QVERIFY(!QTestEventLoop::instance().timeout());
|
||||
QVERIFY(!valid);
|
||||
QVERIFY(result.addresses().isEmpty());
|
||||
|
||||
// the slot should have been called 2 times.
|
||||
QCOMPARE(lookupsDoneCounter, 2);
|
||||
}
|
||||
|
||||
void tst_QHostInfo::resultsReady(const QHostInfo &hi)
|
||||
{
|
||||
QVERIFY(QThread::currentThread() == thread());
|
||||
lookupDone = true;
|
||||
lookupResults = hi;
|
||||
lookupsDoneCounter++;
|
||||
QTestEventLoop::instance().exitLoop();
|
||||
}
|
||||
|
||||
void tst_QHostInfo::abortHostLookup()
|
||||
{
|
||||
//reset counter
|
||||
lookupsDoneCounter = 0;
|
||||
bool valid = false;
|
||||
int id = -1;
|
||||
QHostInfo result = qt_qhostinfo_lookup("a-single" TEST_DOMAIN, this, SLOT(resultsReady(QHostInfo)), &valid, &id);
|
||||
QVERIFY(!valid);
|
||||
//it is assumed that the DNS request/response in the backend is slower than it takes to call abort
|
||||
QHostInfo::abortHostLookup(id);
|
||||
QTestEventLoop::instance().enterLoop(5);
|
||||
QCOMPARE(lookupsDoneCounter, 0);
|
||||
}
|
||||
|
||||
class LookupAborter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public slots:
|
||||
void abort()
|
||||
{
|
||||
QHostInfo::abortHostLookup(id);
|
||||
QThread::currentThread()->quit();
|
||||
}
|
||||
public:
|
||||
int id;
|
||||
};
|
||||
|
||||
QTEST_MAIN(tst_QHostInfo)
|
||||
#include "tst_qhostinfo.moc"
|
@ -0,0 +1,13 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qnetworkaddressentry Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qnetworkaddressentry
|
||||
SOURCES
|
||||
tst_qnetworkaddressentry.cpp
|
||||
LIBRARIES
|
||||
Qt::Network
|
||||
)
|
@ -0,0 +1,146 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
|
||||
#include <QTest>
|
||||
#include <qcoreapplication.h>
|
||||
#include <qnetworkinterface.h>
|
||||
|
||||
Q_DECLARE_METATYPE(QHostAddress)
|
||||
|
||||
class tst_QNetworkAddressEntry: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void getSetCheck();
|
||||
void prefixAndNetmask_data();
|
||||
void prefixAndNetmask();
|
||||
};
|
||||
|
||||
void tst_QNetworkAddressEntry::getSetCheck()
|
||||
{
|
||||
QNetworkAddressEntry entry;
|
||||
|
||||
QVERIFY(entry.ip().isNull());
|
||||
QVERIFY(entry.netmask().isNull());
|
||||
QVERIFY(entry.broadcast().isNull());
|
||||
QCOMPARE(entry.prefixLength(), -1);
|
||||
|
||||
entry.setIp(QHostAddress::LocalHost);
|
||||
QCOMPARE(entry.ip(), QHostAddress(QHostAddress::LocalHost));
|
||||
entry.setIp(QHostAddress());
|
||||
QVERIFY(entry.ip().isNull());
|
||||
|
||||
entry.setBroadcast(QHostAddress::LocalHost);
|
||||
QCOMPARE(entry.broadcast(), QHostAddress(QHostAddress::LocalHost));
|
||||
entry.setBroadcast(QHostAddress());
|
||||
QVERIFY(entry.broadcast().isNull());
|
||||
|
||||
// netmask and prefix length tested in the next test
|
||||
entry.setIp(QHostAddress::LocalHost);
|
||||
entry.setBroadcast(QHostAddress::LocalHost);
|
||||
|
||||
QNetworkAddressEntry entry2;
|
||||
QVERIFY(entry != entry2);
|
||||
QVERIFY(!(entry == entry2));
|
||||
|
||||
entry = entry2;
|
||||
QCOMPARE(entry, entry2);
|
||||
QCOMPARE(entry, entry);
|
||||
QVERIFY(!(entry != entry2));
|
||||
}
|
||||
|
||||
void tst_QNetworkAddressEntry::prefixAndNetmask_data()
|
||||
{
|
||||
QTest::addColumn<QHostAddress>("ip");
|
||||
QTest::addColumn<QHostAddress>("netmask");
|
||||
QTest::addColumn<int>("prefix");
|
||||
|
||||
// IPv4 set:
|
||||
QHostAddress ipv4(QHostAddress::LocalHost);
|
||||
QTest::newRow("v4/0") << ipv4 << QHostAddress(QHostAddress::AnyIPv4) << 0;
|
||||
QTest::newRow("v4/32") << ipv4 << QHostAddress("255.255.255.255") << 32;
|
||||
QTest::newRow("v4/24") << ipv4 << QHostAddress("255.255.255.0") << 24;
|
||||
QTest::newRow("v4/23") << ipv4 << QHostAddress("255.255.254.0") << 23;
|
||||
QTest::newRow("v4/20") << ipv4 << QHostAddress("255.255.240.0") << 20;
|
||||
QTest::newRow("v4/invalid1") << ipv4 << QHostAddress(QHostAddress::LocalHost) << -1;
|
||||
QTest::newRow("v4/invalid2") << ipv4 << QHostAddress(QHostAddress::AnyIPv6) << -1;
|
||||
QTest::newRow("v4/invalid3") << ipv4 << QHostAddress("255.255.253.0") << -1;
|
||||
QTest::newRow("v4/invalid4") << ipv4 << QHostAddress() << -2;
|
||||
QTest::newRow("v4/invalid5") << ipv4 << QHostAddress() << 33;
|
||||
|
||||
// IPv6 set:
|
||||
QHostAddress ipv6(QHostAddress::LocalHostIPv6);
|
||||
QTest::newRow("v6/0") << ipv6 << QHostAddress(QHostAddress::AnyIPv6) << 0;
|
||||
QTest::newRow("v6/128") << ipv6 << QHostAddress("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") << 128;
|
||||
QTest::newRow("v6/64") << ipv6 << QHostAddress("ffff:ffff:ffff:ffff::") << 64;
|
||||
QTest::newRow("v6/63") << ipv6 << QHostAddress("ffff:ffff:ffff:fffe::") << 63;
|
||||
QTest::newRow("v6/60") << ipv6 << QHostAddress("ffff:ffff:ffff:fff0::") << 60;
|
||||
QTest::newRow("v6/48") << ipv6 << QHostAddress("ffff:ffff:ffff::") << 48;
|
||||
QTest::newRow("v6/3") << ipv6 << QHostAddress("e000::") << 3;
|
||||
QTest::newRow("v6/invalid1") << ipv6 << QHostAddress(QHostAddress::LocalHostIPv6) << -1;
|
||||
QTest::newRow("v6/invalid2") << ipv6 << QHostAddress(QHostAddress::Any) << -1;
|
||||
QTest::newRow("v6/invalid3") << ipv6 << QHostAddress("fffd::") << -1;
|
||||
QTest::newRow("v6/invalid4") << ipv6 << QHostAddress() << -2;
|
||||
QTest::newRow("v6/invalid5") << ipv6 << QHostAddress() << 129;
|
||||
}
|
||||
|
||||
void tst_QNetworkAddressEntry::prefixAndNetmask()
|
||||
{
|
||||
QFETCH(QHostAddress, ip);
|
||||
QFETCH(QHostAddress, netmask);
|
||||
QFETCH(int, prefix);
|
||||
|
||||
QNetworkAddressEntry entry;
|
||||
|
||||
// first, without setting the IP, all must be invalid:
|
||||
entry.setNetmask(netmask);
|
||||
QVERIFY(entry.netmask().isNull());
|
||||
entry.setPrefixLength(prefix);
|
||||
QCOMPARE(entry.prefixLength(), -1);
|
||||
|
||||
// set the IP:
|
||||
entry.setIp(ip);
|
||||
|
||||
// set the netmask:
|
||||
if (!netmask.isNull()) {
|
||||
entry.setNetmask(netmask);
|
||||
|
||||
// was it a valid one?
|
||||
if (prefix != -1) {
|
||||
QVERIFY(!entry.netmask().isNull());
|
||||
QCOMPARE(entry.netmask(), netmask);
|
||||
QCOMPARE(entry.prefixLength(), prefix);
|
||||
} else {
|
||||
// not valid
|
||||
QVERIFY(entry.netmask().isNull());
|
||||
QCOMPARE(entry.prefixLength(), -1);
|
||||
}
|
||||
}
|
||||
entry.setNetmask(QHostAddress());
|
||||
QVERIFY(entry.netmask().isNull());
|
||||
QCOMPARE(entry.prefixLength(), -1);
|
||||
|
||||
// set the prefix
|
||||
if (prefix != -1) {
|
||||
entry.setPrefixLength(prefix);
|
||||
|
||||
// was it a valid one?
|
||||
if (!netmask.isNull()) {
|
||||
QVERIFY(!entry.netmask().isNull());
|
||||
QCOMPARE(entry.netmask(), netmask);
|
||||
QCOMPARE(entry.prefixLength(), prefix);
|
||||
} else {
|
||||
// not valid
|
||||
QVERIFY(entry.netmask().isNull());
|
||||
QCOMPARE(entry.prefixLength(), -1);
|
||||
}
|
||||
}
|
||||
entry.setPrefixLength(-1);
|
||||
QVERIFY(entry.netmask().isNull());
|
||||
QCOMPARE(entry.prefixLength(), -1);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QNetworkAddressEntry)
|
||||
#include "tst_qnetworkaddressentry.moc"
|
||||
|
13
tests/auto/network/kernel/qnetworkdatagram/CMakeLists.txt
Normal file
13
tests/auto/network/kernel/qnetworkdatagram/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qnetworkdatagram Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qnetworkdatagram
|
||||
SOURCES
|
||||
tst_qnetworkdatagram.cpp
|
||||
LIBRARIES
|
||||
Qt::Network
|
||||
)
|
@ -0,0 +1,132 @@
|
||||
// Copyright (C) 2016 Intel Corporation.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QNetworkDatagram>
|
||||
#include <QTest>
|
||||
#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);
|
||||
|
||||
auto dg2 = dg;
|
||||
QCOMPARE(dg2.hopLimit(), dg.hopLimit());
|
||||
QCOMPARE(dg2.interfaceIndex(), dg.interfaceIndex());
|
||||
QCOMPARE(dg2.senderAddress(), dg.senderAddress());
|
||||
QCOMPARE(dg2.senderPort(), dg.senderPort());
|
||||
QCOMPARE(dg2.destinationAddress(), dg.destinationAddress());
|
||||
QCOMPARE(dg2.destinationPort(), dg.destinationPort());
|
||||
|
||||
dg.clear();
|
||||
QVERIFY(dg.isNull());
|
||||
}
|
||||
|
||||
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 = std::move(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"
|
@ -0,0 +1,9 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
qt_internal_add_test(tst_qnetworkinformation
|
||||
SOURCES
|
||||
tst_qnetworkinformation.cpp
|
||||
LIBRARIES
|
||||
Qt::NetworkPrivate
|
||||
)
|
@ -0,0 +1,255 @@
|
||||
// Copyright (C) 2021 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QtNetwork/private/qnetworkinformation_p.h>
|
||||
#include <QtNetwork/qnetworkinformation.h>
|
||||
#include <QtTest/qtest.h>
|
||||
#include <QtTest/qsignalspy.h>
|
||||
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
|
||||
class MockFactory;
|
||||
class tst_QNetworkInformation : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void supportedFeatures();
|
||||
void reachability();
|
||||
void behindCaptivePortal();
|
||||
void transportMedium();
|
||||
void isMetered();
|
||||
void cleanupTestCase();
|
||||
|
||||
private:
|
||||
std::unique_ptr<MockFactory> mockFactory;
|
||||
};
|
||||
|
||||
static const QString mockName = QStringLiteral("mock");
|
||||
class MockBackend : public QNetworkInformationBackend
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MockBackend()
|
||||
{
|
||||
Q_ASSERT(!instance);
|
||||
instance = this;
|
||||
setReachability(QNetworkInformation::Reachability::Online);
|
||||
setNewBehindCaptivePortal(false);
|
||||
}
|
||||
~MockBackend() { instance = nullptr; }
|
||||
|
||||
QString name() const override { return mockName; }
|
||||
|
||||
QNetworkInformation::Features featuresSupported() const override
|
||||
{
|
||||
return featuresSupportedStatic();
|
||||
}
|
||||
|
||||
static void setNewReachability(QNetworkInformation::Reachability value)
|
||||
{
|
||||
Q_ASSERT(instance);
|
||||
instance->setReachability(value);
|
||||
}
|
||||
|
||||
static void setNewBehindCaptivePortal(bool value)
|
||||
{
|
||||
Q_ASSERT(instance);
|
||||
instance->setBehindCaptivePortal(value);
|
||||
}
|
||||
|
||||
static void setNewTransportMedium(QNetworkInformation::TransportMedium medium)
|
||||
{
|
||||
Q_ASSERT(instance);
|
||||
instance->setTransportMedium(medium);
|
||||
}
|
||||
|
||||
static void setNewMetered(bool metered)
|
||||
{
|
||||
Q_ASSERT(instance);
|
||||
instance->setMetered(metered);
|
||||
}
|
||||
|
||||
static QNetworkInformation::Features featuresSupportedStatic()
|
||||
{
|
||||
return { QNetworkInformation::Feature::Reachability
|
||||
| QNetworkInformation::Feature::CaptivePortal
|
||||
| QNetworkInformation::Feature::TransportMedium
|
||||
| QNetworkInformation::Feature::Metered };
|
||||
}
|
||||
|
||||
private:
|
||||
static inline MockBackend *instance = nullptr;
|
||||
};
|
||||
|
||||
class MockFactory : public QNetworkInformationBackendFactory
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QString name() const override { return mockName; }
|
||||
QNetworkInformationBackend *
|
||||
create(QNetworkInformation::Features requiredFeatures) const override
|
||||
{
|
||||
if ((requiredFeatures & featuresSupported()) != requiredFeatures)
|
||||
return nullptr;
|
||||
return new MockBackend();
|
||||
}
|
||||
QNetworkInformation::Features featuresSupported() const override
|
||||
{
|
||||
return MockBackend::featuresSupportedStatic();
|
||||
}
|
||||
};
|
||||
|
||||
void tst_QNetworkInformation::initTestCase()
|
||||
{
|
||||
auto prevBackends = QNetworkInformation::availableBackends();
|
||||
qDebug() << "available backends:" << prevBackends;
|
||||
// Creating the factory registers it as a backend
|
||||
mockFactory = std::make_unique<MockFactory>();
|
||||
auto backends = QNetworkInformation::availableBackends();
|
||||
QVERIFY(backends.size() > prevBackends.size());
|
||||
QVERIFY(backends.contains(u"mock"));
|
||||
QVERIFY(QNetworkInformation::loadBackendByName(u"mock"));
|
||||
QVERIFY(QNetworkInformation::loadBackendByName(u"mock"));
|
||||
QVERIFY(QNetworkInformation::loadBackendByName(u"mOcK"));
|
||||
QVERIFY(!QNetworkInformation::loadBackendByName(u"mocks"));
|
||||
}
|
||||
|
||||
void tst_QNetworkInformation::cleanupTestCase()
|
||||
{
|
||||
// Make sure the factory gets unregistered on destruction:
|
||||
mockFactory.reset();
|
||||
auto backends = QNetworkInformation::availableBackends();
|
||||
QVERIFY(!backends.contains(u"mock"));
|
||||
}
|
||||
|
||||
void tst_QNetworkInformation::supportedFeatures()
|
||||
{
|
||||
auto info = QNetworkInformation::instance();
|
||||
|
||||
auto allFeatures = QNetworkInformation::Features(QNetworkInformation::Feature::CaptivePortal
|
||||
| QNetworkInformation::Feature::Reachability
|
||||
| QNetworkInformation::Feature::TransportMedium
|
||||
| QNetworkInformation::Feature::Metered);
|
||||
|
||||
QCOMPARE(info->supportedFeatures(), allFeatures);
|
||||
|
||||
QVERIFY(info->supports(allFeatures));
|
||||
QVERIFY(info->supports(QNetworkInformation::Feature::CaptivePortal));
|
||||
QVERIFY(info->supports(QNetworkInformation::Feature::Reachability));
|
||||
QVERIFY(info->supports(QNetworkInformation::Feature::TransportMedium));
|
||||
QVERIFY(info->supports(QNetworkInformation::Feature::Metered));
|
||||
}
|
||||
|
||||
void tst_QNetworkInformation::reachability()
|
||||
{
|
||||
auto info = QNetworkInformation::instance();
|
||||
QNetworkInformation::Reachability boundIsOnline = QNetworkInformation::Reachability::Unknown;
|
||||
bool signalEmitted = false;
|
||||
|
||||
connect(info, &QNetworkInformation::reachabilityChanged, this, [&, info]() {
|
||||
signalEmitted = true;
|
||||
boundIsOnline = info->reachability();
|
||||
});
|
||||
QCOMPARE(info->reachability(), QNetworkInformation::Reachability::Online);
|
||||
MockBackend::setNewReachability(QNetworkInformation::Reachability::Disconnected);
|
||||
QCoreApplication::processEvents();
|
||||
QVERIFY(signalEmitted);
|
||||
QCOMPARE(info->reachability(), QNetworkInformation::Reachability::Disconnected);
|
||||
QCOMPARE(boundIsOnline, QNetworkInformation::Reachability::Disconnected);
|
||||
|
||||
// Set the same value again, signal should not be emitted again
|
||||
signalEmitted = false;
|
||||
MockBackend::setNewReachability(QNetworkInformation::Reachability::Disconnected);
|
||||
QCoreApplication::processEvents();
|
||||
QVERIFY(!signalEmitted);
|
||||
|
||||
MockBackend::setNewReachability(QNetworkInformation::Reachability::Local);
|
||||
QCOMPARE(info->reachability(), QNetworkInformation::Reachability::Local);
|
||||
QCOMPARE(boundIsOnline, QNetworkInformation::Reachability::Local);
|
||||
MockBackend::setNewReachability(QNetworkInformation::Reachability::Site);
|
||||
QCOMPARE(info->reachability(), QNetworkInformation::Reachability::Site);
|
||||
QCOMPARE(boundIsOnline, QNetworkInformation::Reachability::Site);
|
||||
}
|
||||
|
||||
void tst_QNetworkInformation::behindCaptivePortal()
|
||||
{
|
||||
auto info = QNetworkInformation::instance();
|
||||
bool behindPortal = false;
|
||||
bool signalEmitted = false;
|
||||
|
||||
connect(info, &QNetworkInformation::isBehindCaptivePortalChanged, this,
|
||||
[&, info](bool state) {
|
||||
signalEmitted = true;
|
||||
QCOMPARE(state, info->isBehindCaptivePortal());
|
||||
behindPortal = info->isBehindCaptivePortal();
|
||||
});
|
||||
QVERIFY(!info->isBehindCaptivePortal());
|
||||
MockBackend::setNewBehindCaptivePortal(true);
|
||||
QCoreApplication::processEvents();
|
||||
QVERIFY(signalEmitted);
|
||||
QVERIFY(info->isBehindCaptivePortal());
|
||||
QVERIFY(behindPortal);
|
||||
|
||||
// Set the same value again, signal should not be emitted again
|
||||
signalEmitted = false;
|
||||
MockBackend::setNewBehindCaptivePortal(true);
|
||||
QCoreApplication::processEvents();
|
||||
QVERIFY(!signalEmitted);
|
||||
}
|
||||
|
||||
void tst_QNetworkInformation::transportMedium()
|
||||
{
|
||||
auto info = QNetworkInformation::instance();
|
||||
using TransportMedium = QNetworkInformation::TransportMedium;
|
||||
TransportMedium medium = TransportMedium::Unknown;
|
||||
bool signalEmitted = false;
|
||||
|
||||
connect(info, &QNetworkInformation::transportMediumChanged, this, [&, info](TransportMedium tm) {
|
||||
signalEmitted = true;
|
||||
QCOMPARE(tm, info->transportMedium());
|
||||
medium = info->transportMedium();
|
||||
});
|
||||
QCOMPARE(info->transportMedium(), TransportMedium::Unknown); // Default is unknown
|
||||
|
||||
auto transportMediumEnum = QMetaEnum::fromType<TransportMedium>();
|
||||
auto mediumCount = transportMediumEnum.keyCount();
|
||||
// Verify index 0 is Unknown and skip it in the loop, it's the default.
|
||||
QCOMPARE(TransportMedium(transportMediumEnum.value(0)), TransportMedium::Unknown);
|
||||
for (int i = 1; i < mediumCount; ++i) {
|
||||
signalEmitted = false;
|
||||
TransportMedium m = TransportMedium(transportMediumEnum.value(i));
|
||||
MockBackend::setNewTransportMedium(m);
|
||||
QCoreApplication::processEvents();
|
||||
QVERIFY(signalEmitted);
|
||||
QCOMPARE(info->transportMedium(), m);
|
||||
QCOMPARE(medium, m);
|
||||
}
|
||||
|
||||
// Set the current value again, signal should not be emitted again
|
||||
signalEmitted = false;
|
||||
MockBackend::setNewTransportMedium(medium);
|
||||
QCoreApplication::processEvents();
|
||||
QVERIFY(!signalEmitted);
|
||||
}
|
||||
|
||||
void tst_QNetworkInformation::isMetered()
|
||||
{
|
||||
auto info = QNetworkInformation::instance();
|
||||
|
||||
QSignalSpy spy(info, &QNetworkInformation::isMeteredChanged);
|
||||
QVERIFY(!info->isMetered());
|
||||
MockBackend::setNewMetered(true);
|
||||
QCOMPARE(spy.size(), 1);
|
||||
QVERIFY(info->isMetered());
|
||||
QVERIFY(spy[0][0].toBool());
|
||||
spy.clear();
|
||||
|
||||
// Set the same value again, signal should not be emitted again
|
||||
MockBackend::setNewMetered(true);
|
||||
QCOMPARE(spy.size(), 0);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QNetworkInformation);
|
||||
#include "tst_qnetworkinformation.moc"
|
@ -0,0 +1,9 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
qt_internal_add_test(tst_qnetworkinformation_appless
|
||||
SOURCES
|
||||
tst_qnetworkinformation_appless.cpp
|
||||
LIBRARIES
|
||||
Qt::Network
|
||||
)
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user