summaryrefslogtreecommitdiffstats
path: root/tests/auto/network/access
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/network/access')
-rw-r--r--tests/auto/network/access/CMakeLists.txt8
-rw-r--r--tests/auto/network/access/hpack/CMakeLists.txt14
-rw-r--r--tests/auto/network/access/hpack/tst_hpack.cpp31
-rw-r--r--tests/auto/network/access/hsts/CMakeLists.txt12
-rw-r--r--tests/auto/network/access/hsts/tst_qhsts.cpp118
-rw-r--r--tests/auto/network/access/http2/BLACKLIST2
-rw-r--r--tests/auto/network/access/http2/CMakeLists.txt14
-rw-r--r--tests/auto/network/access/http2/http2srv.cpp145
-rw-r--r--tests/auto/network/access/http2/http2srv.h52
-rw-r--r--tests/auto/network/access/http2/tst_http2.cpp776
-rw-r--r--tests/auto/network/access/qabstractnetworkcache/CMakeLists.txt14
-rw-r--r--tests/auto/network/access/qabstractnetworkcache/tst_qabstractnetworkcache.cpp35
-rw-r--r--tests/auto/network/access/qdecompresshelper/10K.gzbin0 -> 55 bytes
-rw-r--r--tests/auto/network/access/qdecompresshelper/BLACKLIST2
-rw-r--r--tests/auto/network/access/qdecompresshelper/CMakeLists.txt16
-rw-r--r--tests/auto/network/access/qdecompresshelper/tst_qdecompresshelper.cpp123
-rw-r--r--tests/auto/network/access/qhttp2connection/CMakeLists.txt16
-rw-r--r--tests/auto/network/access/qhttp2connection/tst_qhttp2connection.cpp397
-rw-r--r--tests/auto/network/access/qhttpheaderparser/CMakeLists.txt19
-rw-r--r--tests/auto/network/access/qhttpheaderparser/tst_qhttpheaderparser.cpp94
-rw-r--r--tests/auto/network/access/qhttpheaders/CMakeLists.txt16
-rw-r--r--tests/auto/network/access/qhttpheaders/tst_qhttpheaders.cpp552
-rw-r--r--tests/auto/network/access/qhttpnetworkconnection/CMakeLists.txt17
-rw-r--r--tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp51
-rw-r--r--tests/auto/network/access/qhttpnetworkreply/CMakeLists.txt14
-rw-r--r--tests/auto/network/access/qhttpnetworkreply/tst_qhttpnetworkreply.cpp126
-rw-r--r--tests/auto/network/access/qnetworkaccessmanager/CMakeLists.txt11
-rw-r--r--tests/auto/network/access/qnetworkaccessmanager/tst_qnetworkaccessmanager.cpp29
-rw-r--r--tests/auto/network/access/qnetworkcachemetadata/CMakeLists.txt11
-rw-r--r--tests/auto/network/access/qnetworkcachemetadata/tst_qnetworkcachemetadata.cpp29
-rw-r--r--tests/auto/network/access/qnetworkcookie/CMakeLists.txt11
-rw-r--r--tests/auto/network/access/qnetworkcookie/tst_qnetworkcookie.cpp255
-rw-r--r--tests/auto/network/access/qnetworkcookiejar/CMakeLists.txt13
-rw-r--r--tests/auto/network/access/qnetworkcookiejar/testdata/publicsuffix/public_suffix_list.dafsabin0 -> 52422 bytes
-rw-r--r--tests/auto/network/access/qnetworkcookiejar/tst_qnetworkcookiejar.cpp101
-rw-r--r--tests/auto/network/access/qnetworkdiskcache/CMakeLists.txt11
-rw-r--r--tests/auto/network/access/qnetworkdiskcache/tst_qnetworkdiskcache.cpp122
-rw-r--r--tests/auto/network/access/qnetworkreply/4G.brbin0 -> 3361 bytes
-rw-r--r--tests/auto/network/access/qnetworkreply/BLACKLIST34
-rw-r--r--tests/auto/network/access/qnetworkreply/CMakeLists.txt9
-rw-r--r--tests/auto/network/access/qnetworkreply/certs/qt-test-server-cacert.pem30
-rw-r--r--tests/auto/network/access/qnetworkreply/certs/server.key38
-rw-r--r--tests/auto/network/access/qnetworkreply/certs/server.pem42
-rw-r--r--tests/auto/network/access/qnetworkreply/data/gzip.rcc.cpp1235
-rw-r--r--tests/auto/network/access/qnetworkreply/data/zstandard.rcc.cpp116
-rw-r--r--tests/auto/network/access/qnetworkreply/echo/CMakeLists.txt5
-rw-r--r--tests/auto/network/access/qnetworkreply/echo/main.cpp35
-rw-r--r--tests/auto/network/access/qnetworkreply/qnetworkreply.qrc5
-rw-r--r--tests/auto/network/access/qnetworkreply/test/CMakeLists.txt30
-rw-r--r--tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp2110
-rw-r--r--tests/auto/network/access/qnetworkrequest/CMakeLists.txt11
-rw-r--r--tests/auto/network/access/qnetworkrequest/tst_qnetworkrequest.cpp84
-rw-r--r--tests/auto/network/access/qnetworkrequestfactory/CMakeLists.txt17
-rw-r--r--tests/auto/network/access/qnetworkrequestfactory/tst_qnetworkrequestfactory.cpp423
-rw-r--r--tests/auto/network/access/qrestaccessmanager/CMakeLists.txt17
-rw-r--r--tests/auto/network/access/qrestaccessmanager/httptestserver.cpp268
-rw-r--r--tests/auto/network/access/qrestaccessmanager/httptestserver_p.h90
-rw-r--r--tests/auto/network/access/qrestaccessmanager/tst_qrestaccessmanager.cpp870
58 files changed, 7333 insertions, 1393 deletions
diff --git a/tests/auto/network/access/CMakeLists.txt b/tests/auto/network/access/CMakeLists.txt
index d225eb1299..44b7d5c1bb 100644
--- a/tests/auto/network/access/CMakeLists.txt
+++ b/tests/auto/network/access/CMakeLists.txt
@@ -1,14 +1,20 @@
-# Generated from access.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+add_subdirectory(qhttpheaders)
add_subdirectory(qnetworkdiskcache)
add_subdirectory(qnetworkcookiejar)
add_subdirectory(qnetworkaccessmanager)
add_subdirectory(qnetworkcookie)
add_subdirectory(qnetworkrequest)
+add_subdirectory(qnetworkrequestfactory)
add_subdirectory(qnetworkreply)
add_subdirectory(qnetworkcachemetadata)
add_subdirectory(qabstractnetworkcache)
+add_subdirectory(qrestaccessmanager)
if(QT_FEATURE_private_tests)
+ add_subdirectory(qhttp2connection)
+ add_subdirectory(qhttpheaderparser)
add_subdirectory(qhttpnetworkconnection)
add_subdirectory(qhttpnetworkreply)
add_subdirectory(hpack)
diff --git a/tests/auto/network/access/hpack/CMakeLists.txt b/tests/auto/network/access/hpack/CMakeLists.txt
index 421a60c17d..32cd4b2f06 100644
--- a/tests/auto/network/access/hpack/CMakeLists.txt
+++ b/tests/auto/network/access/hpack/CMakeLists.txt
@@ -1,17 +1,21 @@
-# Generated from hpack.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_hpack Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_hpack LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_hpack
SOURCES
tst_hpack.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::CorePrivate
Qt::Network
Qt::NetworkPrivate
)
-
-#### Keys ignored in scope 1:.:.:hpack.pro:<TRUE>:
-# TEMPLATE = "app"
diff --git a/tests/auto/network/access/hpack/tst_hpack.cpp b/tests/auto/network/access/hpack/tst_hpack.cpp
index ee66ba73ca..e6b43eaed4 100644
--- a/tests/auto/network/access/hpack/tst_hpack.cpp
+++ b/tests/auto/network/access/hpack/tst_hpack.cpp
@@ -1,31 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2014 Governikus GmbH & Co. KG.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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
#include <QTest>
#include <QRandomGenerator>
diff --git a/tests/auto/network/access/hsts/CMakeLists.txt b/tests/auto/network/access/hsts/CMakeLists.txt
index ba3b110f59..821a034940 100644
--- a/tests/auto/network/access/hsts/CMakeLists.txt
+++ b/tests/auto/network/access/hsts/CMakeLists.txt
@@ -1,17 +1,23 @@
-# Generated from hsts.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qhsts Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qhsts LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qhsts
SOURCES
tst_qhsts.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::CorePrivate
Qt::Network
Qt::NetworkPrivate
)
-#### Keys ignored in scope 1:.:.:hsts.pro:<TRUE>:
# TEMPLATE = "app"
diff --git a/tests/auto/network/access/hsts/tst_qhsts.cpp b/tests/auto/network/access/hsts/tst_qhsts.cpp
index f59d04e9e2..4e9a5cc53f 100644
--- a/tests/auto/network/access/hsts/tst_qhsts.cpp
+++ b/tests/auto/network/access/hsts/tst_qhsts.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
@@ -34,6 +9,7 @@
#include <QtCore/qpair.h>
#include <QtCore/qurl.h>
+#include <QtNetwork/qhttpheaders.h>
#include <QtNetwork/private/qhstsstore_p.h>
#include <QtNetwork/private/qhsts_p.h>
@@ -214,104 +190,108 @@ void tst_QHsts::testPolicyExpiration()
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));
+ QHttpHeaders headers;
+ QVERIFY(!parser.parse(headers));
QVERIFY(!parser.includeSubDomains());
QVERIFY(!parser.expirationDate().isValid());
- list << Header("Strict-Transport-security", "200");
- QVERIFY(!parser.parse(list));
+ headers.append("Strict-Transport-security", "200");
+ QVERIFY(!parser.parse(headers));
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));
+ headers.append("Strict-Transport-Security", "includeSubDomains");
+ QVERIFY(!parser.parse(headers));
QVERIFY(!parser.includeSubDomains());
QVERIFY(!parser.expirationDate().isValid());
- list.pop_back();
- list << Header("Strict-Transport-Security", "includeSubDomains;max-age=1000");
- QVERIFY(parser.parse(list));
+ headers.removeAt(headers.size() - 1);
+ headers.append("Strict-Transport-Security", "includeSubDomains;max-age=1000");
+ QVERIFY(parser.parse(headers));
QVERIFY(parser.expirationDate() > QDateTime::currentDateTimeUtc());
QVERIFY(parser.includeSubDomains());
- list.pop_back();
+ headers.removeAt(headers.size() - 1);
+ headers.append("strict-transport-security", "includeSubDomains;max-age=1000");
+ QVERIFY(parser.parse(headers));
+ QVERIFY(parser.expirationDate() > QDateTime::currentDateTimeUtc());
+ QVERIFY(parser.includeSubDomains());
+
+ headers.removeAt(headers.size() - 1);
// Invalid (includeSubDomains twice):
- list << Header("Strict-Transport-Security", "max-age = 1000 ; includeSubDomains;includeSubDomains");
- QVERIFY(!parser.parse(list));
+ headers.append("Strict-Transport-Security", "max-age = 1000 ; includeSubDomains;includeSubDomains");
+ QVERIFY(!parser.parse(headers));
QVERIFY(!parser.includeSubDomains());
QVERIFY(!parser.expirationDate().isValid());
- list.pop_back();
+ headers.removeAt(headers.size() - 1);
// Invalid (weird number of seconds):
- list << Header("Strict-Transport-Security", "max-age=-1000 ; includeSubDomains");
- QVERIFY(!parser.parse(list));
+ headers.append("Strict-Transport-Security", "max-age=-1000 ; includeSubDomains");
+ QVERIFY(!parser.parse(headers));
QVERIFY(!parser.includeSubDomains());
QVERIFY(!parser.expirationDate().isValid());
- list.pop_back();
+ headers.removeAt(headers.size() - 1);
// Note, directives are case-insensitive + we should ignore unknown directive.
- list << Header("Strict-Transport-Security", ";max-age=1000 ;includesubdomains;;"
+ headers.append("Strict-Transport-Security", ";max-age=1000 ;includesubdomains;;"
"nowsomeunknownheader=\"somevaluewithescapes\\;\"");
- QVERIFY(parser.parse(list));
+ QVERIFY(parser.parse(headers));
QVERIFY(parser.includeSubDomains());
QVERIFY(parser.expirationDate().isValid());
- list.pop_back();
+ headers.removeAt(headers.size() - 1);
// Check that we know how to unescape max-age:
- list << Header("Strict-Transport-Security", "max-age=\"1000\"");
- QVERIFY(parser.parse(list));
+ headers.append("Strict-Transport-Security", "max-age=\"1000\"");
+ QVERIFY(parser.parse(headers));
QVERIFY(!parser.includeSubDomains());
QVERIFY(parser.expirationDate().isValid());
- list.pop_back();
+ headers.removeAt(headers.size() - 1);
// 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));
+ headers.append("Strict-Transport-Security", "max-age; max-age=15768000");
+ QVERIFY(!parser.parse(headers));
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));
+ headers.clear();
+ headers.append("Strict-Transport-Security", "includeSubdomains; max-age=\"hehehe\";");
+ headers.append("Strict-Transport-Security", "max-age=10101");
+ QVERIFY(parser.parse(headers));
QVERIFY(!parser.includeSubDomains());
QVERIFY(parser.expirationDate().isValid());
- list.clear();
- list << Header("Strict-Transport-Security", "max-age=0");
- QVERIFY(parser.parse(list));
+ headers.clear();
+ headers.append("Strict-Transport-Security", "max-age=0");
+ QVERIFY(parser.parse(headers));
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));
+ headers.removeAt(headers.size() - 1);
+ headers.append("Strict-Transport-Security", "Max-aGE=1000; InclUdesUbdomains");
+ QVERIFY(parser.parse(headers));
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; ; ; ; ;;; ;;"
+ headers.removeAt(headers.size() - 1);
+ headers.append("Strict-Transport-Security", ";;; max-age = 17; ; ; ; ;;; ;;"
";;; ; includeSubdomains ;;thisIsUnknownDirective;;;;");
- QVERIFY(parser.parse(list));
+ QVERIFY(parser.parse(headers));
QVERIFY(parser.includeSubDomains());
QVERIFY(parser.expirationDate().isValid());
- list.pop_back();
- list << Header("Strict-Transport-Security", "max-age=1000; includeSubDomains bogon");
- QVERIFY(!parser.parse(list));
+ headers.removeAt(headers.size() - 1);
+ headers.append("Strict-Transport-Security", "max-age=1000; includeSubDomains bogon");
+ QVERIFY(!parser.parse(headers));
QVERIFY(!parser.includeSubDomains());
QVERIFY(!parser.expirationDate().isValid());
}
diff --git a/tests/auto/network/access/http2/BLACKLIST b/tests/auto/network/access/http2/BLACKLIST
new file mode 100644
index 0000000000..3de8d6d448
--- /dev/null
+++ b/tests/auto/network/access/http2/BLACKLIST
@@ -0,0 +1,2 @@
+[duplicateRequestsWithAborts]
+qnx ci # QTBUG-119616
diff --git a/tests/auto/network/access/http2/CMakeLists.txt b/tests/auto/network/access/http2/CMakeLists.txt
index 735f95deff..7ea559940b 100644
--- a/tests/auto/network/access/http2/CMakeLists.txt
+++ b/tests/auto/network/access/http2/CMakeLists.txt
@@ -1,18 +1,24 @@
-# Generated from http2.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_http2 Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_http2 LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_http2
SOURCES
http2srv.cpp http2srv.h
tst_http2.cpp
- DEFINES
- SRCDIR=\\\"${CMAKE_CURRENT_SOURCE_DIR}/\\\"
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::CorePrivate
Qt::Network
Qt::NetworkPrivate
Qt::TestPrivate
+ BUNDLE_ANDROID_OPENSSL_LIBS
)
diff --git a/tests/auto/network/access/http2/http2srv.cpp b/tests/auto/network/access/http2/http2srv.cpp
index d09779bb8f..b52ea5527b 100644
--- a/tests/auto/network/access/http2/http2srv.cpp
+++ b/tests/auto/network/access/http2/http2srv.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
@@ -109,6 +84,12 @@ Http2Server::~Http2Server()
{
}
+void Http2Server::setInformationalStatusCode(int code)
+{
+ if (code == 100 || (102 <= code && code <= 199))
+ informationalStatusCode = code;
+}
+
void Http2Server::enablePushPromise(bool pushEnabled, const QByteArray &path)
{
pushPromiseEnabled = pushEnabled;
@@ -125,6 +106,28 @@ void Http2Server::setContentEncoding(const QByteArray &encoding)
contentEncoding = encoding;
}
+void Http2Server::setAuthenticationHeader(const QByteArray &authentication)
+{
+ authenticationHeader = authentication;
+}
+
+void Http2Server::setAuthenticationRequired(bool enable)
+{
+ Q_ASSERT(!enable || authenticationHeader.isEmpty());
+ authenticationRequired = enable;
+}
+
+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);
@@ -143,6 +146,17 @@ 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()) {
@@ -251,9 +265,20 @@ void Http2Server::sendDATA(quint32 streamID, quint32 windowSize)
return;
if (last) {
- writer.start(FrameType::DATA, FrameFlag::END_STREAM, streamID);
- writer.setPayloadSize(0);
- writer.write(*socket);
+ 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);
@@ -302,11 +327,12 @@ void Http2Server::incomingConnection(qintptr socketDescriptor)
sslSocket->setProtocol(QSsl::TlsV1_2OrLater);
connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)),
this, SLOT(ignoreErrorSlot()));
- QFile file(SRCDIR "certs/fluke.key");
- file.open(QIODevice::ReadOnly);
+ QFile file(QT_TESTCASE_SOURCEDIR "/certs/fluke.key");
+ if (!file.open(QIODevice::ReadOnly))
+ qFatal("Cannot open certificate file %s", qPrintable(file.fileName()));
QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
sslSocket->setPrivateKey(key);
- auto localCert = QSslCertificate::fromPath(SRCDIR "certs/fluke.cert");
+ auto localCert = QSslCertificate::fromPath(QT_TESTCASE_SOURCEDIR "/certs/fluke.cert");
sslSocket->setLocalCertificateChain(localCert);
sslSocket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState);
// Stop listening.
@@ -364,16 +390,12 @@ bool Http2Server::verifyProtocolUpgradeRequest()
bool settingsOk = false;
QHttpNetworkReplyPrivate *firstRequestReader = protocolUpgradeHandler->d_func();
+ const auto headers = firstRequestReader->headers();
// That's how we append them, that's what I expect to find:
- for (const auto &header : firstRequestReader->fields) {
- 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;
- }
+ connectionOk = headers.combinedValue(QHttpHeaders::WellKnownHeader::Connection).contains("Upgrade, HTTP2-Settings");
+ upgradeOk = headers.combinedValue(QHttpHeaders::WellKnownHeader::Upgrade).contains("h2c");
+ settingsOk = headers.contains("HTTP2-Settings");
return connectionOk && upgradeOk && settingsOk;
}
@@ -737,10 +759,15 @@ void Http2Server::handleDATA()
}
if (inboundFrame.flags().testFlag(FrameFlag::END_STREAM)) {
- closedStreams.insert(streamID); // Enter "half-closed remote" state.
- streamWindows.erase(it);
+ 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()
@@ -817,10 +844,32 @@ void Http2Server::sendResponse(quint32 streamID, bool emptyBody)
// Now we'll continue with _normal_ response.
}
+ // Create a header with an informational status code and some random header
+ // fields. The setter ensures that the value is 100 or is between 102 and 199
+ // (inclusive) if set - otherwise it is 0
+
+ if (informationalStatusCode > 0) {
+ writer.start(FrameType::HEADERS, FrameFlag::END_HEADERS, streamID);
+
+ HttpHeader informationalHeader;
+ informationalHeader.push_back({":status", QByteArray::number(informationalStatusCode)});
+ informationalHeader.push_back(HeaderField("a_random_header_field", "it_will_be_dropped"));
+ informationalHeader.push_back(HeaderField("another_random_header_field", "drop_this_too"));
+
+ HPack::BitOStream ostream(writer.outboundFrame().buffer);
+ const bool result = encoder.encodeResponse(ostream, informationalHeader);
+ Q_ASSERT(result);
+
+ writer.writeHEADERS(*socket, maxFrameSize);
+ }
+
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) {
@@ -836,7 +885,15 @@ void Http2Server::sendResponse(quint32 streamID, bool emptyBody)
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));
+ } else if (authenticationRequired) {
+ header.push_back({ ":status", "401" });
} else {
header.push_back({":status", "200"});
}
diff --git a/tests/auto/network/access/http2/http2srv.h b/tests/auto/network/access/http2/http2srv.h
index baf0155988..dc94318527 100644
--- a/tests/auto/network/access/http2/http2srv.h
+++ b/tests/auto/network/access/http2/http2srv.h
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#ifndef HTTP2SRV_H
#define HTTP2SRV_H
@@ -83,16 +58,29 @@ public:
~Http2Server();
+ // To send responses with status code 1xx
+ void setInformationalStatusCode(int code);
// 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);
+ // Authentication always required, no challenge provided
+ void setAuthenticationRequired(bool enable);
+ // 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();
@@ -129,6 +117,8 @@ Q_SIGNALS:
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();
@@ -215,6 +205,14 @@ private:
QAtomicInt interrupted;
QByteArray contentEncoding;
+ QByteArray authenticationHeader;
+ bool authenticationRequired = false;
+
+ QByteArray redirectUrl;
+ int redirectCount = 0;
+
+ bool sendTrailingHEADERS = false;
+ int informationalStatusCode = 0;
protected slots:
void ignoreErrorSlot();
};
diff --git a/tests/auto/network/access/http2/tst_http2.cpp b/tests/auto/network/access/http2/tst_http2.cpp
index 1aa012c6ac..d9e82330b2 100644
--- a/tests/auto/network/access/http2/tst_http2.cpp
+++ b/tests/auto/network/access/http2/tst_http2.cpp
@@ -1,30 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtNetwork/qtnetworkglobal.h>
#include <QTest>
#include <QTestEventLoop>
@@ -40,16 +17,15 @@
#include <QtNetwork/qnetworkrequest.h>
#include <QtNetwork/qnetworkreply.h>
+#if QT_CONFIG(ssl)
+#include <QtNetwork/qsslsocket.h>
+#endif
+
#include <QtCore/qglobal.h>
#include <QtCore/qobject.h>
#include <QtCore/qthread.h>
#include <QtCore/qurl.h>
-
-#ifndef QT_NO_SSL
-#ifndef QT_NO_OPENSSL
-#include <QtNetwork/private/qsslsocket_openssl_symbols_p.h>
-#endif // NO_OPENSSL
-#endif // NO_SSL
+#include <QtCore/qset.h>
#include <cstdlib>
#include <memory>
@@ -57,21 +33,13 @@
#include <QtTest/private/qemulationdetector_p.h>
-#if (!defined(QT_NO_OPENSSL) && OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(OPENSSL_NO_TLSEXT)) \
- || QT_CONFIG(schannel)
-// HTTP/2 over TLS requires ALPN/NPN to negotiate the protocol version.
-const bool clearTextHTTP2 = false;
-#else
-// No ALPN/NPN support to negotiate HTTP/2, we'll use cleartext 'h2c' with
-// a protocol upgrade procedure.
-const bool clearTextHTTP2 = true;
-#endif
-
Q_DECLARE_METATYPE(H2Type)
Q_DECLARE_METATYPE(QNetworkRequest::Attribute)
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
QHttp2Configuration qt_defaultH2Configuration()
{
QHttp2Configuration config;
@@ -102,8 +70,11 @@ public slots:
void init();
private slots:
// Tests:
+ void defaultQnamHttp2Configuration();
void singleRequest_data();
void singleRequest();
+ void informationalRequest_data();
+ void informationalRequest();
void multipleRequests();
void flowControlClientSide();
void flowControlServerSide();
@@ -114,10 +85,29 @@ private slots:
void connectToHost_data();
void connectToHost();
void maxFrameSize();
+ void http2DATAFrames();
+
+ void moreActivitySignals_data();
+ void moreActivitySignals();
void contentEncoding_data();
void contentEncoding();
+ void authenticationRequired_data();
+ void authenticationRequired();
+
+ void unsupportedAuthenticateChallenge();
+
+ void h2cAllowedAttribute_data();
+ void h2cAllowedAttribute();
+
+ void redirect_data();
+ void redirect();
+
+ void trailingHEADERS();
+
+ void duplicateRequestsWithAborts();
+
protected slots:
// Slots to listen to our in-process server:
void serverStarted(quint16 port);
@@ -161,6 +151,7 @@ private:
int windowUpdates = 0;
bool prefaceOK = false;
bool serverGotSettingsACK = false;
+ bool POSTResponseHEADOnly = true;
static const RawSettings defaultServerSettings;
};
@@ -186,6 +177,8 @@ struct ServerDeleter
}
};
+bool clearTextHTTP2 = false;
+
using ServerPtr = QScopedPointer<Http2Server, ServerDeleter>;
H2Type defaultConnectionType()
@@ -198,6 +191,12 @@ H2Type defaultConnectionType()
tst_Http2::tst_Http2()
: workerThread(new QThread)
{
+#if QT_CONFIG(ssl)
+ const auto features = QSslSocket::supportedFeatures();
+ clearTextHTTP2 = !features.contains(QSsl::SupportedFeature::ServerSideAlpn);
+#else
+ clearTextHTTP2 = true;
+#endif
workerThread->start();
}
@@ -219,6 +218,12 @@ void tst_Http2::init()
manager.reset(new QNetworkAccessManager);
}
+void tst_Http2::defaultQnamHttp2Configuration()
+{
+ // The configuration we also implicitly use in QNAM.
+ QCOMPARE(qt_defaultH2Configuration(), QNetworkRequest().http2Configuration());
+}
+
void tst_Http2::singleRequest_data()
{
QTest::addColumn<QNetworkRequest::Attribute>("h2Attribute");
@@ -249,7 +254,7 @@ void tst_Http2::singleRequest()
// we have to use TLS sockets (== private key) and thus suppress a
// keychain UI asking for permission to use a private key.
// Our CI has this, but somebody testing locally - will have a problem.
- qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", QByteArray("1"));
+ qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", "1");
auto envRollback = qScopeGuard([](){
qunsetenv("QT_SSL_USE_TEMPORARY_KEYCHAIN");
});
@@ -270,10 +275,73 @@ void tst_Http2::singleRequest()
url.setPath("/index.html");
QNetworkRequest request(url);
+ request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
QFETCH(const QNetworkRequest::Attribute, h2Attribute);
request.setAttribute(h2Attribute, QVariant(true));
auto reply = manager->get(request);
+#if QT_CONFIG(ssl)
+ QSignalSpy encSpy(reply, &QNetworkReply::encrypted);
+#endif // QT_CONFIG(ssl)
+
+ connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished);
+ // Since we're using self-signed certificates,
+ // ignore SSL errors:
+ reply->ignoreSslErrors();
+
+ runEventLoop();
+ STOP_ON_FAILURE
+
+ QCOMPARE(nRequests, 0);
+ QVERIFY(prefaceOK);
+ QVERIFY(serverGotSettingsACK);
+
+ QCOMPARE(reply->error(), QNetworkReply::NoError);
+ QVERIFY(reply->isFinished());
+
+#if QT_CONFIG(ssl)
+ if (connectionType == H2Type::h2Alpn || connectionType == H2Type::h2Direct)
+ QCOMPARE(encSpy.size(), 1);
+#endif // QT_CONFIG(ssl)
+}
+
+void tst_Http2::informationalRequest_data()
+{
+ QTest::addColumn<int>("statusCode");
+
+ // 'Clear text' that should always work, either via the protocol upgrade
+ // or as direct.
+ QTest::addRow("statusCode-100") << 100;
+ QTest::addRow("statusCode-125") << 125;
+ QTest::addRow("statusCode-150") << 150;
+ QTest::addRow("statusCode-175") << 175;
+}
+
+void tst_Http2::informationalRequest()
+{
+ clearHTTP2State();
+
+ serverPort = 0;
+ nRequests = 1;
+
+ ServerPtr srv(newServer(defaultServerSettings, defaultConnectionType()));
+
+ QFETCH(const int, statusCode);
+ srv->setInformationalStatusCode(statusCode);
+
+ QMetaObject::invokeMethod(srv.data(), "startServer", Qt::QueuedConnection);
+ runEventLoop();
+
+ QVERIFY(serverPort != 0);
+
+ auto url = requestUrl(defaultConnectionType());
+ url.setPath("/index.html");
+
+ QNetworkRequest request(url);
+ request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
+
+ auto reply = manager->get(request);
+
connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished);
// Since we're using self-signed certificates,
// ignore SSL errors:
@@ -282,12 +350,23 @@ void tst_Http2::singleRequest()
runEventLoop();
STOP_ON_FAILURE
- QVERIFY(nRequests == 0);
+ QCOMPARE(nRequests, 0);
QVERIFY(prefaceOK);
QVERIFY(serverGotSettingsACK);
QCOMPARE(reply->error(), QNetworkReply::NoError);
QVERIFY(reply->isFinished());
+
+ const QVariant code(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute));
+
+ // We are discarding informational headers if the status code is in the range of
+ // 102-199 or if it is 100. As these header fields were part of the informational
+ // header used for this test case, we should not see them at this point and the
+ // status code should be 200.
+
+ QCOMPARE(code.value<int>(), 200);
+ QVERIFY(!reply->hasRawHeader("a_random_header_field"));
+ QVERIFY(!reply->hasRawHeader("another_random_header_field"));
}
void tst_Http2::multipleRequests()
@@ -318,7 +397,7 @@ void tst_Http2::multipleRequests()
runEventLoop();
STOP_ON_FAILURE
- QVERIFY(nRequests == 0);
+ QCOMPARE(nRequests, 0);
QVERIFY(prefaceOK);
QVERIFY(serverGotSettingsACK);
}
@@ -363,7 +442,7 @@ void tst_Http2::flowControlClientSide()
runEventLoop(120000);
STOP_ON_FAILURE
- QVERIFY(nRequests == 0);
+ QCOMPARE(nRequests, 0);
QVERIFY(prefaceOK);
QVERIFY(serverGotSettingsACK);
QVERIFY(windowUpdates > 0);
@@ -404,7 +483,7 @@ void tst_Http2::flowControlServerSide()
runEventLoop(120000);
STOP_ON_FAILURE
- QVERIFY(nRequests == 0);
+ QCOMPARE(nRequests, 0);
QVERIFY(prefaceOK);
QVERIFY(serverGotSettingsACK);
}
@@ -436,6 +515,7 @@ void tst_Http2::pushPromise()
url.setPath("/index.html");
QNetworkRequest request(url);
+ request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
request.setAttribute(QNetworkRequest::Http2AllowedAttribute, QVariant(true));
request.setHttp2Configuration(params);
@@ -447,7 +527,7 @@ void tst_Http2::pushPromise()
runEventLoop();
STOP_ON_FAILURE
- QVERIFY(nRequests == 0);
+ QCOMPARE(nRequests, 0);
QVERIFY(prefaceOK);
QVERIFY(serverGotSettingsACK);
@@ -462,6 +542,7 @@ void tst_Http2::pushPromise()
url.setPath("/script.js");
QNetworkRequest promisedRequest(url);
+ request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
promisedRequest.setAttribute(QNetworkRequest::Http2AllowedAttribute, QVariant(true));
reply = manager->get(promisedRequest);
connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished);
@@ -516,6 +597,7 @@ void tst_Http2::goaway()
for (int i = 0; i < nRequests; ++i) {
url.setPath(QString("/%1").arg(i));
QNetworkRequest request(url);
+ request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
request.setAttribute(QNetworkRequest::Http2AllowedAttribute, QVariant(true));
replies[i] = manager->get(request);
QCOMPARE(replies[i]->error(), QNetworkReply::NoError);
@@ -571,7 +653,7 @@ void tst_Http2::earlyResponse()
runEventLoop();
STOP_ON_FAILURE
- QVERIFY(nRequests == 0);
+ QCOMPARE(nRequests, 0);
QVERIFY(prefaceOK);
QVERIFY(serverGotSettingsACK);
}
@@ -628,7 +710,7 @@ void tst_Http2::connectToHost()
// we have to use TLS sockets (== private key) and thus suppress a
// keychain UI asking for permission to use a private key.
// Our CI has this, but somebody testing locally - will have a problem.
- qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", QByteArray("1"));
+ qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", "1");
auto envRollback = qScopeGuard([](){
qunsetenv("QT_SSL_USE_TEMPORARY_KEYCHAIN");
});
@@ -657,6 +739,7 @@ void tst_Http2::connectToHost()
auto copyUrl = url;
copyUrl.setScheme(QLatin1String("preconnect-https"));
QNetworkRequest request(copyUrl);
+ request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
request.setAttribute(requestAttribute, true);
reply = manager->get(request);
// Since we're using self-signed certificates, ignore SSL errors:
@@ -669,6 +752,7 @@ void tst_Http2::connectToHost()
auto copyUrl = url;
copyUrl.setScheme(QLatin1String("preconnect-http"));
QNetworkRequest request(copyUrl);
+ request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
request.setAttribute(requestAttribute, true);
reply = manager->get(request);
}
@@ -689,6 +773,7 @@ void tst_Http2::connectToHost()
QCOMPARE(nRequests, 1);
QNetworkRequest request(url);
+ request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
request.setAttribute(requestAttribute, QVariant(true));
reply = manager->get(request);
connect(reply, &QNetworkReply::finished, this, &tst_Http2::replyFinished);
@@ -699,7 +784,7 @@ void tst_Http2::connectToHost()
runEventLoop();
STOP_ON_FAILURE
- QVERIFY(nRequests == 0);
+ QCOMPARE(nRequests, 0);
QVERIFY(prefaceOK);
QVERIFY(serverGotSettingsACK);
@@ -723,7 +808,7 @@ void tst_Http2::maxFrameSize()
// we have to use TLS sockets (== private key) and thus suppress a
// keychain UI asking for permission to use a private key.
// Our CI has this, but somebody testing locally - will have a problem.
- qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", QByteArray("1"));
+ qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", "1");
auto envRollback = qScopeGuard([](){
qunsetenv("QT_SSL_USE_TEMPORARY_KEYCHAIN");
});
@@ -754,6 +839,7 @@ void tst_Http2::maxFrameSize()
url.setPath(QString("/stream1.html"));
QNetworkRequest request(url);
+ request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
request.setAttribute(attribute, QVariant(true));
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain"));
request.setHttp2Configuration(h2Config);
@@ -767,11 +853,166 @@ void tst_Http2::maxFrameSize()
// Normally, with a 16kb limit, our server would split such
// a response into 3 'DATA' frames (16kb + 16kb + 0|END_STREAM).
- QCOMPARE(frameCounter.count(), 1);
+ QCOMPARE(frameCounter.size(), 1);
+
+ QCOMPARE(nRequests, 0);
+ QVERIFY(prefaceOK);
+ QVERIFY(serverGotSettingsACK);
+}
+
+void tst_Http2::http2DATAFrames()
+{
+ using namespace Http2;
+
+ {
+ // 0. DATA frame with payload, no padding.
+
+ FrameWriter writer(FrameType::DATA, FrameFlag::EMPTY, 1);
+ writer.append('a');
+ writer.append('b');
+ writer.append('c');
+
+ const Frame frame = writer.outboundFrame();
+ const auto &buffer = frame.buffer;
+ // Frame's header is 9 bytes + 3 bytes of payload
+ // (+ 0 bytes of padding and no padding length):
+ QCOMPARE(int(buffer.size()), 12);
+
+ QVERIFY(!frame.padding());
+ QCOMPARE(int(frame.payloadSize()), 3);
+ QCOMPARE(int(frame.dataSize()), 3);
+ QCOMPARE(frame.dataBegin() - buffer.data(), 9);
+ QCOMPARE(char(*frame.dataBegin()), 'a');
+ }
+
+ {
+ // 1. DATA with padding.
+
+ const int padLength = 10;
+ FrameWriter writer(FrameType::DATA, FrameFlag::END_STREAM | FrameFlag::PADDED, 1);
+ writer.append(uchar(padLength)); // The length of padding is 1 byte long.
+ writer.append('a');
+ for (int i = 0; i < padLength; ++i)
+ writer.append('b');
+
+ const Frame frame = writer.outboundFrame();
+ const auto &buffer = frame.buffer;
+ // Frame's header is 9 bytes + 1 byte for padding length
+ // + 1 byte of data + 10 bytes of padding:
+ QCOMPARE(int(buffer.size()), 21);
+
+ QCOMPARE(frame.padding(), padLength);
+ QCOMPARE(int(frame.payloadSize()), 12); // Includes padding, its length + data.
+ QCOMPARE(int(frame.dataSize()), 1);
+
+ // Skipping 9 bytes long header and padding length:
+ QCOMPARE(frame.dataBegin() - buffer.data(), 10);
+
+ QCOMPARE(char(frame.dataBegin()[0]), 'a');
+ QCOMPARE(char(frame.dataBegin()[1]), 'b');
+
+ QVERIFY(frame.flags().testFlag(FrameFlag::END_STREAM));
+ QVERIFY(frame.flags().testFlag(FrameFlag::PADDED));
+ }
+ {
+ // 2. DATA with PADDED flag, but 0 as padding length.
+
+ FrameWriter writer(FrameType::DATA, FrameFlag::END_STREAM | FrameFlag::PADDED, 1);
+
+ writer.append(uchar(0)); // Number of padding bytes is 1 byte long.
+ writer.append('a');
+
+ const Frame frame = writer.outboundFrame();
+ const auto &buffer = frame.buffer;
+
+ // Frame's header is 9 bytes + 1 byte for padding length + 1 byte of data
+ // + 0 bytes of padding:
+ QCOMPARE(int(buffer.size()), 11);
+
+ QCOMPARE(frame.padding(), 0);
+ QCOMPARE(int(frame.payloadSize()), 2); // Includes padding (0 bytes), its length + data.
+ QCOMPARE(int(frame.dataSize()), 1);
+
+ // Skipping 9 bytes long header and padding length:
+ QCOMPARE(frame.dataBegin() - buffer.data(), 10);
+
+ QCOMPARE(char(*frame.dataBegin()), 'a');
+
+ QVERIFY(frame.flags().testFlag(FrameFlag::END_STREAM));
+ QVERIFY(frame.flags().testFlag(FrameFlag::PADDED));
+ }
+}
+
+void tst_Http2::moreActivitySignals_data()
+{
+ QTest::addColumn<QNetworkRequest::Attribute>("h2Attribute");
+ QTest::addColumn<H2Type>("connectionType");
+
+ QTest::addRow("h2c-upgrade")
+ << QNetworkRequest::Http2AllowedAttribute << H2Type::h2c;
+ QTest::addRow("h2c-direct")
+ << QNetworkRequest::Http2DirectAttribute << H2Type::h2cDirect;
+
+ if (!clearTextHTTP2)
+ QTest::addRow("h2-ALPN")
+ << QNetworkRequest::Http2AllowedAttribute << H2Type::h2Alpn;
+
+#if QT_CONFIG(ssl)
+ QTest::addRow("h2-direct")
+ << QNetworkRequest::Http2DirectAttribute << H2Type::h2Direct;
+#endif
+}
+
+void tst_Http2::moreActivitySignals()
+{
+ clearHTTP2State();
+
+#if QT_CONFIG(securetransport)
+ // Normally on macOS we use plain text only for SecureTransport
+ // does not support ALPN on the server side. With 'direct encrytped'
+ // we have to use TLS sockets (== private key) and thus suppress a
+ // keychain UI asking for permission to use a private key.
+ // Our CI has this, but somebody testing locally - will have a problem.
+ qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", "1");
+ auto envRollback = qScopeGuard([]() { qunsetenv("QT_SSL_USE_TEMPORARY_KEYCHAIN"); });
+#endif
+
+ serverPort = 0;
+ QFETCH(H2Type, connectionType);
+ ServerPtr srv(newServer(defaultServerSettings, connectionType));
+ QMetaObject::invokeMethod(srv.data(), "startServer", Qt::QueuedConnection);
+ runEventLoop(100);
+ QVERIFY(serverPort != 0);
+ auto url = requestUrl(connectionType);
+ url.setPath(QString("/stream1.html"));
+ QNetworkRequest request(url);
+ request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
+ QFETCH(const QNetworkRequest::Attribute, h2Attribute);
+ request.setAttribute(h2Attribute, QVariant(true));
+ request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain"));
+ QSharedPointer<QNetworkReply> reply(manager->get(request));
+ nRequests = 1;
+ connect(reply.data(), &QNetworkReply::finished, this, &tst_Http2::replyFinished);
+ QSignalSpy spy1(reply.data(), SIGNAL(socketStartedConnecting()));
+ QSignalSpy spy2(reply.data(), SIGNAL(requestSent()));
+ QSignalSpy spy3(reply.data(), SIGNAL(metaDataChanged()));
+ // Since we're using self-signed certificates,
+ // ignore SSL errors:
+ reply->ignoreSslErrors();
+
+ spy1.wait();
+ spy2.wait();
+ spy3.wait();
+
+ runEventLoop();
+ STOP_ON_FAILURE
- QVERIFY(nRequests == 0);
+ QCOMPARE(nRequests, 0);
QVERIFY(prefaceOK);
QVERIFY(serverGotSettingsACK);
+
+ QVERIFY(reply->error() == QNetworkReply::NoError);
+ QVERIFY(reply->isFinished());
}
void tst_Http2::contentEncoding_data()
@@ -843,7 +1084,7 @@ void tst_Http2::contentEncoding()
// we have to use TLS sockets (== private key) and thus suppress a
// keychain UI asking for permission to use a private key.
// Our CI has this, but somebody testing locally - will have a problem.
- qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", QByteArray("1"));
+ qputenv("QT_SSL_USE_TEMPORARY_KEYCHAIN", "1");
auto envRollback = qScopeGuard([]() { qunsetenv("QT_SSL_USE_TEMPORARY_KEYCHAIN"); });
#endif
@@ -866,6 +1107,7 @@ void tst_Http2::contentEncoding()
url.setPath("/index.html");
QNetworkRequest request(url);
+ request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
QFETCH(const QNetworkRequest::Attribute, h2Attribute);
request.setAttribute(h2Attribute, QVariant(true));
@@ -878,7 +1120,7 @@ void tst_Http2::contentEncoding()
runEventLoop();
STOP_ON_FAILURE
- QVERIFY(nRequests == 0);
+ QCOMPARE(nRequests, 0);
QVERIFY(prefaceOK);
QVERIFY(serverGotSettingsACK);
@@ -887,6 +1129,422 @@ void tst_Http2::contentEncoding()
QTEST(reply->readAll(), "expected");
}
+void tst_Http2::authenticationRequired_data()
+{
+ QTest::addColumn<bool>("success");
+ QTest::addColumn<bool>("responseHEADOnly");
+ QTest::addColumn<bool>("withChallenge");
+
+ QTest::addRow("failed-auth") << false << true << true;
+ QTest::addRow("successful-auth") << true << true << true;
+ // Include a DATA frame in the response from the remote server. An example would be receiving a
+ // JSON response on a request along with the 401 error.
+ QTest::addRow("failed-auth-with-response") << false << false << true;
+ QTest::addRow("successful-auth-with-response") << true << false << true;
+
+ // Don't provide a challenge header. This is valid if you are actually just
+ // denied access for whatever reason.
+ QTest::addRow("no-challenge") << false << false << false;
+}
+
+void tst_Http2::authenticationRequired()
+{
+ clearHTTP2State();
+ serverPort = 0;
+ QFETCH(const bool, responseHEADOnly);
+ POSTResponseHEADOnly = responseHEADOnly;
+
+ QFETCH(const bool, success);
+ QFETCH(const bool, withChallenge);
+
+ ServerPtr targetServer(newServer(defaultServerSettings, defaultConnectionType()));
+ QByteArray responseBody = "Hello"_ba;
+ targetServer->setResponseBody(responseBody);
+ if (withChallenge)
+ targetServer->setAuthenticationHeader("Basic realm=\"Shadow\"");
+ else
+ targetServer->setAuthenticationRequired(true);
+
+ QMetaObject::invokeMethod(targetServer.data(), "startServer", Qt::QueuedConnection);
+ runEventLoop();
+
+ QVERIFY(serverPort != 0);
+
+ nRequests = 1;
+
+ auto url = requestUrl(defaultConnectionType());
+ url.setPath("/index.html");
+ QNetworkRequest request(url);
+ request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
+
+ QByteArray expectedBody = "Hello, World!";
+ request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
+ QScopedPointer<QNetworkReply> reply;
+ reply.reset(manager->post(request, expectedBody));
+
+ bool authenticationRequested = false;
+ connect(manager.get(), &QNetworkAccessManager::authenticationRequired, reply.get(),
+ [&](QNetworkReply *, QAuthenticator *auth) {
+ authenticationRequested = true;
+ if (success) {
+ auth->setUser("admin");
+ auth->setPassword("admin");
+ }
+ });
+
+ QByteArray receivedBody;
+ connect(targetServer.get(), &Http2Server::receivedDATAFrame, reply.get(),
+ [&receivedBody](quint32 streamID, const QByteArray &body) {
+ if (streamID == 3) // The expected body is on the retry, so streamID == 3
+ receivedBody += body;
+ });
+
+ if (success) {
+ connect(reply.get(), &QNetworkReply::finished, this, &tst_Http2::replyFinished);
+ } else {
+ // Use queued connection so that the finished signal can be emitted and the isFinished
+ // property can be set.
+ connect(reply.get(), &QNetworkReply::errorOccurred, this,
+ &tst_Http2::replyFinishedWithError, Qt::QueuedConnection);
+ }
+ // Since we're using self-signed certificates,
+ // ignore SSL errors:
+ reply->ignoreSslErrors();
+
+ runEventLoop();
+ STOP_ON_FAILURE
+ QVERIFY2(reply->isFinished(),
+ "The reply should error out if authentication fails, or finish if it succeeds");
+
+ if (!success)
+ QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError);
+ // else: no error (is checked in tst_Http2::replyFinished)
+
+ QVERIFY(authenticationRequested || !withChallenge);
+
+ const auto isAuthenticated = [](const QByteArray &bv) {
+ return bv == "Basic YWRtaW46YWRtaW4="; // admin:admin
+ };
+ // Get the "authorization" header out from the server and make sure it's as expected:
+ auto reqAuthHeader = targetServer->requestAuthorizationHeader();
+ QCOMPARE(isAuthenticated(reqAuthHeader), success);
+ if (success)
+ QCOMPARE(receivedBody, expectedBody);
+ if (responseHEADOnly) {
+ const QVariant contentLenHeader = reply->header(QNetworkRequest::ContentLengthHeader);
+ QVERIFY2(!contentLenHeader.isValid(), "We expect no DATA frames to be received");
+ QCOMPARE(reply->readAll(), QByteArray());
+ } else {
+ const qint32 contentLen = reply->header(QNetworkRequest::ContentLengthHeader).toInt();
+ QCOMPARE(contentLen, responseBody.length());
+ QCOMPARE(reply->bytesAvailable(), responseBody.length());
+ QCOMPARE(reply->readAll(), QByteArray("Hello"));
+ }
+ // In the `!success` case we need to wait for the server to emit this or it might cause issues
+ // in the next test running after this. In the `success` case we anyway expect it to have been
+ // received.
+ QTRY_VERIFY(serverGotSettingsACK);
+}
+
+void tst_Http2::unsupportedAuthenticateChallenge()
+{
+ clearHTTP2State();
+ serverPort = 0;
+
+ if (defaultConnectionType() == H2Type::h2c)
+ QSKIP("This test requires TLS with ALPN to work");
+
+ ServerPtr targetServer(newServer(defaultServerSettings, defaultConnectionType()));
+ QByteArray responseBody = "Hello"_ba;
+ targetServer->setResponseBody(responseBody);
+ targetServer->setAuthenticationHeader("Bearer realm=\"qt.io accounts\"");
+
+ QMetaObject::invokeMethod(targetServer.data(), "startServer", Qt::QueuedConnection);
+ runEventLoop();
+
+ QVERIFY(serverPort != 0);
+
+ nRequests = 1;
+
+ QUrl url = requestUrl(defaultConnectionType());
+ url.setPath("/index.html");
+ QNetworkRequest request(url);
+
+ QByteArray expectedBody = "Hello, World!";
+ request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
+ QScopedPointer<QNetworkReply> reply;
+ reply.reset(manager->post(request, expectedBody));
+
+ bool authenticationRequested = false;
+ connect(manager.get(), &QNetworkAccessManager::authenticationRequired, reply.get(),
+ [&](QNetworkReply *, QAuthenticator *auth) {
+ authenticationRequested = true;
+ });
+
+ bool finishedReceived = false;
+ connect(reply.get(), &QNetworkReply::finished, reply.get(),
+ [&]() { finishedReceived = true; });
+ bool errorReceived = false;
+ connect(reply.get(), &QNetworkReply::errorOccurred, reply.get(),
+ [&]() { errorReceived = true; });
+
+ QSet<quint32> receivedDataOnStreams;
+ connect(targetServer.get(), &Http2Server::receivedDATAFrame, reply.get(),
+ [&receivedDataOnStreams](quint32 streamID, const QByteArray &body) {
+ Q_UNUSED(body);
+ receivedDataOnStreams.insert(streamID);
+ });
+
+ // Use queued connection so that the finished signal can be emitted and the
+ // isFinished property can be set.
+ connect(reply.get(), &QNetworkReply::errorOccurred, this,
+ &tst_Http2::replyFinishedWithError, Qt::QueuedConnection);
+
+ // Since we're using self-signed certificates, ignore SSL errors:
+ reply->ignoreSslErrors();
+
+ runEventLoop();
+ STOP_ON_FAILURE
+ QVERIFY2(reply->isFinished(),
+ "The reply should error out if authentication fails, or finish if it succeeds");
+
+ QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError);
+ QVERIFY(reply->isFinished());
+ QVERIFY(errorReceived);
+ QVERIFY(finishedReceived);
+ QCOMPARE(receivedDataOnStreams.size(), 1);
+ QVERIFY(receivedDataOnStreams.contains(1)); // the original, failed, request
+
+ QVERIFY(!authenticationRequested);
+
+ // We should not have sent any authentication headers to the server, since
+ // we don't support the challenge.
+ const QByteArray reqAuthHeader = targetServer->requestAuthorizationHeader();
+ QVERIFY(reqAuthHeader.isEmpty());
+
+ // In the `!success` case we need to wait for the server to emit this or it might cause issues
+ // in the next test running after this. In the `success` case we anyway expect it to have been
+ // received.
+ QTRY_VERIFY(serverGotSettingsACK);
+
+}
+
+void tst_Http2::h2cAllowedAttribute_data()
+{
+ QTest::addColumn<bool>("h2cAllowed");
+ QTest::addColumn<bool>("useAttribute"); // true: use attribute, false: use environment variable
+ QTest::addColumn<bool>("success");
+
+ QTest::addRow("h2c-not-allowed") << false << false << false;
+ // Use the attribute to enable/disable the H2C:
+ QTest::addRow("attribute") << true << true << true;
+ // Use the QT_NETWORK_H2C_ALLOWED environment variable to enable/disable the H2C:
+ QTest::addRow("environment-variable") << true << false << true;
+}
+
+void tst_Http2::h2cAllowedAttribute()
+{
+ QFETCH(const bool, h2cAllowed);
+ QFETCH(const bool, useAttribute);
+ QFETCH(const bool, success);
+
+ clearHTTP2State();
+ serverPort = 0;
+
+ ServerPtr targetServer(newServer(defaultServerSettings, H2Type::h2c));
+ targetServer->setResponseBody("Hello");
+
+ QMetaObject::invokeMethod(targetServer.data(), "startServer", Qt::QueuedConnection);
+ runEventLoop();
+
+ QVERIFY(serverPort != 0);
+
+ nRequests = 1;
+
+ auto url = requestUrl(H2Type::h2c);
+ url.setPath("/index.html");
+ QNetworkRequest request(url);
+ if (h2cAllowed) {
+ if (useAttribute)
+ request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
+ else
+ qputenv("QT_NETWORK_H2C_ALLOWED", "1");
+ }
+ auto envCleanup = qScopeGuard([]() { qunsetenv("QT_NETWORK_H2C_ALLOWED"); });
+
+ QScopedPointer<QNetworkReply> reply;
+ reply.reset(manager->get(request));
+
+ if (success)
+ connect(reply.get(), &QNetworkReply::finished, this, &tst_Http2::replyFinished);
+ else
+ connect(reply.get(), &QNetworkReply::errorOccurred, this, &tst_Http2::replyFinishedWithError);
+
+ // Since we're using self-signed certificates,
+ // ignore SSL errors:
+ reply->ignoreSslErrors();
+
+ runEventLoop();
+ STOP_ON_FAILURE
+
+ if (!success) {
+ QCOMPARE(reply->error(), QNetworkReply::ConnectionRefusedError);
+ } else {
+ QCOMPARE(reply->readAll(), QByteArray("Hello"));
+ QTRY_VERIFY(serverGotSettingsACK);
+ }
+}
+
+void tst_Http2::redirect_data()
+{
+ QTest::addColumn<int>("maxRedirects");
+ QTest::addColumn<int>("redirectCount");
+ QTest::addColumn<bool>("success");
+
+ QTest::addRow("1-redirects-none-allowed-failure") << 0 << 1 << false;
+ QTest::addRow("1-redirects-success") << 1 << 1 << true;
+ QTest::addRow("2-redirects-1-allowed-failure") << 1 << 2 << false;
+}
+
+void tst_Http2::redirect()
+{
+ QFETCH(const int, maxRedirects);
+ QFETCH(const int, redirectCount);
+ QFETCH(const bool, success);
+ const QByteArray redirectUrl = "/b.html"_ba;
+
+ clearHTTP2State();
+ serverPort = 0;
+
+ ServerPtr targetServer(newServer(defaultServerSettings, defaultConnectionType()));
+ targetServer->setRedirect(redirectUrl, redirectCount);
+
+ QMetaObject::invokeMethod(targetServer.data(), "startServer", Qt::QueuedConnection);
+ runEventLoop();
+
+ QVERIFY(serverPort != 0);
+
+ nRequests = 1;
+
+ auto originalUrl = requestUrl(defaultConnectionType());
+ auto url = originalUrl;
+ url.setPath("/index.html");
+ QNetworkRequest request(url);
+ request.setMaximumRedirectsAllowed(maxRedirects);
+ // H2C might be used on macOS where SecureTransport doesn't support server-side ALPN
+ qputenv("QT_NETWORK_H2C_ALLOWED", "1");
+ auto envCleanup = qScopeGuard([]() { qunsetenv("QT_NETWORK_H2C_ALLOWED"); });
+
+ QScopedPointer<QNetworkReply> reply;
+ reply.reset(manager->get(request));
+
+ if (success) {
+ connect(reply.get(), &QNetworkReply::finished, this, &tst_Http2::replyFinished);
+ } else {
+ connect(reply.get(), &QNetworkReply::errorOccurred, this,
+ &tst_Http2::replyFinishedWithError);
+ }
+
+ // Since we're using self-signed certificates,
+ // ignore SSL errors:
+ reply->ignoreSslErrors();
+
+ runEventLoop();
+ STOP_ON_FAILURE
+
+ QCOMPARE(nRequests, 0);
+ if (success) {
+ QCOMPARE(reply->error(), QNetworkReply::NoError);
+ QCOMPARE(reply->url().toString(),
+ originalUrl.resolved(QString::fromLatin1(redirectUrl)).toString());
+ } else if (maxRedirects < redirectCount) {
+ QCOMPARE(reply->error(), QNetworkReply::TooManyRedirectsError);
+ }
+ QTRY_VERIFY(serverGotSettingsACK);
+}
+
+void tst_Http2::trailingHEADERS()
+{
+ clearHTTP2State();
+ serverPort = 0;
+
+ ServerPtr targetServer(newServer(defaultServerSettings, defaultConnectionType()));
+ targetServer->setSendTrailingHEADERS(true);
+
+ QMetaObject::invokeMethod(targetServer.data(), "startServer", Qt::QueuedConnection);
+ runEventLoop();
+
+ QVERIFY(serverPort != 0);
+
+ nRequests = 1;
+
+ const auto url = requestUrl(defaultConnectionType());
+ QNetworkRequest request(url);
+ // H2C might be used on macOS where SecureTransport doesn't support server-side ALPN
+ request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
+
+ std::unique_ptr<QNetworkReply> reply{ manager->get(request) };
+ connect(reply.get(), &QNetworkReply::finished, this, &tst_Http2::replyFinished);
+
+ // Since we're using self-signed certificates, ignore SSL errors:
+ reply->ignoreSslErrors();
+
+ runEventLoop();
+ STOP_ON_FAILURE
+
+ QCOMPARE(nRequests, 0);
+
+ QCOMPARE(reply->error(), QNetworkReply::NoError);
+ QTRY_VERIFY(serverGotSettingsACK);
+}
+
+void tst_Http2::duplicateRequestsWithAborts()
+{
+ clearHTTP2State();
+ serverPort = 0;
+
+ ServerPtr targetServer(newServer(defaultServerSettings, defaultConnectionType()));
+
+ QMetaObject::invokeMethod(targetServer.data(), "startServer", Qt::QueuedConnection);
+ runEventLoop();
+
+ QVERIFY(serverPort != 0);
+
+ constexpr int ExpectedSuccessfulRequests = 1;
+ nRequests = ExpectedSuccessfulRequests;
+
+ const auto url = requestUrl(defaultConnectionType());
+ QNetworkRequest request(url);
+ // H2C might be used on macOS where SecureTransport doesn't support server-side ALPN
+ request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
+
+ qint32 finishedCount = 0;
+ auto connectToSlots = [this, &finishedCount](QNetworkReply *reply){
+ const auto onFinished = [&finishedCount, reply, this]() {
+ ++finishedCount;
+ if (reply->error() == QNetworkReply::NoError)
+ replyFinished();
+ };
+ connect(reply, &QNetworkReply::finished, reply, onFinished);
+ };
+
+ std::vector<QNetworkReply *> replies;
+ for (qint32 i = 0; i < 3; ++i) {
+ auto &reply = replies.emplace_back(manager->get(request));
+ connectToSlots(reply);
+ if (i < 2) // Delete and abort all-but-one:
+ reply->deleteLater();
+ // Since we're using self-signed certificates, ignore SSL errors:
+ reply->ignoreSslErrors();
+ }
+
+ runEventLoop();
+ STOP_ON_FAILURE
+
+ QCOMPARE(nRequests, 0);
+ QCOMPARE(finishedCount, ExpectedSuccessfulRequests);
+}
+
void tst_Http2::serverStarted(quint16 port)
{
serverPort = port;
@@ -898,6 +1556,7 @@ void tst_Http2::clearHTTP2State()
windowUpdates = 0;
prefaceOK = false;
serverGotSettingsACK = false;
+ POSTResponseHEADOnly = true;
}
void tst_Http2::runEventLoop(int ms)
@@ -943,6 +1602,7 @@ void tst_Http2::sendRequest(int streamNumber,
url.setPath(QString("/stream%1.html").arg(streamNumber));
QNetworkRequest request(url);
+ request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, true);
request.setAttribute(QNetworkRequest::Http2AllowedAttribute, QVariant(true));
request.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy);
request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain"));
@@ -1030,7 +1690,7 @@ void tst_Http2::receivedData(quint32 streamID)
Q_ASSERT(srv);
QMetaObject::invokeMethod(srv, "sendResponse", Qt::QueuedConnection,
Q_ARG(quint32, streamID),
- Q_ARG(bool, true /*HEADERS only*/));
+ Q_ARG(bool, POSTResponseHEADOnly /*true = HEADERS only*/));
}
void tst_Http2::windowUpdated(quint32 streamID)
diff --git a/tests/auto/network/access/qabstractnetworkcache/CMakeLists.txt b/tests/auto/network/access/qabstractnetworkcache/CMakeLists.txt
index 3958916232..37c3dbda8a 100644
--- a/tests/auto/network/access/qabstractnetworkcache/CMakeLists.txt
+++ b/tests/auto/network/access/qabstractnetworkcache/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from qabstractnetworkcache.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qabstractnetworkcache Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qabstractnetworkcache LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
file(GLOB_RECURSE test_data_glob
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
@@ -13,11 +20,10 @@ list(APPEND test_data ${test_data_glob})
qt_internal_add_test(tst_qabstractnetworkcache
SOURCES
tst_qabstractnetworkcache.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Network
TESTDATA ${test_data}
- QT_TEST_SERVER_LIST "apache2" # special case
+ QT_TEST_SERVER_LIST "apache2"
)
-#### Keys ignored in scope 1:.:.:qabstractnetworkcache.pro:<TRUE>:
# QT_TEST_SERVER_LIST = "apache2"
diff --git a/tests/auto/network/access/qabstractnetworkcache/tst_qabstractnetworkcache.cpp b/tests/auto/network/access/qabstractnetworkcache/tst_qabstractnetworkcache.cpp
index fc21569b0b..9bdef9bbe1 100644
--- a/tests/auto/network/access/qabstractnetworkcache/tst_qabstractnetworkcache.cpp
+++ b/tests/auto/network/access/qabstractnetworkcache/tst_qabstractnetworkcache.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTemporaryDir>
#include <QTest>
@@ -290,7 +265,7 @@ void tst_QAbstractNetworkCache::runTest()
// prime the cache
QNetworkReply *reply = manager.get(request);
QSignalSpy downloaded1(reply, SIGNAL(finished()));
- QTRY_COMPARE(downloaded1.count(), 1);
+ QTRY_COMPARE(downloaded1.size(), 1);
QCOMPARE(diskCache->gotData, false);
QByteArray goodData = reply->readAll();
@@ -299,7 +274,7 @@ void tst_QAbstractNetworkCache::runTest()
// should be in the cache now
QNetworkReply *reply2 = manager.get(request);
QSignalSpy downloaded2(reply2, SIGNAL(finished()));
- QTRY_COMPARE(downloaded2.count(), 1);
+ QTRY_COMPARE(downloaded2.size(), 1);
QByteArray secondData = reply2->readAll();
if (!fetchFromCache && cacheLoadControl == QNetworkRequest::AlwaysCache) {
@@ -388,7 +363,7 @@ void tst_QAbstractNetworkCache::deleteCache()
QNetworkReply *reply = manager.get(request);
QSignalSpy downloaded1(reply, SIGNAL(finished()));
manager.setCache(0);
- QTRY_COMPARE(downloaded1.count(), 1);
+ QTRY_COMPARE(downloaded1.size(), 1);
}
diff --git a/tests/auto/network/access/qdecompresshelper/10K.gz b/tests/auto/network/access/qdecompresshelper/10K.gz
new file mode 100644
index 0000000000..c5c4959763
--- /dev/null
+++ b/tests/auto/network/access/qdecompresshelper/10K.gz
Binary files differ
diff --git a/tests/auto/network/access/qdecompresshelper/BLACKLIST b/tests/auto/network/access/qdecompresshelper/BLACKLIST
new file mode 100644
index 0000000000..d189fd9e00
--- /dev/null
+++ b/tests/auto/network/access/qdecompresshelper/BLACKLIST
@@ -0,0 +1,2 @@
+[bigZlib]
+macos arm
diff --git a/tests/auto/network/access/qdecompresshelper/CMakeLists.txt b/tests/auto/network/access/qdecompresshelper/CMakeLists.txt
index 49fd91db0a..09317ca3eb 100644
--- a/tests/auto/network/access/qdecompresshelper/CMakeLists.txt
+++ b/tests/auto/network/access/qdecompresshelper/CMakeLists.txt
@@ -1,9 +1,16 @@
-# Generated from qdecompresshelper.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qdecompresshelper Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qdecompresshelper LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qdecompresshelper
SOURCES
gzip.rcc.cpp
@@ -11,10 +18,7 @@ qt_internal_add_test(tst_qdecompresshelper
tst_qdecompresshelper.cpp
zstandard.rcc.cpp
DEFINES
- SRC_DIR=${CMAKE_CURRENT_SOURCE_DIR} # special case
- PUBLIC_LIBRARIES
+ SRC_DIR=${CMAKE_CURRENT_SOURCE_DIR}
+ LIBRARIES
Qt::NetworkPrivate
)
-
-#### Keys ignored in scope 1:.:.:qdecompresshelper.pro:<TRUE>:
-# TEMPLATE = "app"
diff --git a/tests/auto/network/access/qdecompresshelper/tst_qdecompresshelper.cpp b/tests/auto/network/access/qdecompresshelper/tst_qdecompresshelper.cpp
index c31ab294cc..cd5a52c209 100644
--- a/tests/auto/network/access/qdecompresshelper/tst_qdecompresshelper.cpp
+++ b/tests/auto/network/access/qdecompresshelper/tst_qdecompresshelper.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
@@ -64,9 +39,10 @@ private Q_SLOTS:
void decompressBigData_data();
void decompressBigData();
-#if QT_POINTER_SIZE >= 8
+ void archiveBomb_data();
+ void archiveBomb();
+
void bigZlib();
-#endif
};
void tst_QDecompressHelper::initTestCase()
@@ -343,23 +319,30 @@ void tst_QDecompressHelper::countAheadPartialRead()
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;
- QTest::newRow("gzip-4G") << QByteArray("gzip") << QString(":/4G.gz") << fourGiB;
+ // 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;
+ << fiveGiB << false;
#if QT_CONFIG(brotli)
- QTest::newRow("brotli-4G") << QByteArray("br") << (srcDir + "/4G.br") << fourGiB;
+ 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;
+ QTest::newRow("zstandard-4G") << QByteArray("zstd") << (":/4G.zst") << fourGiB << false;
+ QTest::newRow("zstandard-counted-4G") << QByteArray("zstd") << (":/4G.zst") << fourGiB << true;
#endif
}
@@ -372,16 +355,20 @@ void tst_QDecompressHelper::decompressBigData()
const qint64 third = file.bytesAvailable() / 3;
QDecompressHelper helper;
- helper.setArchiveBombDetectionEnabled(false);
+ QFETCH(bool, countAhead);
+ helper.setCountingBytesEnabled(countAhead);
+ helper.setDecompressedSafetyCheckThreshold(-1);
QFETCH(QByteArray, encoding);
helper.setEncoding(encoding);
- QByteArray output(32 * 1024, Qt::Uninitialized);
+ // 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'; };
@@ -392,9 +379,56 @@ void tst_QDecompressHelper::decompressBigData()
QTEST(totalSize, "size");
}
-#if QT_POINTER_SIZE >= 8
+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");
@@ -402,20 +436,25 @@ void tst_QDecompressHelper::bigZlib()
QByteArray compressedData = file.readAll();
QDecompressHelper helper;
- helper.setArchiveBombDetectionEnabled(false);
+ 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
- qint64 expected = 5ll * 1024ll * 1024ll * 1024ll;
- // This can be replaced with QByteArray after the qsizetype change is merged
- std::unique_ptr<char[]> output(new char[expected]);
- qsizetype size = helper.read(output.get(), expected);
+ 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)
diff --git a/tests/auto/network/access/qhttp2connection/CMakeLists.txt b/tests/auto/network/access/qhttp2connection/CMakeLists.txt
new file mode 100644
index 0000000000..9a6e7a064e
--- /dev/null
+++ b/tests/auto/network/access/qhttp2connection/CMakeLists.txt
@@ -0,0 +1,16 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qhttp2connection LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qhttp2connection
+ SOURCES
+ tst_qhttp2connection.cpp
+ LIBRARIES
+ Qt::NetworkPrivate
+ Qt::Test
+)
diff --git a/tests/auto/network/access/qhttp2connection/tst_qhttp2connection.cpp b/tests/auto/network/access/qhttp2connection/tst_qhttp2connection.cpp
new file mode 100644
index 0000000000..b9d5219ae9
--- /dev/null
+++ b/tests/auto/network/access/qhttp2connection/tst_qhttp2connection.cpp
@@ -0,0 +1,397 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest/QTest>
+#include <QtTest/QSignalSpy>
+
+#include <QtNetwork/private/qhttp2connection_p.h>
+#include <QtNetwork/private/hpack_p.h>
+#include <QtNetwork/private/bitstreams_p.h>
+
+#include <limits>
+
+using namespace Qt::StringLiterals;
+
+class tst_QHttp2Connection : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void construct();
+ void constructStream();
+ void testSETTINGSFrame();
+ void testPING();
+ void connectToServer();
+ void WINDOW_UPDATE();
+
+private:
+ enum PeerType { Client, Server };
+ [[nodiscard]] auto makeFakeConnectedSockets();
+ [[nodiscard]] auto getRequiredHeaders();
+ [[nodiscard]] QHttp2Connection *makeHttp2Connection(QIODevice *socket,
+ QHttp2Configuration config, PeerType type);
+ [[nodiscard]] bool waitForSettingsExchange(QHttp2Connection *client, QHttp2Connection *server);
+};
+
+class IOBuffer : public QIODevice
+{
+ Q_OBJECT
+public:
+ IOBuffer(QObject *parent, std::shared_ptr<QBuffer> _in, std::shared_ptr<QBuffer> _out)
+ : QIODevice(parent), in(std::move(_in)), out(std::move(_out))
+ {
+ connect(in.get(), &QIODevice::readyRead, this, &IOBuffer::readyRead);
+ connect(out.get(), &QIODevice::bytesWritten, this, &IOBuffer::bytesWritten);
+ connect(out.get(), &QIODevice::aboutToClose, this, &IOBuffer::readChannelFinished);
+ connect(out.get(), &QIODevice::aboutToClose, this, &IOBuffer::aboutToClose);
+ }
+
+ bool open(OpenMode mode) override
+ {
+ QIODevice::open(mode);
+ Q_ASSERT(in->isOpen());
+ Q_ASSERT(out->isOpen());
+ return false;
+ }
+
+ bool isSequential() const override { return true; }
+
+ qint64 bytesAvailable() const override { return in->pos() - readHead; }
+ qint64 bytesToWrite() const override { return 0; }
+
+ qint64 readData(char *data, qint64 maxlen) override
+ {
+ qint64 temp = in->pos();
+ in->seek(readHead);
+ qint64 res = in->read(data, std::min(maxlen, temp - readHead));
+ readHead += res;
+ if (readHead == temp) {
+ // Reached end of buffer, reset
+ in->seek(0);
+ in->buffer().resize(0);
+ readHead = 0;
+ } else {
+ in->seek(temp);
+ }
+ return res;
+ }
+
+ qint64 writeData(const char *data, qint64 len) override
+ {
+ return out->write(data, len);
+ }
+
+ std::shared_ptr<QBuffer> in;
+ std::shared_ptr<QBuffer> out;
+
+ qint64 readHead = 0;
+};
+
+auto tst_QHttp2Connection::makeFakeConnectedSockets()
+{
+ auto clientIn = std::make_shared<QBuffer>();
+ auto serverIn = std::make_shared<QBuffer>();
+ clientIn->open(QIODevice::ReadWrite);
+ serverIn->open(QIODevice::ReadWrite);
+
+ auto client = std::make_unique<IOBuffer>(this, clientIn, serverIn);
+ auto server = std::make_unique<IOBuffer>(this, std::move(serverIn), std::move(clientIn));
+
+ client->open(QIODevice::ReadWrite);
+ server->open(QIODevice::ReadWrite);
+
+ return std::pair{ std::move(client), std::move(server) };
+}
+
+auto tst_QHttp2Connection::getRequiredHeaders()
+{
+ return HPack::HttpHeader{
+ { ":authority", "example.com" },
+ { ":method", "GET" },
+ { ":path", "/" },
+ { ":scheme", "https" },
+ };
+}
+
+QHttp2Connection *tst_QHttp2Connection::makeHttp2Connection(QIODevice *socket,
+ QHttp2Configuration config,
+ PeerType type)
+{
+ QHttp2Connection *connection = nullptr;
+ if (type == PeerType::Server)
+ connection = QHttp2Connection::createDirectServerConnection(socket, config);
+ else
+ connection = QHttp2Connection::createDirectConnection(socket, config);
+ connect(socket, &QIODevice::readyRead, connection, &QHttp2Connection::handleReadyRead);
+ return connection;
+}
+
+bool tst_QHttp2Connection::waitForSettingsExchange(QHttp2Connection *client,
+ QHttp2Connection *server)
+{
+ bool settingsFrameReceived = false;
+ bool serverSettingsFrameReceived = false;
+
+ QMetaObject::Connection c = connect(client, &QHttp2Connection::settingsFrameReceived, client,
+ [&settingsFrameReceived]() {
+ settingsFrameReceived = true;
+ });
+ QMetaObject::Connection s = connect(server, &QHttp2Connection::settingsFrameReceived, server,
+ [&serverSettingsFrameReceived]() {
+ serverSettingsFrameReceived = true;
+ });
+
+ client->handleReadyRead(); // handle incoming frames, send response
+
+ bool success = QTest::qWaitFor([&]() {
+ return settingsFrameReceived && serverSettingsFrameReceived;
+ });
+
+ disconnect(c);
+ disconnect(s);
+
+ return success;
+}
+
+void tst_QHttp2Connection::construct()
+{
+ QBuffer buffer;
+ buffer.open(QIODevice::ReadWrite);
+ auto *connection = QHttp2Connection::createDirectConnection(&buffer, {});
+ QVERIFY(!connection->isGoingAway());
+ QCOMPARE(connection->maxConcurrentStreams(), 100u);
+ QCOMPARE(connection->maxHeaderListSize(), std::numeric_limits<quint32>::max());
+ QVERIFY(!connection->isUpgradedConnection());
+ QVERIFY(!connection->getStream(1)); // No stream has been created yet
+
+ auto *upgradedConnection = QHttp2Connection::createUpgradedConnection(&buffer, {});
+ QVERIFY(upgradedConnection->isUpgradedConnection());
+ // Stream 1 is created by default for an upgraded connection
+ QVERIFY(upgradedConnection->getStream(1));
+}
+
+void tst_QHttp2Connection::constructStream()
+{
+ QBuffer buffer;
+ buffer.open(QIODevice::ReadWrite);
+ auto connection = QHttp2Connection::createDirectConnection(&buffer, {});
+ QHttp2Stream *stream = connection->createStream().unwrap();
+ QVERIFY(stream);
+ QCOMPARE(stream->isPromisedStream(), false);
+ QCOMPARE(stream->isActive(), false);
+ QCOMPARE(stream->RST_STREAM_code(), 0u);
+ QCOMPARE(stream->streamID(), 1u);
+ QCOMPARE(stream->receivedHeaders(), {});
+ QCOMPARE(stream->state(), QHttp2Stream::State::Idle);
+ QCOMPARE(stream->isUploadBlocked(), false);
+ QCOMPARE(stream->isUploadingDATA(), false);
+}
+
+void tst_QHttp2Connection::testSETTINGSFrame()
+{
+ constexpr qint32 PrefaceLength = 24;
+ QBuffer buffer;
+ buffer.open(QIODevice::ReadWrite);
+ QHttp2Configuration config;
+ constexpr quint32 MaxFrameSize = 16394;
+ constexpr bool ServerPushEnabled = false;
+ constexpr quint32 StreamReceiveWindowSize = 50000;
+ constexpr quint32 SessionReceiveWindowSize = 50001;
+ config.setMaxFrameSize(MaxFrameSize);
+ config.setServerPushEnabled(ServerPushEnabled);
+ config.setStreamReceiveWindowSize(StreamReceiveWindowSize);
+ config.setSessionReceiveWindowSize(SessionReceiveWindowSize);
+ auto connection = QHttp2Connection::createDirectConnection(&buffer, config);
+ Q_UNUSED(connection);
+ QCOMPARE_GE(buffer.size(), PrefaceLength);
+
+ // Preface
+ QByteArray preface = buffer.data().first(PrefaceLength);
+ QCOMPARE(preface, "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n");
+
+ // SETTINGS
+ buffer.seek(PrefaceLength);
+ const quint32 maxSize = buffer.size() - PrefaceLength;
+ Http2::FrameReader reader;
+ Http2::FrameStatus status = reader.read(buffer);
+ QCOMPARE(status, Http2::FrameStatus::goodFrame);
+ Http2::Frame f = reader.inboundFrame();
+ QCOMPARE(f.type(), Http2::FrameType::SETTINGS);
+ QCOMPARE_LT(f.payloadSize(), maxSize);
+
+ const qint32 settingsReceived = f.dataSize() / 6;
+ QCOMPARE_GT(settingsReceived, 0);
+ QCOMPARE_LE(settingsReceived, 6);
+
+ struct ExpectedSetting
+ {
+ Http2::Settings identifier;
+ quint32 value;
+ };
+ // Commented-out settings are not sent since they are defaults
+ ExpectedSetting expectedSettings[]{
+ // { Http2::Settings::HEADER_TABLE_SIZE_ID, HPack::FieldLookupTable::DefaultSize },
+ { Http2::Settings::ENABLE_PUSH_ID, ServerPushEnabled ? 1 : 0 },
+ // { Http2::Settings::MAX_CONCURRENT_STREAMS_ID, Http2::maxConcurrentStreams },
+ { Http2::Settings::INITIAL_WINDOW_SIZE_ID, StreamReceiveWindowSize },
+ { Http2::Settings::MAX_FRAME_SIZE_ID, MaxFrameSize },
+ // { Http2::Settings::MAX_HEADER_LIST_SIZE_ID, ??? },
+ };
+
+ QCOMPARE(quint32(settingsReceived), std::size(expectedSettings));
+ for (qint32 i = 0; i < settingsReceived; ++i) {
+ const uchar *it = f.dataBegin() + i * 6;
+ const quint16 ident = qFromBigEndian<quint16>(it);
+ const quint32 intVal = qFromBigEndian<quint32>(it + 2);
+
+ ExpectedSetting expectedSetting = expectedSettings[i];
+ QVERIFY2(ident == quint16(expectedSetting.identifier),
+ qPrintable("ident: %1, expected: %2, index: %3"_L1
+ .arg(QString::number(ident),
+ QString::number(quint16(expectedSetting.identifier)),
+ QString::number(i))));
+ QVERIFY2(intVal == expectedSetting.value,
+ qPrintable("intVal: %1, expected: %2, index: %3"_L1
+ .arg(QString::number(intVal),
+ QString::number(expectedSetting.value),
+ QString::number(i))));
+ }
+}
+
+void tst_QHttp2Connection::testPING()
+{
+ auto [client, server] = makeFakeConnectedSockets();
+ auto connection = makeHttp2Connection(client.get(), {}, Client);
+ auto serverConnection = makeHttp2Connection(server.get(), {}, Server);
+
+ QVERIFY(waitForSettingsExchange(connection, serverConnection));
+
+ QSignalSpy serverPingSpy{ serverConnection, &QHttp2Connection::pingFrameRecived };
+ QSignalSpy clientPingSpy{ connection, &QHttp2Connection::pingFrameRecived };
+
+ QByteArray data{"pingpong"};
+ connection->sendPing(data);
+
+ QVERIFY(serverPingSpy.wait());
+ QVERIFY(clientPingSpy.wait());
+
+ QCOMPARE(serverPingSpy.last().at(0).toInt(), int(QHttp2Connection::PingState::Ping));
+ QCOMPARE(clientPingSpy.last().at(0).toInt(), int(QHttp2Connection::PingState::PongSignatureIdentical));
+
+ serverConnection->sendPing();
+
+ QVERIFY(clientPingSpy.wait());
+ QVERIFY(serverPingSpy.wait());
+
+ QCOMPARE(clientPingSpy.last().at(0).toInt(), int(QHttp2Connection::PingState::Ping));
+ QCOMPARE(serverPingSpy.last().at(0).toInt(), int(QHttp2Connection::PingState::PongSignatureIdentical));
+}
+
+void tst_QHttp2Connection::connectToServer()
+{
+ auto [client, server] = makeFakeConnectedSockets();
+ auto connection = makeHttp2Connection(client.get(), {}, Client);
+ auto serverConnection = makeHttp2Connection(server.get(), {}, Server);
+
+ QVERIFY(waitForSettingsExchange(connection, serverConnection));
+
+ QSignalSpy newIncomingStreamSpy{ serverConnection, &QHttp2Connection::newIncomingStream };
+ QSignalSpy clientIncomingStreamSpy{ connection, &QHttp2Connection::newIncomingStream };
+
+ QHttp2Stream *clientStream = connection->createStream().unwrap();
+ QSignalSpy clientHeaderReceivedSpy{ clientStream, &QHttp2Stream::headersReceived };
+ QVERIFY(clientStream);
+ HPack::HttpHeader headers = getRequiredHeaders();
+ clientStream->sendHEADERS(headers, false);
+
+ QVERIFY(newIncomingStreamSpy.wait());
+ auto *serverStream = newIncomingStreamSpy.front().front().value<QHttp2Stream *>();
+ QVERIFY(serverStream);
+ const HPack::HttpHeader ExpectedResponseHeaders{ { ":status", "200" } };
+ serverStream->sendHEADERS(ExpectedResponseHeaders, true);
+
+ QVERIFY(clientHeaderReceivedSpy.wait());
+ const HPack::HttpHeader
+ headersReceived = clientHeaderReceivedSpy.front().front().value<HPack::HttpHeader>();
+ QCOMPARE(headersReceived, ExpectedResponseHeaders);
+
+ QCOMPARE(clientIncomingStreamSpy.count(), 0);
+}
+
+void tst_QHttp2Connection::WINDOW_UPDATE()
+{
+ auto [client, server] = makeFakeConnectedSockets();
+ auto connection = makeHttp2Connection(client.get(), {}, Client);
+
+ QHttp2Configuration config;
+ config.setStreamReceiveWindowSize(1024); // Small window on server to provoke WINDOW_UPDATE
+ auto serverConnection = makeHttp2Connection(server.get(), config, Server);
+
+ QVERIFY(waitForSettingsExchange(connection, serverConnection));
+
+ QSignalSpy newIncomingStreamSpy{ serverConnection, &QHttp2Connection::newIncomingStream };
+
+ QHttp2Stream *clientStream = connection->createStream().unwrap();
+ QSignalSpy clientHeaderReceivedSpy{ clientStream, &QHttp2Stream::headersReceived };
+ QSignalSpy clientDataReceivedSpy{ clientStream, &QHttp2Stream::dataReceived };
+ QVERIFY(clientStream);
+ HPack::HttpHeader expectedRequestHeaders = HPack::HttpHeader{
+ { ":authority", "example.com" },
+ { ":method", "POST" },
+ { ":path", "/" },
+ { ":scheme", "https" },
+ };
+ clientStream->sendHEADERS(expectedRequestHeaders, false);
+
+ QVERIFY(newIncomingStreamSpy.wait());
+ auto *serverStream = newIncomingStreamSpy.front().front().value<QHttp2Stream *>();
+ QVERIFY(serverStream);
+ QSignalSpy serverDataReceivedSpy{ serverStream, &QHttp2Stream::dataReceived };
+
+ // Since a stream is only opened on the remote side when the header is received,
+ // we can check the headers now immediately
+ QCOMPARE(serverStream->receivedHeaders(), expectedRequestHeaders);
+
+ QBuffer *buffer = new QBuffer(clientStream);
+ QByteArray uploadedData = "Hello World"_ba.repeated(1000);
+ buffer->setData(uploadedData);
+ buffer->open(QIODevice::ReadWrite);
+ clientStream->sendDATA(buffer, true);
+
+ bool streamEnd = false;
+ QByteArray serverReceivedData;
+ while (!streamEnd) { // The window is too small to receive all data at once, so loop
+ QVERIFY(serverDataReceivedSpy.wait());
+ auto latestEmission = serverDataReceivedSpy.back();
+ serverReceivedData += latestEmission.front().value<QByteArray>();
+ streamEnd = latestEmission.back().value<bool>();
+ }
+ QCOMPARE(serverReceivedData.size(), uploadedData.size());
+ QCOMPARE(serverReceivedData, uploadedData);
+
+ QCOMPARE(clientStream->state(), QHttp2Stream::State::HalfClosedLocal);
+ QCOMPARE(serverStream->state(), QHttp2Stream::State::HalfClosedRemote);
+
+ const HPack::HttpHeader ExpectedResponseHeaders{ { ":status", "200" } };
+ serverStream->sendHEADERS(ExpectedResponseHeaders, false);
+ QBuffer *serverBuffer = new QBuffer(serverStream);
+ serverBuffer->setData(uploadedData);
+ serverBuffer->open(QIODevice::ReadWrite);
+ serverStream->sendDATA(serverBuffer, true);
+
+ QVERIFY(clientHeaderReceivedSpy.wait());
+ const HPack::HttpHeader
+ headersReceived = clientHeaderReceivedSpy.front().front().value<HPack::HttpHeader>();
+ QCOMPARE(headersReceived, ExpectedResponseHeaders);
+
+ QTRY_COMPARE_GT(clientDataReceivedSpy.count(), 0);
+ QCOMPARE(clientDataReceivedSpy.count(), 1); // Only one DATA frame since our window is larger
+ QCOMPARE(clientDataReceivedSpy.front().front().value<QByteArray>(), uploadedData);
+
+ QCOMPARE(clientStream->state(), QHttp2Stream::State::Closed);
+ QCOMPARE(serverStream->state(), QHttp2Stream::State::Closed);
+}
+
+QTEST_MAIN(tst_QHttp2Connection)
+
+#include "tst_qhttp2connection.moc"
diff --git a/tests/auto/network/access/qhttpheaderparser/CMakeLists.txt b/tests/auto/network/access/qhttpheaderparser/CMakeLists.txt
new file mode 100644
index 0000000000..50deeb3e56
--- /dev/null
+++ b/tests/auto/network/access/qhttpheaderparser/CMakeLists.txt
@@ -0,0 +1,19 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qhttpheaderparser LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+if(NOT QT_FEATURE_private_tests)
+ return()
+endif()
+
+qt_internal_add_test(tst_qhttpheaderparser
+ SOURCES
+ tst_qhttpheaderparser.cpp
+ LIBRARIES
+ Qt::NetworkPrivate
+)
diff --git a/tests/auto/network/access/qhttpheaderparser/tst_qhttpheaderparser.cpp b/tests/auto/network/access/qhttpheaderparser/tst_qhttpheaderparser.cpp
new file mode 100644
index 0000000000..9ba889fdb3
--- /dev/null
+++ b/tests/auto/network/access/qhttpheaderparser/tst_qhttpheaderparser.cpp
@@ -0,0 +1,94 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#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"
diff --git a/tests/auto/network/access/qhttpheaders/CMakeLists.txt b/tests/auto/network/access/qhttpheaders/CMakeLists.txt
new file mode 100644
index 0000000000..0de1f96c67
--- /dev/null
+++ b/tests/auto/network/access/qhttpheaders/CMakeLists.txt
@@ -0,0 +1,16 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qhttpheaders LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qhttpheaders
+ SOURCES
+ tst_qhttpheaders.cpp
+ LIBRARIES
+ Qt::Core
+ Qt::Network
+)
diff --git a/tests/auto/network/access/qhttpheaders/tst_qhttpheaders.cpp b/tests/auto/network/access/qhttpheaders/tst_qhttpheaders.cpp
new file mode 100644
index 0000000000..457d30feeb
--- /dev/null
+++ b/tests/auto/network/access/qhttpheaders/tst_qhttpheaders.cpp
@@ -0,0 +1,552 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtNetwork/qhttpheaders.h>
+
+#include <QtTest/qtest.h>
+
+#include <QtCore/qmap.h>
+#include <QtCore/qset.h>
+
+using namespace Qt::StringLiterals;
+
+class tst_QHttpHeaders : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void constructors();
+ void accessors();
+ void wellKnownHeader();
+ void headerNameField();
+ void headerValueField();
+ void valueEncoding();
+ void replaceOrAppend();
+
+private:
+ static constexpr QAnyStringView n1{"name1"};
+ static constexpr QAnyStringView n2{"name2"};
+ static constexpr QAnyStringView n3{"name3"};
+ static constexpr QAnyStringView v1{"value1"};
+ static constexpr QAnyStringView v2{"value2"};
+ static constexpr QAnyStringView v3{"value3"};
+ static constexpr QAnyStringView N1{"NAME1"};
+ static constexpr QAnyStringView N2{"NAME2"};
+ static constexpr QAnyStringView N3{"NAME3"};
+ static constexpr QAnyStringView V1{"VALUE1"};
+ static constexpr QAnyStringView V2{"VALUE2"};
+ static constexpr QAnyStringView V3{"VALUE3"};
+};
+
+void tst_QHttpHeaders::constructors()
+{
+ // Default ctor
+ QHttpHeaders h1;
+ QVERIFY(h1.isEmpty());
+
+ // Copy ctor
+ QHttpHeaders h2(h1);
+ QCOMPARE(h2.toListOfPairs(), h1.toListOfPairs());
+
+ // Copy assignment
+ QHttpHeaders h3;
+ h3 = h1;
+ QCOMPARE(h3.toListOfPairs(), h1.toListOfPairs());
+
+ // Move assignment
+ QHttpHeaders h4;
+ h4 = std::move(h2);
+ QCOMPARE(h4.toListOfPairs(), h1.toListOfPairs());
+
+ // Move ctor
+ QHttpHeaders h5(std::move(h4));
+ QCOMPARE(h5.toListOfPairs(), h1.toListOfPairs());
+
+ // Constructors that are counterparts to 'toXXX()' conversion getters
+ const QByteArray nb1{"name1"};
+ const QByteArray nb2{"name2"};
+ const QByteArray nv1{"value1"};
+ const QByteArray nv2{"value2"};
+ // Initialize three QHttpHeaders with similar content, and verify that they have
+ // similar header entries
+#define CONTAINS_HEADER(NAME, VALUE) \
+ QVERIFY(hlist.contains(NAME) && hmap.contains(NAME) && hhash.contains(NAME)); \
+ QCOMPARE(hlist.combinedValue(NAME), VALUE); \
+ QCOMPARE(hmap.combinedValue(NAME), VALUE); \
+ QCOMPARE(hhash.combinedValue(NAME), VALUE); \
+
+ QList<std::pair<QByteArray, QByteArray>> list{{nb1, nv1}, {nb2, nv2}, {nb2, nv2}};
+ QMultiMap<QByteArray, QByteArray> map{{nb1, nv1}, {nb2, nv2}, {nb2, nv2}};
+ QMultiHash<QByteArray, QByteArray> hash{{nb1, nv1}, {nb2, nv2}, {nb2, nv2}};
+ QHttpHeaders hlist = QHttpHeaders::fromListOfPairs(list);
+ QHttpHeaders hmap = QHttpHeaders::fromMultiMap(map);
+ QHttpHeaders hhash = QHttpHeaders::fromMultiHash(hash);
+ CONTAINS_HEADER(nb1, v1);
+ CONTAINS_HEADER(nb2, nv2 + ", " + nv2)
+#undef CONTAINS_HEADER
+}
+
+void tst_QHttpHeaders::accessors()
+{
+ QHttpHeaders h1;
+
+ // isEmpty(), clear(), size()
+ h1.append(n1,v1);
+ QVERIFY(!h1.isEmpty());
+ QCOMPARE(h1.size(), 1);
+ QVERIFY(h1.append(n1, v1));
+ QCOMPARE(h1.size(), 2);
+ h1.insert(0, n1, v1);
+ QCOMPARE(h1.size(), 3);
+ h1.clear();
+ QVERIFY(h1.isEmpty());
+
+ // contains()
+ h1.append(n1, v1);
+ QVERIFY(h1.contains(n1));
+ QVERIFY(h1.contains(N1));
+ QVERIFY(!h1.contains(n2));
+ QVERIFY(!h1.contains(QHttpHeaders::WellKnownHeader::Allow));
+ h1.append(QHttpHeaders::WellKnownHeader::Accept, "nothing");
+ QVERIFY(h1.contains(QHttpHeaders::WellKnownHeader::Accept));
+ QVERIFY(h1.contains("accept"));
+
+ // values()/value()
+#define EXISTS_NOT(H, N) do { \
+ QVERIFY(!H.contains(N)); \
+ QCOMPARE(H.value(N, "ENOENT"), "ENOENT"); \
+ const auto values = H.values(N); \
+ QVERIFY(values.isEmpty()); \
+ QVERIFY(H.combinedValue(N).isNull()); \
+ } while (false)
+
+#define EXISTS_N_TIMES(X, H, N, ...) do { \
+ const std::array expected = { __VA_ARGS__ }; \
+ static_assert(std::tuple_size_v<decltype(expected)> == X); \
+ QVERIFY(H.contains(N)); \
+ QCOMPARE(H.value(N, "ENOENT"), expected.front()); \
+ const auto values = H.values(N); \
+ QCOMPARE(values.size(), X); \
+ QCOMPARE(values.front(), expected.front()); \
+ /* ignore in-between */ \
+ QCOMPARE(values.back(), expected.back()); \
+ QCOMPARE(H.combinedValue(N), values.join(", ")); \
+ } while (false)
+
+#define EXISTS_ONCE(H, N, V) EXISTS_N_TIMES(1, H, N, V)
+
+ EXISTS_ONCE(h1, n1, v1);
+ EXISTS_ONCE(h1, N1, v1);
+ EXISTS_ONCE(h1, QHttpHeaders::WellKnownHeader::Accept, "nothing");
+ EXISTS_ONCE(h1, "Accept", "nothing");
+
+ EXISTS_NOT(h1, N2);
+ EXISTS_NOT(h1, QHttpHeaders::WellKnownHeader::Allow);
+
+ h1.clear();
+
+ EXISTS_NOT(h1, n1);
+
+ h1.append(n1, v1);
+ h1.append(n1, v2);
+ h1.append(n1, v3);
+ h1.append(n2, v2);
+ h1.append(n3, ""); // empty value
+
+ EXISTS_N_TIMES(3, h1, n1, v1, v2, v3);
+ EXISTS_N_TIMES(3, h1, N1, v1, v2, v3);
+ EXISTS_ONCE(h1, n3, ""); // empty value
+
+ h1.append(QHttpHeaders::WellKnownHeader::Accept, "nothing");
+ h1.append(QHttpHeaders::WellKnownHeader::Accept, "ever");
+
+ EXISTS_N_TIMES(2, h1, QHttpHeaders::WellKnownHeader::Accept, "nothing", "ever");
+ EXISTS_NOT(h1, "nonexistent");
+
+#undef EXISTS_ONCE
+#undef EXISTS_N_TIMES
+#undef EXISTS_NOT
+
+ // valueAt()
+ h1.clear();
+ h1.append(n1, v1);
+ h1.append(n2, v2);
+ h1.append(n3, v3);
+ QCOMPARE(h1.valueAt(0), v1);
+ QCOMPARE(h1.valueAt(1), v2);
+ QCOMPARE(h1.valueAt(2), v3);
+
+ // nameAt()
+ h1.clear();
+ h1.append(n1, v1);
+ h1.append(n2, v2);
+ h1.append(n3, v3);
+ QCOMPARE(h1.nameAt(0), n1);
+ QCOMPARE(h1.nameAt(1), n2);
+ QCOMPARE(h1.nameAt(2), n3);
+
+ // removeAll()
+ h1.clear();
+ QVERIFY(h1.append(n1, v1));
+ QVERIFY(h1.append(QHttpHeaders::WellKnownHeader::Accept, "nothing"));
+ QVERIFY(h1.append(n1, v1));
+ QCOMPARE(h1.size(), 3);
+ h1.removeAll(n1);
+ QVERIFY(!h1.contains(n1));
+ QCOMPARE(h1.size(), 1);
+ QVERIFY(h1.contains("accept"));
+ h1.removeAll(QHttpHeaders::WellKnownHeader::Accept);
+ QVERIFY(!h1.contains(QHttpHeaders::WellKnownHeader::Accept));
+
+ // removeAt()
+ h1.clear();
+ h1.append(n1, v1);
+ h1.append(n2, v2);
+ h1.append(n3, v3);
+
+ // Valid removals
+ QVERIFY(h1.contains(n3));
+ h1.removeAt(2);
+ QVERIFY(!h1.contains(n3));
+ QVERIFY(h1.contains(n1));
+ h1.removeAt(0);
+ QVERIFY(!h1.contains(n1));
+ QVERIFY(h1.contains(n2));
+ h1.removeAt(0);
+ QVERIFY(!h1.contains(n2));
+ QVERIFY(h1.isEmpty());
+
+ // toListOfPairs()
+ h1.clear();
+ h1.append(n1, v1);
+ h1.append(n2, v2);
+ h1.append(N3, V3); // uppercase of n3
+ auto list = h1.toListOfPairs();
+ QCOMPARE(list.size(), h1.size());
+ QCOMPARE(list.at(0).first, n1);
+ QCOMPARE(list.at(0).second, v1);
+ QCOMPARE(list.at(1).first, n2);
+ QCOMPARE(list.at(1).second, v2);
+ QCOMPARE(list.at(2).first, n3); // N3 has been lower-cased
+ QCOMPARE(list.at(2).second, V3);
+
+ // toMultiMap()
+ auto map = h1.toMultiMap();
+ QCOMPARE(map.size(), h1.size());
+ QCOMPARE(map.value(n1.toString().toLatin1()), v1);
+ QCOMPARE(map.value(n2.toString().toLatin1()), v2);
+ QCOMPARE(map.value(n3.toString().toLatin1()), V3);
+
+ // toMultiHash()
+ auto hash = h1.toMultiHash();
+ QCOMPARE(hash.size(), h1.size());
+ QCOMPARE(hash.value(n1.toString().toLatin1()), v1);
+ QCOMPARE(hash.value(n2.toString().toLatin1()), v2);
+ QCOMPARE(hash.value(n3.toString().toLatin1()), V3);
+
+ // insert()
+ h1.clear();
+ h1.append(n3, v3);
+ QVERIFY(h1.insert(0, n1, v1));
+ list = h1.toListOfPairs();
+ QCOMPARE(list.size(), 2);
+ QCOMPARE(list.at(0).first, n1);
+ QCOMPARE(list.at(0).second, v1);
+ QCOMPARE(list.at(1).first, n3);
+ QCOMPARE(list.at(1).second, v3);
+ QVERIFY(h1.insert(1, n2, v2));
+ list = h1.toListOfPairs();
+ QCOMPARE(list.size(), 3);
+ QCOMPARE(list.at(0).first, n1);
+ QCOMPARE(list.at(0).second, v1);
+ QCOMPARE(list.at(1).first, n2);
+ QCOMPARE(list.at(1).second, v2);
+ QCOMPARE(list.at(2).first, n3);
+ QCOMPARE(list.at(2).second, v3);
+ QVERIFY(h1.insert(1, QHttpHeaders::WellKnownHeader::Accept, "nothing"));
+ QCOMPARE(h1.size(), 4);
+ list = h1.toListOfPairs();
+ QCOMPARE(list.at(1).first, "accept");
+ QCOMPARE(list.at(1).second, "nothing");
+ QVERIFY(h1.insert(list.size(), "LastName", "lastValue"));
+ QCOMPARE(h1.size(), 5);
+ list = h1.toListOfPairs();
+ QCOMPARE(list.last().first, "lastname");
+ QCOMPARE(list.last().second, "lastValue");
+ // Failed insert
+ QRegularExpression re("HTTP header name contained*");
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, re);
+ QVERIFY(!h1.insert(0, "a€", "b"));
+
+ // replace
+ h1.clear();
+ h1.append(n1, v1);
+ h1.append(n2, v2);
+ QCOMPARE(h1.size(), 2);
+ QVERIFY(h1.replace(0, n3, v3));
+ QVERIFY(h1.replace(1, QHttpHeaders::WellKnownHeader::Accept, "nothing"));
+ QCOMPARE(h1.size(), 2);
+ list = h1.toListOfPairs();
+ QCOMPARE(list.at(0).first, n3);
+ QCOMPARE(list.at(0).second, v3);
+ QCOMPARE(list.at(1).first, "accept");
+ QCOMPARE(list.at(1).second, "nothing");
+ QVERIFY(h1.replace(1, "ACCEPT", "NOTHING"));
+ QCOMPARE(h1.size(), 2);
+ list = h1.toListOfPairs();
+ QCOMPARE(list.at(0).first, n3);
+ QCOMPARE(list.at(0).second, v3);
+ QCOMPARE(list.at(1).first, "accept");
+ QCOMPARE(list.at(1).second, "NOTHING");
+ // Failed replace
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, re);
+ QVERIFY(!h1.replace(0, "a€", "b"));
+
+}
+
+void tst_QHttpHeaders::wellKnownHeader()
+{
+ QByteArrayView view = QHttpHeaders::wellKnownHeaderName(QHttpHeaders::WellKnownHeader::AIM);
+ QCOMPARE(view, "a-im");
+}
+
+#define TEST_ILLEGAL_HEADER_NAME_CHARACTER(NAME) \
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, re); \
+ QVERIFY(!h1.append(NAME, v1)); \
+ QVERIFY(h1.isEmpty()); \
+
+void tst_QHttpHeaders::headerNameField()
+{
+ QHttpHeaders h1;
+
+ // All allowed characters in different encodings and types
+ // const char[]
+ h1.append("abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&'*+-.^_`|~", v1);
+ QCOMPARE(h1.size(), 1);
+ // UTF-8
+ h1.append(u8"abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&'*+-.^_`|~",
+ v1);
+ QCOMPARE(h1.size(), 2);
+ // UTF-16
+ h1.append(u"abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&'*+-.^_`|~", v1);
+ QCOMPARE(h1.size(), 3);
+ // QString (UTF-16)
+ h1.append(u"abcdefghijklmnopqrstuvwyxzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&'*+-.^_`|~"_s,
+ v1);
+ QCOMPARE(h1.size(), 4);
+ QCOMPARE(h1.nameAt(0), h1.nameAt(1));
+ QCOMPARE(h1.nameAt(1), h1.nameAt(2));
+ QCOMPARE(h1.nameAt(2), h1.nameAt(3));
+ h1.clear();
+
+ // Error cases
+ // Header name must contain at least 1 character
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, "HTTP header name cannot be empty");
+ h1.append("", v1);
+ QVERIFY(h1.isEmpty());
+ // Disallowed ASCII/extended ASCII characters (not exhaustive list)
+ QRegularExpression re("HTTP header name contained illegal character*");
+ TEST_ILLEGAL_HEADER_NAME_CHARACTER("foo\x08" "bar"); // BS
+ TEST_ILLEGAL_HEADER_NAME_CHARACTER("foo\x7F" "bar"); // DEL
+ TEST_ILLEGAL_HEADER_NAME_CHARACTER("foo()" "bar"); // parantheses
+ TEST_ILLEGAL_HEADER_NAME_CHARACTER("foobar" "¿"); // extended ASCII
+ TEST_ILLEGAL_HEADER_NAME_CHARACTER("©" "foobar"); // extended ASCII
+ TEST_ILLEGAL_HEADER_NAME_CHARACTER("foo,bar"); // comma
+ // Disallowed UTF-8 characters
+ TEST_ILLEGAL_HEADER_NAME_CHARACTER(u8"€");
+ TEST_ILLEGAL_HEADER_NAME_CHARACTER(u8"𝒜𝒴𝟘𝟡𐎀𐎜𐒀𐒐𝓐𝓩𝔸𝔹𝕀𝕁𝕌𝕍𓂀𓂁𓃀𓃁𓇋𓇌𓉐𓉑𓋴𓋵𓎡𓎢𓎣𓏏");
+ // Disallowed UTF-16 characters
+ TEST_ILLEGAL_HEADER_NAME_CHARACTER(u"€");
+ TEST_ILLEGAL_HEADER_NAME_CHARACTER(u"𝒜𝒴𝟘𝟡𐎀𐎜𐒀𐒐𝓐𝓩𝔸𝔹𝕀𝕁𝕌𝕍𓂀𓂁𓃀𓃁𓇋𓇌𓉐𓉑𓋴𓋵𓎡𓎢𓎣𓏏");
+
+ // Non-null-terminated name. The 'x' below is to make sure the strings don't
+ // null-terminate by happenstance
+ h1.clear();
+ constexpr char L1Array[] = {'a','b','c','x'};
+ const QLatin1StringView nonNullLatin1{L1Array, sizeof(L1Array) - 1}; // abc
+
+ constexpr char UTF8Array[] = {0x64, 0x65, 0x66, 0x78};
+ const QUtf8StringView nonNullUTF8(UTF8Array, sizeof(UTF8Array) - 1); // def
+
+ constexpr QChar UTF16Array[] = {'g', 'h', 'i', 'x'};
+ QStringView nonNullUTF16(UTF16Array, sizeof(UTF16Array) / sizeof(QChar) - 1); // ghi
+
+ h1.append(nonNullLatin1, v1);
+ QCOMPARE(h1.size(), 1);
+ QVERIFY(h1.contains(nonNullLatin1));
+ QCOMPARE(h1.combinedValue(nonNullLatin1), v1);
+
+ h1.append(nonNullUTF8, v2);
+ QCOMPARE(h1.size(), 2);
+ QVERIFY(h1.contains(nonNullUTF8));
+ QCOMPARE(h1.combinedValue(nonNullUTF8), v2);
+
+ h1.append(nonNullUTF16, v3);
+ QCOMPARE(h1.size(), 3);
+ QVERIFY(h1.contains(nonNullUTF16));
+ QCOMPARE(h1.combinedValue(nonNullUTF16), v3);
+}
+
+#define TEST_ILLEGAL_HEADER_VALUE_CHARACTER(VALUE) \
+QTest::ignoreMessage(QtMsgType::QtWarningMsg, re); \
+ QVERIFY(!h1.append(n1, VALUE)); \
+ QVERIFY(h1.isEmpty()); \
+
+void tst_QHttpHeaders::headerValueField()
+{
+ QHttpHeaders h1;
+
+ // Visible ASCII characters and space and horizontal tab
+ // const char[]
+ h1.append(n1, "!\"#$%&'()*+,-./0123456789:; \t<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
+ "`abcdefghijklmnopqrstuvwxyz{|}~");
+ QCOMPARE(h1.size(), 1);
+ // UTF-8
+ h1.append(n1, u8"!\"#$%&'()*+,-./0123456789:; \t<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
+ "`abcdefghijklmnopqrstuvwxyz{|}~");
+ QCOMPARE(h1.size(), 2);
+ // UTF-16
+ h1.append(n1, u"!\"#$%&'()*+,-./0123456789:; \t<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
+ "`abcdefghijklmnopqrstuvwxyz{|}~");
+ QCOMPARE(h1.size(), 3);
+ // QString / UTF-16
+ h1.append(n1, u"!\"#$%&'()*+,-./0123456789:; \t<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
+ "`abcdefghijklmnopqrstuvwxyz{|}~"_s);
+ QCOMPARE(h1.size(), 4);
+ const auto values = h1.values(n1);
+ QVERIFY(!values.isEmpty() && values.size() == 4);
+ QVERIFY(values[0] == values[1]
+ && values[1] == values[2]
+ && values[2] == values[3]);
+ // Extended ASCII (explicit on Latin-1 to avoid UTF-8 interpretation)
+ h1.append(n1, "\x80\x09\xB2\xFF"_L1);
+ QCOMPARE(h1.size(), 5);
+ // Empty value
+ h1.append(n1, "");
+ QCOMPARE(h1.size(), 6);
+ // Leading and trailing space
+ h1.clear();
+ h1.append(n1, " foo ");
+ QCOMPARE(h1.combinedValue(n1), "foo");
+ h1.append(n1, "\tbar\t");
+ QCOMPARE(h1.combinedValue(n1), "foo, bar");
+ QCOMPARE(h1.size(), 2);
+
+ h1.clear();
+ QRegularExpression re("HTTP header value contained illegal character*");
+ TEST_ILLEGAL_HEADER_VALUE_CHARACTER("foo\x08" "bar"); // BS
+ TEST_ILLEGAL_HEADER_VALUE_CHARACTER("foo\x1B" "bar"); // ESC
+ // Disallowed UTF-8 characters
+ TEST_ILLEGAL_HEADER_VALUE_CHARACTER(u8"€");
+ TEST_ILLEGAL_HEADER_VALUE_CHARACTER(u8"𝒜𝒴𝟘𝟡𐎀𐎜𐒀𐒐𝓐𝓩𝔸𝔹𝕀𝕁𝕌𝕍𓂀𓂁𓃀𓃁𓇋𓇌𓉐𓉑𓋴𓋵𓎡𓎢𓎣𓏏");
+ // Disallowed UTF-16 characters
+ TEST_ILLEGAL_HEADER_VALUE_CHARACTER(u"€");
+ TEST_ILLEGAL_HEADER_VALUE_CHARACTER(u"𝒜𝒴𝟘𝟡𐎀𐎜𐒀𐒐𝓐𝓩𝔸𝔹𝕀𝕁𝕌𝕍𓂀𓂁𓃀𓃁𓇋𓇌𓉐𓉑𓋴𓋵𓎡𓎢𓎣𓏏");
+
+ // Non-null-terminated value. The 'x' below is to make sure the strings don't
+ // null-terminate by happenstance
+ h1.clear();
+ constexpr char L1Array[] = {'a','b','c','x'};
+ const QLatin1StringView nonNullLatin1{L1Array, sizeof(L1Array) - 1}; // abc
+
+ constexpr char UTF8Array[] = {0x64, 0x65, 0x66, 0x78};
+ const QUtf8StringView nonNullUTF8(UTF8Array, sizeof(UTF8Array) - 1); // def
+
+ constexpr QChar UTF16Array[] = {'g', 'h', 'i', 'x'};
+ QStringView nonNullUTF16(UTF16Array, sizeof(UTF16Array) / sizeof(QChar) - 1); // ghi
+
+ h1.append(n1, nonNullLatin1);
+ QCOMPARE(h1.size(), 1);
+ QVERIFY(h1.contains(n1));
+ QCOMPARE(h1.combinedValue(n1), "abc");
+
+ h1.append(n2, nonNullUTF8);
+ QCOMPARE(h1.size(), 2);
+ QVERIFY(h1.contains(n2));
+ QCOMPARE(h1.combinedValue(n2), "def");
+
+ h1.append(n3, nonNullUTF16);
+ QCOMPARE(h1.size(), 3);
+ QVERIFY(h1.contains(n3));
+ QCOMPARE(h1.combinedValue(n3), "ghi");
+}
+
+void tst_QHttpHeaders::valueEncoding()
+{
+ // Test that common encodings are possible to set and not blocked by
+ // header value character filter (ie. don't contain disallowed characters as per RFC 9110)
+ QHttpHeaders h1;
+ // Within visible ASCII range
+ QVERIFY(h1.append(n1, "foo"_ba.toBase64()));
+ QCOMPARE(h1.values(n1).at(0), "Zm9v");
+ h1.replace(0, n1, "foo"_ba.toPercentEncoding());
+ QCOMPARE(h1.values(n1).at(0), "foo");
+
+ // Outside of ASCII/Latin-1 range (€)
+ h1.replace(0, n1, "foo€"_ba.toBase64());
+ QCOMPARE(h1.values(n1).at(0), "Zm9v4oKs");
+ h1.replace(0, n1, "foo€"_ba.toPercentEncoding());
+ QCOMPARE(h1.values(n1).at(0), "foo%E2%82%AC");
+}
+
+void tst_QHttpHeaders::replaceOrAppend()
+{
+ QHttpHeaders h1;
+
+#define REPLACE_OR_APPEND(NAME, VALUE, INDEX, TOTALSIZE) \
+ do { \
+ QVERIFY(h1.replaceOrAppend(NAME, VALUE)); \
+ QCOMPARE(h1.size(), TOTALSIZE); \
+ QCOMPARE(h1.nameAt(INDEX), NAME); \
+ QCOMPARE(h1.valueAt(INDEX), VALUE); \
+ } while (false)
+
+ // Append to empty container and replace it
+ REPLACE_OR_APPEND(n1, v1, 0, 1); // Appends
+ REPLACE_OR_APPEND(n1, v2, 0, 1); // Replaces
+
+ // Replace at beginning, middle, and end
+ h1.clear();
+ REPLACE_OR_APPEND(n1, v1, 0, 1); // Appends
+ REPLACE_OR_APPEND(n2, v2, 1, 2); // Appends
+ REPLACE_OR_APPEND(n3, v3, 2, 3); // Appends
+ REPLACE_OR_APPEND(n1, V1, 0, 3); // Replaces at beginning
+ REPLACE_OR_APPEND(n2, V2, 1, 3); // Replaces at middle
+ REPLACE_OR_APPEND(n3, V3, 2, 3); // Replaces at end
+
+ // Pre-existing multiple values (n2) are removed
+ h1.clear();
+ h1.append(n1, v1);
+ h1.append(n2, v2); // First n2 is at index 1
+ h1.append(n2, v2);
+ h1.append(n3, v3);
+ h1.append(n2, v2);
+ QCOMPARE(h1.size(), 5);
+ QCOMPARE(h1.combinedValue(n2), "value2, value2, value2");
+ REPLACE_OR_APPEND(n2, V2, 1, 3); // Replaces value at index 1, and removes the rest
+ QCOMPARE(h1.combinedValue(n2), "VALUE2");
+#undef REPLACE_OR_APPEND
+
+ // Implicit sharing / detaching
+ h1.clear();
+ h1.append(n1, v1);
+ QHttpHeaders h2 = h1;
+ QCOMPARE(h1.size(), h2.size());
+ QCOMPARE(h1.valueAt(0), h2.valueAt(0)); // Iniially values are equal
+ h1.replaceOrAppend(n1, v2); // Change value in h1 => detaches h1
+ QCOMPARE_NE(h1.valueAt(0), h2.valueAt(0)); // Values are no more equal
+ QCOMPARE(h1.valueAt(0), v2); // Value in h1 changed
+ QCOMPARE(h2.valueAt(0), v1); // Value in h2 remained
+
+ // Failed attempts
+ h1.clear();
+ h1.append(n1, v1);
+ QRegularExpression re("HTTP header*");
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, re);
+ QVERIFY(!h1.replaceOrAppend("", V1));
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, re);
+ QVERIFY(!h1.replaceOrAppend(v1, "foo\x08"));
+}
+
+QTEST_MAIN(tst_QHttpHeaders)
+#include "tst_qhttpheaders.moc"
diff --git a/tests/auto/network/access/qhttpnetworkconnection/CMakeLists.txt b/tests/auto/network/access/qhttpnetworkconnection/CMakeLists.txt
index 29b345b0db..679990062f 100644
--- a/tests/auto/network/access/qhttpnetworkconnection/CMakeLists.txt
+++ b/tests/auto/network/access/qhttpnetworkconnection/CMakeLists.txt
@@ -1,4 +1,11 @@
-# Generated from qhttpnetworkconnection.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qhttpnetworkconnection LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
if(NOT QT_FEATURE_private_tests)
return()
@@ -11,12 +18,8 @@ endif()
qt_internal_add_test(tst_qhttpnetworkconnection
SOURCES
tst_qhttpnetworkconnection.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::CorePrivate
Qt::NetworkPrivate
- QT_TEST_SERVER_LIST "apache2" # special case
+ QT_TEST_SERVER_LIST "apache2"
)
-
-#### Keys ignored in scope 1:.:.:qhttpnetworkconnection.pro:<TRUE>:
-# QT_TEST_SERVER_LIST = "apache2"
-# _REQUIREMENTS = "qtConfig(private_tests)"
diff --git a/tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp b/tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp
index 945111bfb8..decd442164 100644
--- a/tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp
+++ b/tests/auto/network/access/qhttpnetworkconnection/tst_qhttpnetworkconnection.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
@@ -150,7 +125,7 @@ void tst_QHttpNetworkConnection::head()
QFETCH(QString, statusString);
QFETCH(int, contentLength);
- QHttpNetworkConnection connection(host, port, encrypt);
+ QHttpNetworkConnection connection(QHttpNetworkConnectionPrivate::defaultHttpChannelCount, host, port, encrypt);
QCOMPARE(connection.port(), port);
QCOMPARE(connection.hostName(), host);
QCOMPARE(connection.isSsl(), encrypt);
@@ -200,7 +175,7 @@ void tst_QHttpNetworkConnection::get()
QFETCH(int, contentLength);
QFETCH(int, downloadSize);
- QHttpNetworkConnection connection(host, port, encrypt);
+ QHttpNetworkConnection connection(QHttpNetworkConnectionPrivate::defaultHttpChannelCount, host, port, encrypt);
QCOMPARE(connection.port(), port);
QCOMPARE(connection.hostName(), host);
QCOMPARE(connection.isSsl(), encrypt);
@@ -266,7 +241,7 @@ void tst_QHttpNetworkConnection::put()
QFETCH(QString, data);
QFETCH(bool, succeed);
- QHttpNetworkConnection connection(host, port, encrypt);
+ QHttpNetworkConnection connection(QHttpNetworkConnectionPrivate::defaultHttpChannelCount, host, port, encrypt);
QCOMPARE(connection.port(), port);
QCOMPARE(connection.hostName(), host);
QCOMPARE(connection.isSsl(), encrypt);
@@ -348,7 +323,7 @@ void tst_QHttpNetworkConnection::post()
QFETCH(int, contentLength);
QFETCH(int, downloadSize);
- QHttpNetworkConnection connection(host, port, encrypt);
+ QHttpNetworkConnection connection(QHttpNetworkConnectionPrivate::defaultHttpChannelCount, host, port, encrypt);
QCOMPARE(connection.port(), port);
QCOMPARE(connection.hostName(), host);
QCOMPARE(connection.isSsl(), encrypt);
@@ -475,7 +450,7 @@ void tst_QHttpNetworkConnection::get401()
QFETCH(QString, password);
QFETCH(int, statusCode);
- QHttpNetworkConnection connection(host, port, encrypt);
+ QHttpNetworkConnection connection(QHttpNetworkConnectionPrivate::defaultHttpChannelCount, host, port, encrypt);
QCOMPARE(connection.port(), port);
QCOMPARE(connection.hostName(), host);
QCOMPARE(connection.isSsl(), encrypt);
@@ -535,7 +510,7 @@ void tst_QHttpNetworkConnection::compression()
QFETCH(bool, autoCompress);
QFETCH(QString, contentCoding);
- QHttpNetworkConnection connection(host, port, encrypt);
+ QHttpNetworkConnection connection(QHttpNetworkConnectionPrivate::defaultHttpChannelCount, host, port, encrypt);
QCOMPARE(connection.port(), port);
QCOMPARE(connection.hostName(), host);
QCOMPARE(connection.isSsl(), encrypt);
@@ -609,7 +584,7 @@ void tst_QHttpNetworkConnection::ignoresslerror()
QFETCH(bool, ignoreFromSignal);
QFETCH(int, statusCode);
- QHttpNetworkConnection connection(host, port, encrypt);
+ QHttpNetworkConnection connection(QHttpNetworkConnectionPrivate::defaultHttpChannelCount, host, port, encrypt);
QCOMPARE(connection.port(), port);
QCOMPARE(connection.hostName(), host);
if (ignoreInit)
@@ -654,7 +629,7 @@ void tst_QHttpNetworkConnection::nossl()
QFETCH(bool, encrypt);
QFETCH(QNetworkReply::NetworkError, networkError);
- QHttpNetworkConnection connection(host, port, encrypt);
+ QHttpNetworkConnection connection(QHttpNetworkConnectionPrivate::defaultHttpChannelCount, host, port, encrypt);
QCOMPARE(connection.port(), port);
QCOMPARE(connection.hostName(), host);
@@ -691,7 +666,7 @@ void tst_QHttpNetworkConnection::getMultiple_data()
static bool allRepliesFinished(const QList<QHttpNetworkReply*> *_replies)
{
const QList<QHttpNetworkReply*> &replies = *_replies;
- for (int i = 0; i < replies.length(); i++)
+ for (int i = 0; i < replies.size(); i++)
if (!replies.at(i)->isFinished())
return false;
return true;
@@ -760,7 +735,7 @@ void tst_QHttpNetworkConnection::getMultipleWithPipeliningAndMultiplePriorities(
QTRY_VERIFY_WITH_TIMEOUT(allRepliesFinished(&replies), 60000);
int pipelinedCount = 0;
- for (int i = 0; i < replies.length(); i++) {
+ for (int i = 0; i < replies.size(); i++) {
QVERIFY (!(replies.at(i)->request().isPipeliningAllowed() == false
&& replies.at(i)->isPipeliningUsed()));
@@ -944,7 +919,7 @@ void tst_QHttpNetworkConnection::getAndThenDeleteObject_data()
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());
+ QHttpNetworkConnection *connection = new QHttpNetworkConnection(QHttpNetworkConnectionPrivate::defaultHttpChannelCount, httpServerName());
QHttpNetworkRequest request("http://" + httpServerName() + "/qtest/bigfile");
QHttpNetworkReply *reply = connection->sendRequest(request);
reply->setDownstreamLimited(true);
diff --git a/tests/auto/network/access/qhttpnetworkreply/CMakeLists.txt b/tests/auto/network/access/qhttpnetworkreply/CMakeLists.txt
index 4d35cc8a2b..b4e4a822ee 100644
--- a/tests/auto/network/access/qhttpnetworkreply/CMakeLists.txt
+++ b/tests/auto/network/access/qhttpnetworkreply/CMakeLists.txt
@@ -1,4 +1,11 @@
-# Generated from qhttpnetworkreply.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qhttpnetworkreply LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
if(NOT QT_FEATURE_private_tests)
return()
@@ -11,10 +18,7 @@ endif()
qt_internal_add_test(tst_qhttpnetworkreply
SOURCES
tst_qhttpnetworkreply.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::CorePrivate
Qt::NetworkPrivate
)
-
-#### Keys ignored in scope 1:.:.:qhttpnetworkreply.pro:<TRUE>:
-# _REQUIREMENTS = "qtConfig(private_tests)"
diff --git a/tests/auto/network/access/qhttpnetworkreply/tst_qhttpnetworkreply.cpp b/tests/auto/network/access/qhttpnetworkreply/tst_qhttpnetworkreply.cpp
index 50132dde35..e83d15fdc3 100644
--- a/tests/auto/network/access/qhttpnetworkreply/tst_qhttpnetworkreply.cpp
+++ b/tests/auto/network/access/qhttpnetworkreply/tst_qhttpnetworkreply.cpp
@@ -1,35 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
#include <QtCore/QBuffer>
#include <QtCore/QByteArray>
+#include <QtCore/QStringBuilder>
#include "private/qhttpnetworkconnection_p.h"
@@ -40,6 +16,9 @@ private Q_SLOTS:
void parseHeader_data();
void parseHeader();
+ void parseHeaderVerification_data();
+ void parseHeaderVerification();
+
void parseEndOfHeader_data();
void parseEndOfHeader();
};
@@ -50,6 +29,7 @@ void tst_QHttpNetworkReply::parseHeader_data()
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() << "");
@@ -60,6 +40,9 @@ void tst_QHttpNetworkReply::parseHeader_data()
" 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"
@@ -94,13 +77,100 @@ void tst_QHttpNetworkReply::parseHeader()
QHttpNetworkReply reply;
reply.parseHeader(headers);
- for (int i = 0; i < fields.count(); ++i) {
+ 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:
diff --git a/tests/auto/network/access/qnetworkaccessmanager/CMakeLists.txt b/tests/auto/network/access/qnetworkaccessmanager/CMakeLists.txt
index 1d819c3784..b0fe6eda46 100644
--- a/tests/auto/network/access/qnetworkaccessmanager/CMakeLists.txt
+++ b/tests/auto/network/access/qnetworkaccessmanager/CMakeLists.txt
@@ -1,12 +1,19 @@
-# Generated from qnetworkaccessmanager.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qnetworkaccessmanager Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qnetworkaccessmanager LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qnetworkaccessmanager
SOURCES
tst_qnetworkaccessmanager.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Network
)
diff --git a/tests/auto/network/access/qnetworkaccessmanager/tst_qnetworkaccessmanager.cpp b/tests/auto/network/access/qnetworkaccessmanager/tst_qnetworkaccessmanager.cpp
index fcafea0125..43db6d5841 100644
--- a/tests/auto/network/access/qnetworkaccessmanager/tst_qnetworkaccessmanager.cpp
+++ b/tests/auto/network/access/qnetworkaccessmanager/tst_qnetworkaccessmanager.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
diff --git a/tests/auto/network/access/qnetworkcachemetadata/CMakeLists.txt b/tests/auto/network/access/qnetworkcachemetadata/CMakeLists.txt
index 3a8bef0aa2..2aa918c49c 100644
--- a/tests/auto/network/access/qnetworkcachemetadata/CMakeLists.txt
+++ b/tests/auto/network/access/qnetworkcachemetadata/CMakeLists.txt
@@ -1,12 +1,19 @@
-# Generated from qnetworkcachemetadata.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qnetworkcachemetadata Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qnetworkcachemetadata LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qnetworkcachemetadata
SOURCES
tst_qnetworkcachemetadata.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Network
)
diff --git a/tests/auto/network/access/qnetworkcachemetadata/tst_qnetworkcachemetadata.cpp b/tests/auto/network/access/qnetworkcachemetadata/tst_qnetworkcachemetadata.cpp
index e35ce84898..f811943dea 100644
--- a/tests/auto/network/access/qnetworkcachemetadata/tst_qnetworkcachemetadata.cpp
+++ b/tests/auto/network/access/qnetworkcachemetadata/tst_qnetworkcachemetadata.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
diff --git a/tests/auto/network/access/qnetworkcookie/CMakeLists.txt b/tests/auto/network/access/qnetworkcookie/CMakeLists.txt
index 0460ff3235..91773a83fd 100644
--- a/tests/auto/network/access/qnetworkcookie/CMakeLists.txt
+++ b/tests/auto/network/access/qnetworkcookie/CMakeLists.txt
@@ -1,12 +1,19 @@
-# Generated from qnetworkcookie.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qnetworkcookie Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qnetworkcookie LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qnetworkcookie
SOURCES
tst_qnetworkcookie.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Network
)
diff --git a/tests/auto/network/access/qnetworkcookie/tst_qnetworkcookie.cpp b/tests/auto/network/access/qnetworkcookie/tst_qnetworkcookie.cpp
index b71934fc15..438c5e6983 100644
--- a/tests/auto/network/access/qnetworkcookie/tst_qnetworkcookie.cpp
+++ b/tests/auto/network/access/qnetworkcookie/tst_qnetworkcookie.cpp
@@ -1,36 +1,13 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
-#include <QtCore/QUrl>
#include <QtNetwork/QNetworkCookie>
+#include <QtCore/QDateTime>
+#include <QtCore/QTimeZone>
+#include <QtCore/QUrl>
class tst_QNetworkCookie: public QObject
{
@@ -110,6 +87,12 @@ 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");
@@ -254,140 +237,140 @@ void tst_QNetworkCookie::parseSingleCookie_data()
cookie = QNetworkCookie();
cookie.setName("a");
cookie.setValue("b");
- cookie.setExpirationDate(QDateTime(QDate(2012, 1, 29), QTime(23, 59, 59), Qt::UTC));
+ 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(QDateTime(QDate(1999, 11, 9), QTime(23, 12, 40), Qt::UTC));
+ 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(QDateTime(QDate(1989, 4, 14), QTime(3, 20, 0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 4, 14, 3, 20));
QTest::newRow("time-0") << "a=b;expires=14 Apr 89 03:20" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 4, 14), QTime(3, 20, 12, 0), Qt::UTC));
+ 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(QDateTime(QDate(1989, 4, 14), QTime(3, 20, 12, 88), Qt::UTC));
+ 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(QDateTime(QDate(1989, 4, 14), QTime(3, 20, 12, 88), Qt::UTC));
+ 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(QDateTime(QDate(1989, 4, 14), QTime(15, 20, 12, 88), Qt::UTC));
+ 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(QDateTime(QDate(1989, 4, 14), QTime(3, 20, 12, 88), Qt::UTC));
+ 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(QDateTime(QDate(1989, 4, 14), QTime(15, 20, 12, 88), Qt::UTC));
+ 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(QDateTime(QDate(1989, 4, 14), QTime(15, 20, 12, 88), Qt::UTC));
+ 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(QDateTime(QDate(1989, 1, 1), QTime(1, 1), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 1, 1, 1));
QTest::newRow("months-1") << "a=b;expires=Jan 1 89 1:1" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 2, 1), QTime(1, 1), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 2, 1, 1, 1));
QTest::newRow("months-2") << "a=b;expires=Feb 1 89 1:1" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 3, 1), QTime(1, 1), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 3, 1, 1, 1));
QTest::newRow("months-3") << "a=b;expires=mar 1 89 1:1" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 4, 1), QTime(1, 1), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 4, 1, 1, 1));
QTest::newRow("months-4") << "a=b;expires=Apr 1 89 1:1" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 5, 1), QTime(1, 1), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 5, 1, 1, 1));
QTest::newRow("months-5") << "a=b;expires=May 1 89 1:1" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 6, 1), QTime(1, 1), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 6, 1, 1, 1));
QTest::newRow("months-6") << "a=b;expires=Jun 1 89 1:1" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 7, 1), QTime(1, 1), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 7, 1, 1, 1));
QTest::newRow("months-7") << "a=b;expires=Jul 1 89 1:1" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 8, 1), QTime(1, 1), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 8, 1, 1, 1));
QTest::newRow("months-8") << "a=b;expires=Aug 1 89 1:1" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 9, 1), QTime(1, 1), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 9, 1, 1, 1));
QTest::newRow("months-9") << "a=b;expires=Sep 1 89 1:1" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 10, 1), QTime(1, 1), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 10, 1, 1, 1));
QTest::newRow("months-10") << "a=b;expires=Oct 1 89 1:1" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 11, 1), QTime(1, 1), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 11, 1, 1, 1));
QTest::newRow("months-11") << "a=b;expires=Nov 1 89 1:1" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 12, 1), QTime(1, 1), Qt::UTC));
+ 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(QDateTime(QDate(1989, 12, 1), QTime(1, 1), Qt::UTC));
+ 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(QDateTime(QDate(2024, 2, 29), QTime(1, 1), Qt::UTC));
+ cookie.setExpirationDate(utc(2024, 2, 29, 1, 1));
QTest::newRow("months-16") << "a=b;expires=2024 29 Feb 1:1" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(2024, 2, 29), QTime(1, 1), Qt::UTC));
+ 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(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 1));
QTest::newRow("zoneoffset-0") << "a=b;expires=Jan 1 89 8:0 PST" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 1));
QTest::newRow("zoneoffset-1") << "a=b;expires=Jan 1 89 8:0 PDT" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 1));
QTest::newRow("zoneoffset-2") << "a=b;expires=Jan 1 89 7:0 MST" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 1));
QTest::newRow("zoneoffset-3") << "a=b;expires=Jan 1 89 7:0 MDT" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 1));
QTest::newRow("zoneoffset-4") << "a=b;expires=Jan 1 89 6:0 CST" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 1));
QTest::newRow("zoneoffset-5") << "a=b;expires=Jan 1 89 6:0 CDT" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 1));
QTest::newRow("zoneoffset-6") << "a=b;expires=Jan 1 89 5:0 EST" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 1));
QTest::newRow("zoneoffset-7") << "a=b;expires=Jan 1 89 5:0 EDT" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 1));
QTest::newRow("zoneoffset-8") << "a=b;expires=Jan 1 89 4:0 AST" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 1));
QTest::newRow("zoneoffset-9") << "a=b;expires=Jan 1 89 3:0 NST" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 1));
QTest::newRow("zoneoffset-10") << "a=b;expires=Jan 1 89 0:0 GMT" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 1));
QTest::newRow("zoneoffset-11") << "a=b;expires=Jan 1 89 0:0 BST" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 1, 2), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 2));
QTest::newRow("zoneoffset-12") << "a=b;expires=Jan 1 89 23:0 MET" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 1, 2), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 2));
QTest::newRow("zoneoffset-13") << "a=b;expires=Jan 1 89 22:0 EET" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 1, 2), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 2));
QTest::newRow("zoneoffset-14") << "a=b;expires=Jan 1 89 15:0 JST" << cookie;
// extra offsets
- cookie.setExpirationDate(QDateTime(QDate(1989, 1, 2), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 2));
QTest::newRow("zoneoffset-15") << "a=b;expires=Jan 1 89 15:0 JST+1" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(1, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 1, 1));
QTest::newRow("zoneoffset-16") << "a=b;expires=Jan 1 89 0:0 GMT+1" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 1));
QTest::newRow("zoneoffset-17") << "a=b;expires=Jan 1 89 1:0 GMT-1" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(1, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 1, 1));
QTest::newRow("zoneoffset-18") << "a=b;expires=Jan 1 89 0:0 GMT+01" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(1, 5), Qt::UTC));
+ 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(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 1));
QTest::newRow("zoneoffset-20") << "a=b;expires=Jan 1 89 0:0 GMT+015" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 1));
QTest::newRow("zoneoffset-21") << "a=b;expires=Jan 1 89 0:0 GM" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC));
+ 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(QDateTime(QDate(1989, 1, 1), QTime(1, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 1, 1));
QTest::newRow("zoneoffset-23") << "a=b;expires=Jan 1 89 0:0 +1" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(1, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 1, 1));
QTest::newRow("zoneoffset-24") << "a=b;expires=Jan 1 89 0:0 +01" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(1, 1), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 1, 1, 1));
QTest::newRow("zoneoffset-25") << "a=b;expires=Jan 1 89 0:0 +0101" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 1, 1), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 1, 1));
QTest::newRow("zoneoffset-26") << "a=b;expires=Jan 1 89 1:0 -1" << cookie;
// Y2k
- cookie.setExpirationDate(QDateTime(QDate(2000, 1, 1), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(2000, 1, 1));
QTest::newRow("year-0") << "a=b;expires=Jan 1 00 0:0" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1970, 1, 1), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1970, 1, 1));
QTest::newRow("year-1") << "a=b;expires=Jan 1 70 0:0" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1971, 1, 1), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1971, 1, 1));
QTest::newRow("year-2") << "a=b;expires=Jan 1 71 0:0" << cookie;
// Day, month, year
- cookie.setExpirationDate(QDateTime(QDate(2013, 1, 2), QTime(0, 0), Qt::UTC));
+ 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;
@@ -397,141 +380,141 @@ void tst_QNetworkCookie::parseSingleCookie_data()
QTest::newRow("date-6") << "a=b;expires=1/2/13 0:0" << cookie;
// Known Year, determine month and day
- cookie.setExpirationDate(QDateTime(QDate(1995, 1, 13), QTime(0, 0), Qt::UTC));
+ 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(QDateTime(QDate(1995, 1, 2), QTime(0, 0), Qt::UTC));
+ 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(QDateTime(QDate(1995, 1, 13), QTime(0, 0), Qt::UTC));
+ 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(QDateTime(QDate(2021, 03, 26), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(2021, 03, 26));
QTest::newRow("month-0") << "a=b;expires=26/03/21 0:0" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(2015, 12, 30), QTime(16, 25, 0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(2015, 12, 30, 16, 25));
QTest::newRow("month-1") << "a=b;expires=wed 16:25pm December 2015 30" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(2031, 11, 11), QTime(16, 25, 0, 0), Qt::UTC));
+ 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(QDateTime(QDate(2013, 10, 2), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(2013, 10, 2));
QTest::newRow("ambiguousd-0") << "a=b;expires=10/2/13 0:0" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(2013, 2, 10), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(2013, 2, 10));
QTest::newRow("ambiguousd-1") << "a=b;expires=2/10/13 0:0" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(2010, 2, 3), QTime(0, 0), Qt::UTC));
+ 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(QDateTime(QDate(2003, 2, 10), QTime(0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(2003, 2, 10));
QTest::newRow("ambiguousd-3") << "a=b;expires=2/10/3 0:0" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(2003, 10, 2), QTime(0, 0), Qt::UTC));
+ 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(QDateTime(QDate(1989, 4, 14), QTime(3, 20, 0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 4, 14, 3, 20));
QTest::newRow("firefox-0") << "a=b;expires=14 Apr 89 03:20" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 4, 14), QTime(3, 20, 0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1989, 4, 14, 3, 20));
QTest::newRow("firefox-1") << "a=b;expires=14 Apr 89 03:20 GMT" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1989, 3, 17), QTime(4, 1, 33, 0), Qt::UTC));
+ 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(QDateTime(QDate(1989, 3, 17), QTime(4, 1, 0, 0), Qt::UTC));
+ 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(QDateTime(QDate(1989, 1, 16), QTime(16-8, 12, 0, 0), Qt::UTC));
+ 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(QDateTime(QDate(1989, 1, 16), QTime(17, 42, 0, 0), Qt::UTC));
+ 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(QDateTime(QDate(1992, 5, 6), QTime(16-9, 41, 0, 0), Qt::UTC));
+ 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(QDateTime(QDate(1993, 8, 22), QTime(10, 59, 12, 82), Qt::UTC));
+ 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(QDateTime(QDate(1993, 8, 22), QTime(22, 59, 0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1993, 8, 22, 22, 59));
QTest::newRow("firefox-8") << "a=b;expires=22-AUG-1993 10:59pm" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1993, 8, 22), QTime(12, 59, 0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1993, 8, 22, 12, 59));
QTest::newRow("firefox-9") << "a=b;expires=22-AUG-1993 12:59am" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1993, 8, 22), QTime(12, 59, 0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1993, 8, 22, 12, 59));
QTest::newRow("firefox-10") << "a=b;expires=22-AUG-1993 12:59 PM" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1995, 8, 4), QTime(15, 54, 0, 0), Qt::UTC));
+ 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(QDateTime(QDate(1995, 6, 21), QTime(16, 24, 34, 0), Qt::UTC));
+ 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(QDateTime(QDate(1995, 6, 20), QTime(21, 7, 0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(1995, 6, 20, 21, 7));
QTest::newRow("firefox-13") << "a=b;expires=20/06/95 21:07" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(1995, 6, 8), QTime(19-5, 32, 48, 0), Qt::UTC));
+ 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(QDateTime(QDate(2030, 2, 25), QTime(1, 1, 0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(2030, 2, 25, 1, 1));
QTest::newRow("fuzz-0") << "a=b; expires=30 -000002 1:1 25;" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(2031, 11, 20), QTime(1, 1, 0, 0), Qt::UTC));
+ 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(QDateTime(QDate(2031, 4, 30), QTime(1, 1, 0, 0), Qt::UTC));
+ 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(QDateTime(QDate(2031, 9, 21), QTime(1, 1, 0, 0), Qt::UTC));
+ 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(QDateTime(QDate(2031, 11, 1), QTime(1, 1, 0, 0), Qt::UTC));
+ 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(QDateTime(QDate(2030, 2, 20), QTime(1, 1, 0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(2030, 2, 20, 1, 1));
QTest::newRow("fuzz-5") << "a=b; expires=30 02 20 1:1" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(2021, 12, 22), QTime(1, 1, 0, 0), Qt::UTC));
+ cookie.setExpirationDate(utc(2021, 12, 22, 1, 1));
QTest::newRow("fuzz-6") << "a=b; expires=2021 12 22 1:1" << cookie;
- cookie.setExpirationDate(QDateTime(QDate(2029, 2, 23), QTime(1, 1, 0, 0), Qt::UTC));
+ 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(QDateTime(QDate(2031, 11, 06), QTime(1, 1, 0, 0), Qt::UTC));
+ 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(QDateTime(QDate(1999, 11, 9), QTime(23, 12, 40), Qt::UTC));
+ 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(QDateTime(QDate(1970, 1, 1), QTime(23, 12, 40), Qt::UTC));
+ 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(QDateTime(QDate(2000, 1, 1), QTime(23, 12, 40), Qt::UTC));
+ 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(QDateTime(QDate(2020, 1, 1), QTime(23, 12, 40), Qt::UTC));
+ 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(QDateTime(QDate(2069, 1, 1), QTime(23, 12, 40), Qt::UTC));
+ 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(QDateTime(QDate(1999, 11, 9), QTime(23, 12, 40), Qt::UTC));
+ 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;
@@ -544,7 +527,7 @@ void tst_QNetworkCookie::parseSingleCookie_data()
// cookies obtained from the network:
cookie = QNetworkCookie("__siteid", "1");
cookie.setPath("/");
- cookie.setExpirationDate(QDateTime(QDate(9999, 12, 31), QTime(23, 59, 59), Qt::UTC));
+ 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");
@@ -554,13 +537,13 @@ void tst_QNetworkCookie::parseSingleCookie_data()
cookie = QNetworkCookie("__ac", "\"c2hhdXNtYW46U2FTYW80Wm8%3D\"");
cookie.setPath("/");
- cookie.setExpirationDate(QDateTime(QDate(2008, 8, 30), QTime(20, 21, 49), Qt::UTC));
+ 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(QDateTime(QDate(2011, 3, 1), QTime(10, 51, 14), Qt::UTC));
+ 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
@@ -578,11 +561,11 @@ void tst_QNetworkCookie::parseSingleCookie()
QList<QNetworkCookie> result = QNetworkCookie::parseCookies(cookieString.toUtf8());
//QEXPECT_FAIL("network2", "QDateTime parsing problem: the date is beyond year 8000", Abort);
- QCOMPARE(result.count(), 1);
+ QCOMPARE(result.size(), 1);
QCOMPARE(result.at(0), expectedCookie);
result = QNetworkCookie::parseCookies(result.at(0).toRawForm());
- QCOMPARE(result.count(), 1);
+ QCOMPARE(result.size(), 1);
// Drop any millisecond information, if there's any
QDateTime dt = expectedCookie.expirationDate();
@@ -636,7 +619,7 @@ void tst_QNetworkCookie::parseMultipleCookies_data()
cookie = QNetworkCookie("id", "51706646077999719");
cookie.setDomain(".bluestreak.com");
cookie.setPath("/");
- cookie.setExpirationDate(QDateTime(QDate(2017, 12, 05), QTime(9, 11, 7), Qt::UTC));
+ 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|");
@@ -655,8 +638,8 @@ void tst_QNetworkCookie::parseMultipleCookies_data()
cookieB.setValue("d");
// NewLine
- cookieA.setExpirationDate(QDateTime(QDate(2009, 3, 10), QTime(7, 0, 0, 0), Qt::UTC));
- cookieB.setExpirationDate(QDateTime(QDate(2009, 3, 20), QTime(7, 0, 0, 0), Qt::UTC));
+ 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;
@@ -688,11 +671,11 @@ void tst_QNetworkCookie::parseMultipleCookies()
void tst_QNetworkCookie::sameSite()
{
QList<QNetworkCookie> result = QNetworkCookie::parseCookies(QByteArrayLiteral("a=b;domain=qt-project.org"));
- QCOMPARE(result.first().sameSite(), QNetworkCookie::SameSite::Default);
+ QCOMPARE(result.first().sameSitePolicy(), QNetworkCookie::SameSite::Default);
result = QNetworkCookie::parseCookies(QByteArrayLiteral("a=b;domain=qt-project.org;samesite=strict"));
- QCOMPARE(result.first().sameSite(), QNetworkCookie::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().sameSite(), QNetworkCookie::SameSite::None);
+ QCOMPARE(result.first().sameSitePolicy(), QNetworkCookie::SameSite::None);
QCOMPARE(result.first().toRawForm(), QByteArrayLiteral("a=b; secure; SameSite=None; domain=qt-project.org"));
}
diff --git a/tests/auto/network/access/qnetworkcookiejar/CMakeLists.txt b/tests/auto/network/access/qnetworkcookiejar/CMakeLists.txt
index 55f30633ee..0d74a1d84d 100644
--- a/tests/auto/network/access/qnetworkcookiejar/CMakeLists.txt
+++ b/tests/auto/network/access/qnetworkcookiejar/CMakeLists.txt
@@ -1,16 +1,23 @@
-# Generated from qnetworkcookiejar.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qnetworkcookiejar Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qnetworkcookiejar LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
# Collect test data
-list(APPEND test_data "parser.json")
+list(APPEND test_data "parser.json" "testdata/publicsuffix/public_suffix_list.dafsa")
qt_internal_add_test(tst_qnetworkcookiejar
SOURCES
tst_qnetworkcookiejar.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::CorePrivate
Qt::Network
Qt::NetworkPrivate
diff --git a/tests/auto/network/access/qnetworkcookiejar/testdata/publicsuffix/public_suffix_list.dafsa b/tests/auto/network/access/qnetworkcookiejar/testdata/publicsuffix/public_suffix_list.dafsa
new file mode 100644
index 0000000000..2bd4ca05f3
--- /dev/null
+++ b/tests/auto/network/access/qnetworkcookiejar/testdata/publicsuffix/public_suffix_list.dafsa
Binary files differ
diff --git a/tests/auto/network/access/qnetworkcookiejar/tst_qnetworkcookiejar.cpp b/tests/auto/network/access/qnetworkcookiejar/tst_qnetworkcookiejar.cpp
index d4e9698ca3..9460060dbf 100644
--- a/tests/auto/network/access/qnetworkcookiejar/tst_qnetworkcookiejar.cpp
+++ b/tests/auto/network/access/qnetworkcookiejar/tst_qnetworkcookiejar.cpp
@@ -1,31 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
#include <QtCore/QJsonArray>
@@ -37,14 +11,17 @@
#include <QtNetwork/QNetworkRequest>
#if QT_CONFIG(topleveldomain)
#include "private/qtldurl_p.h"
-#include "private/qurltlds_p.h"
#endif
+#include <memory>
+
class tst_QNetworkCookieJar: public QObject
{
Q_OBJECT
private slots:
+ void initTestCase();
+
void getterSetter();
void setCookiesFromUrl_data();
void setCookiesFromUrl();
@@ -56,6 +33,8 @@ private slots:
#endif
void rfc6265_data();
void rfc6265();
+private:
+ QSharedPointer<QTemporaryDir> m_dataDir;
};
class MyCookieJar: public QNetworkCookieJar
@@ -83,6 +62,22 @@ void tst_QNetworkCookieJar::getterSetter()
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");
@@ -241,7 +236,7 @@ void tst_QNetworkCookieJar::setCookiesFromUrl()
QFETCH(QList<QNetworkCookie>, preset);
QFETCH(QNetworkCookie, newCookie);
QFETCH(QString, referenceUrl);
- QFETCH(QList<QNetworkCookie>, expectedResult);
+ QFETCH(const QList<QNetworkCookie>, expectedResult);
QFETCH(bool, setCookies);
QList<QNetworkCookie> cookieList;
@@ -251,11 +246,11 @@ void tst_QNetworkCookieJar::setCookiesFromUrl()
QCOMPARE(jar.setCookiesFromUrl(cookieList, referenceUrl), setCookies);
QList<QNetworkCookie> result = jar.allCookies();
- foreach (QNetworkCookie cookie, expectedResult) {
+ for (const QNetworkCookie &cookie : expectedResult) {
QVERIFY2(result.contains(cookie), cookie.toRawForm());
result.removeAll(cookie);
}
- QVERIFY2(result.isEmpty(), QTest::toString(result));
+ QVERIFY2(result.isEmpty(), std::unique_ptr<char[]>(QTest::toString(result)).get());
}
void tst_QNetworkCookieJar::cookiesForUrl_data()
@@ -414,13 +409,11 @@ void tst_QNetworkCookieJar::effectiveTLDs_data()
QTest::newRow("yes1") << "com" << true;
QTest::newRow("yes2") << "de" << true;
- QTest::newRow("yes3") << "ulm.museum" << 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("yes9") << "wallonie.museum" << true;
QTest::newRow("yes10") << "hk.com" << true;
QTest::newRow("yes11") << "hk.org" << true;
@@ -437,33 +430,23 @@ void tst_QNetworkCookieJar::effectiveTLDs_data()
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 s2[] = {0x5d9, 0x5e8, 0x5d5, 0x5e9, 0x5dc, 0x5d9, 0x5dd, 0x2e, 0x6d, 0x75, 0x73, 0x65, 0x75, 0x6d, 0x00}; // xn--9dbhblg6di.museum
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
- const char16_t s8[] = {0x63, 0x6f, 0x72, 0x72, 0x65, 0x69, 0x6f, 0x73, 0x2d, 0x65, 0x2d, 0x74, 0x65, 0x6c, 0x65,
- 0x63, 0x6f, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0xe7, 0xf5, 0x65, 0x73, 0x2e, 0x6d, 0x75,
- 0x73, 0x65, 0x75, 0x6d, 0x00}; // xn--correios-e-telecomunicaes-ghc29a.museum
QTest::newRow("yes-specialchars1") << QString::fromUtf16(s1) << true;
- QTest::newRow("yes-specialchars2") << QString::fromUtf16(s2) << 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("yes-specialchars8") << QString::fromUtf16(s8) << true;
QTest::newRow("no-specialchars1") << QString::fromUtf16(s1).prepend("something") << false;
- QTest::newRow("no-specialchars2") << QString::fromUtf16(s2).prepend(QString::fromUtf16(s2)) << false;
- QTest::newRow("no-specialchars2.5") << QString::fromUtf16(s2).prepend("whatever") << false;
QTest::newRow("no-specialchars3") << QString::fromUtf16(s3).prepend("foo") << false;
QTest::newRow("no-specialchars4") << QString::fromUtf16(s4).prepend("bar") << false;
- QTest::newRow("no-specialchars5") << QString::fromUtf16(s5).prepend(QString::fromUtf16(s2)) << 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("no-specialchars8") << QString::fromUtf16(s8).append("foo") << false;
QTest::newRow("exception1") << "pref.iwate.jp" << false;
QTest::newRow("exception2") << "omanpost.om" << false;
@@ -479,28 +462,6 @@ void tst_QNetworkCookieJar::effectiveTLDs_data()
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;
-
- int inFirst = 0; // First group is guaranteed to be in first chunk.
- while (tldIndices[inFirst] < tldChunks[0])
- ++inFirst;
- Q_ASSERT(inFirst < tldCount);
- const char *lastGroupFromFirstChunk = &tldData[0][tldIndices[inFirst - 1]];
- const char *cut = &tldData[0][tldChunks[0]];
- for (const char *entry = lastGroupFromFirstChunk; entry < cut; entry += strlen(entry) + 1)
- QTest::addRow("lastGroupFromFirstChunk: %s", entry) << entry << true;
-
- Q_ASSERT(tldChunkCount > 1); // There are enough TLDs to fill 64K bytes
- // The tldCount + 1 entries in tldIndices are indexed by hash value and some
- // hash cells may be empty: we need to find the last non-empty hash cell.
- int tail = tldCount;
- while (tldIndices[tail - 1] == tldIndices[tail])
- --tail;
- Q_ASSERT(tldIndices[tail] == tldChunks[tldChunkCount - 1]);
- const char *lastGroupFromLastChunk =
- &tldData[tldChunkCount-1][tldIndices[tail - 1] - tldChunks[tldChunkCount - 2]];
- const char *end = &tldData[tldChunkCount-1][tldIndices[tail] - tldChunks[tldChunkCount - 2]];
- for (const char *entry = lastGroupFromLastChunk; entry < end; entry += strlen(entry) + 1)
- QTest::addRow("lastGroupFromLastChunk: %s", entry) << entry << true;
}
void tst_QNetworkCookieJar::effectiveTLDs()
@@ -556,7 +517,7 @@ void tst_QNetworkCookieJar::rfc6265_data()
void tst_QNetworkCookieJar::rfc6265()
{
- QFETCH(QStringList, received);
+ QFETCH(const QStringList, received);
QFETCH(QList<QNetworkCookie>, sent);
QFETCH(QString, sentTo);
@@ -567,16 +528,16 @@ void tst_QNetworkCookieJar::rfc6265()
QNetworkCookieJar jar;
QList<QNetworkCookie> receivedCookies;
- foreach (const QString &cookieLine, received)
+ for (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.count(), sent.count());
+ QCOMPARE(cookiesToSend.size(), sent.size());
bool ok = true;
- for (int i = 0; i < cookiesToSend.count(); i++) {
+ for (int i = 0; i < cookiesToSend.size(); i++) {
if (cookiesToSend.at(i).name() != sent.at(i).name()) {
ok = false;
break;
diff --git a/tests/auto/network/access/qnetworkdiskcache/CMakeLists.txt b/tests/auto/network/access/qnetworkdiskcache/CMakeLists.txt
index af58132fb3..023868f57e 100644
--- a/tests/auto/network/access/qnetworkdiskcache/CMakeLists.txt
+++ b/tests/auto/network/access/qnetworkdiskcache/CMakeLists.txt
@@ -1,12 +1,19 @@
-# Generated from qnetworkdiskcache.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qnetworkdiskcache Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qnetworkdiskcache LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qnetworkdiskcache
SOURCES
tst_qnetworkdiskcache.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Network
)
diff --git a/tests/auto/network/access/qnetworkdiskcache/tst_qnetworkdiskcache.cpp b/tests/auto/network/access/qnetworkdiskcache/tst_qnetworkdiskcache.cpp
index f15d4dc7ac..ec32c780cd 100644
--- a/tests/auto/network/access/qnetworkdiskcache/tst_qnetworkdiskcache.cpp
+++ b/tests/auto/network/access/qnetworkdiskcache/tst_qnetworkdiskcache.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtNetwork/QtNetwork>
#include <QTest>
@@ -35,8 +10,9 @@
#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 16
+#define NUM_SUBDIRECTORIES 15
class tst_QNetworkDiskCache : public QObject
{
@@ -141,7 +117,7 @@ class SubQNetworkDiskCache : public QNetworkDiskCache
public:
~SubQNetworkDiskCache()
{
- if (!cacheDirectory().isEmpty())
+ if (!cacheDirectory().isEmpty() && clearOnDestruction)
clear();
}
@@ -170,6 +146,11 @@ public:
d->write("Hello World!");
insert(d);
}
+
+ void setClearCacheOnDestruction(bool value) { clearOnDestruction = value; }
+
+private:
+ bool clearOnDestruction = true;
};
tst_QNetworkDiskCache::tst_QNetworkDiskCache()
@@ -241,17 +222,39 @@ void tst_QNetworkDiskCache::prepare()
// public qint64 cacheSize() const
void tst_QNetworkDiskCache::cacheSize()
{
- SubQNetworkDiskCache cache;
- cache.setCacheDirectory(tempDir.path());
- QCOMPARE(cache.cacheSize(), qint64(0));
+ 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);
- QVERIFY(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));
}
@@ -275,17 +278,17 @@ void tst_QNetworkDiskCache::clear()
QVERIFY(cache.cacheSize() > qint64(0));
QString cacheDirectory = cache.cacheDirectory();
- QCOMPARE(countFiles(cacheDirectory).count(), NUM_SUBDIRECTORIES + 3);
+ QCOMPARE(countFiles(cacheDirectory).size(), NUM_SUBDIRECTORIES + 3);
cache.clear();
- QCOMPARE(countFiles(cacheDirectory).count(), NUM_SUBDIRECTORIES + 2);
+ 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).count(), NUM_SUBDIRECTORIES + 3);
+ QCOMPARE(countFiles(cacheDirectory).size(), NUM_SUBDIRECTORIES + 3);
cache.clear();
- QCOMPARE(countFiles(cacheDirectory).count(), NUM_SUBDIRECTORIES + 3);
+ QCOMPARE(countFiles(cacheDirectory).size(), NUM_SUBDIRECTORIES + 3);
}
}
@@ -352,9 +355,9 @@ void tst_QNetworkDiskCache::remove()
QUrl url(EXAMPLE_URL);
cache.setupWithOne(tempDir.path(), url);
QString cacheDirectory = cache.cacheDirectory();
- QCOMPARE(countFiles(cacheDirectory).count(), NUM_SUBDIRECTORIES + 3);
+ QCOMPARE(countFiles(cacheDirectory).size(), NUM_SUBDIRECTORIES + 3);
cache.remove(url);
- QCOMPARE(countFiles(cacheDirectory).count(), NUM_SUBDIRECTORIES + 2);
+ QCOMPARE(countFiles(cacheDirectory).size(), NUM_SUBDIRECTORIES + 2);
}
void tst_QNetworkDiskCache::accessAfterRemove() // QTBUG-17400
@@ -473,9 +476,9 @@ void tst_QNetworkDiskCache::fileMetaData()
url.setFragment(QString());
QString cacheDirectory = cache.cacheDirectory();
- QStringList list = countFiles(cacheDirectory);
- QCOMPARE(list.count(), NUM_SUBDIRECTORIES + 3);
- foreach(QString fileName, list) {
+ const QStringList list = countFiles(cacheDirectory);
+ QCOMPARE(list.size(), NUM_SUBDIRECTORIES + 3);
+ for (const QString &fileName : list) {
QFileInfo info(fileName);
if (info.isFile()) {
QNetworkCacheMetaData metaData = cache.call_fileMetaData(fileName);
@@ -518,9 +521,9 @@ void tst_QNetworkDiskCache::expire()
}
QString cacheDirectory = cache.cacheDirectory();
- QStringList list = countFiles(cacheDirectory);
+ const QStringList list = countFiles(cacheDirectory);
QStringList cacheList;
- foreach(QString fileName, list) {
+ for (const QString &fileName : list) {
QFileInfo info(fileName);
if (info.isFile()) {
QNetworkCacheMetaData metaData = cache.call_fileMetaData(fileName);
@@ -528,7 +531,7 @@ void tst_QNetworkDiskCache::expire()
}
}
std::sort(cacheList.begin(), cacheList.end());
- for (int i = 0; i < cacheList.count(); ++i) {
+ for (int i = 0; i < cacheList.size(); ++i) {
QString fileName = cacheList[i];
QCOMPARE(fileName, QLatin1String("http://localhost:4/") + QString::number(i + 6));
}
@@ -566,11 +569,11 @@ void tst_QNetworkDiskCache::oldCacheVersionFile()
QVERIFY(!metaData.isValid());
QVERIFY(!QFile::exists(name));
} else {
- QStringList files = countFiles(cache.cacheDirectory());
- QCOMPARE(files.count(), NUM_SUBDIRECTORIES + 3);
+ const QStringList files = countFiles(cache.cacheDirectory());
+ QCOMPARE(files.size(), NUM_SUBDIRECTORIES + 3);
// find the file
QString cacheFile;
- foreach (QString file, files) {
+ for (const QString &file : files) {
QFileInfo info(file);
if (info.isFile())
cacheFile = file;
@@ -607,8 +610,8 @@ void tst_QNetworkDiskCache::streamVersion()
QString cacheFile;
// find the file
- QStringList files = countFiles(cache.cacheDirectory());
- foreach (const QString &file, files) {
+ const QStringList files = countFiles(cache.cacheDirectory());
+ for (const QString &file : files) {
QFileInfo info(file);
if (info.isFile()) {
cacheFile = file;
@@ -654,6 +657,7 @@ void tst_QNetworkDiskCache::streamVersion()
QIODevice *dataDevice = cache.data(url);
QVERIFY(dataDevice != 0);
QByteArray cachedData = dataDevice->readAll();
+ delete dataDevice;
QCOMPARE(cachedData, data);
}
}
@@ -690,8 +694,6 @@ public:
QNetworkDiskCache cache;
cache.setCacheDirectory(cachePath);
- int read = 0;
-
int i = 0;
for (; i < 5000; ++i) {
if (other && other->isFinished())
@@ -733,7 +735,7 @@ public:
if (d) {
QByteArray x = d->readAll();
if (x != longString && x != longString2) {
- qDebug() << x.length() << QString(x);
+ qDebug() << x.size() << QString(x);
gotMetaData = cache.metaData(url);
qDebug() << (gotMetaData.url().toString())
<< gotMetaData.lastModified()
@@ -742,7 +744,6 @@ public:
}
if (gotMetaData.isValid())
QVERIFY(x == longString || x == longString2);
- read++;
delete d;
}
}
@@ -750,9 +751,8 @@ public:
cache.remove(url);
if (QRandomGenerator::global()->bounded(5) == 1)
cache.clear();
- sleep(0);
+ sleep(std::chrono::seconds{0});
}
- //qDebug() << "read!" << read << i;
}
QDateTime dt;
diff --git a/tests/auto/network/access/qnetworkreply/4G.br b/tests/auto/network/access/qnetworkreply/4G.br
new file mode 100644
index 0000000000..dabd553ed7
--- /dev/null
+++ b/tests/auto/network/access/qnetworkreply/4G.br
Binary files differ
diff --git a/tests/auto/network/access/qnetworkreply/BLACKLIST b/tests/auto/network/access/qnetworkreply/BLACKLIST
index b10a90e8ac..a4c7c1ee30 100644
--- a/tests/auto/network/access/qnetworkreply/BLACKLIST
+++ b/tests/auto/network/access/qnetworkreply/BLACKLIST
@@ -1,14 +1,11 @@
# See qtbase/src/testlib/qtestblacklist.cpp for format
[getErrors:ftp-host]
linux
-[getFromHttpIntoBuffer]
-osx
[ioPostToHttpFromSocket]
osx
[ioHttpRedirectMultipartPost]
b2qt
-ubuntu
-rhel
+linux
[ioHttpRedirectPolicy]
opensuse-leap
b2qt
@@ -18,12 +15,8 @@ windows-10
windows-10
[backgroundRequest]
macos
-[connectToIPv6Address]
-macos
[deleteFromHttp]
macos
-[downloadProgress]
-macos
[httpCanReadLine]
macos
[httpRecursiveCreation]
@@ -32,8 +25,6 @@ osx
macos
[ioGetFromBuiltinHttp]
osx
-[ioGetFromHttp]
-macos
[ioPostToHttpFromFile]
macos
[ioPostToHttpFromSocketSynchronous]
@@ -44,35 +35,18 @@ osx
macos
[lastModifiedHeaderForHttp]
macos
-[multipartSkipIndices]
-macos
-[nestedEventLoops]
-osx
-[postToHttp]
-macos
-[postToHttpMultipart]
-macos
[postToHttpSynchronous]
macos
-[putGetDeleteGetFromHttp]
-macos
[putToHttpSynchronous]
macos
[putToHttpsSynchronous]
osx
-[putWithRateLimiting]
-macos
-[qtbug13431replyThrottling]
-macos
-[receiveCookiesFromHttp]
-osx
[receiveCookiesFromHttpSynchronous]
osx
-[sendCookies]
-osx
[sendCookiesSynchronous]
osx
-[sendCustomRequestToHttp]
-macos
[backgroundRequestConnectInBackground]
osx
+#QTBUG-103055
+[ioGetFromHttpWithProxyAuth]
+qnx
diff --git a/tests/auto/network/access/qnetworkreply/CMakeLists.txt b/tests/auto/network/access/qnetworkreply/CMakeLists.txt
index 3b138b3918..9bfd90cd56 100644
--- a/tests/auto/network/access/qnetworkreply/CMakeLists.txt
+++ b/tests/auto/network/access/qnetworkreply/CMakeLists.txt
@@ -1,4 +1,11 @@
-# Generated from qnetworkreply.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qnetworkreply LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
add_subdirectory(echo)
add_subdirectory(test)
diff --git a/tests/auto/network/access/qnetworkreply/certs/qt-test-server-cacert.pem b/tests/auto/network/access/qnetworkreply/certs/qt-test-server-cacert.pem
index c5aea0d7c9..29fd755de7 100644
--- a/tests/auto/network/access/qnetworkreply/certs/qt-test-server-cacert.pem
+++ b/tests/auto/network/access/qnetworkreply/certs/qt-test-server-cacert.pem
@@ -1,17 +1,17 @@
-----BEGIN CERTIFICATE-----
-MIICpzCCAhACCQCzAF1hyRVzAjANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMC
-Tk8xDTALBgNVBAgTBE9zbG8xDTALBgNVBAcTBE9zbG8xDjAMBgNVBAoTBU5va2lh
-MTUwMwYDVQQLFCxRdCBTb2Z0d2FyZS9lbWFpbEFkZHJlc3M9bm9ib2R5QG5vZG9t
-YWluLm9yZzEjMCEGA1UEAxMacXQtdGVzdC1zZXJ2ZXIucXQtdGVzdC1uZXQwHhcN
-MTkwNjI0MTI0OTIxWhcNMjIwNjIzMTI0OTIxWjCBlzELMAkGA1UEBhMCTk8xDTAL
-BgNVBAgTBE9zbG8xDTALBgNVBAcTBE9zbG8xDjAMBgNVBAoTBU5va2lhMTUwMwYD
-VQQLFCxRdCBTb2Z0d2FyZS9lbWFpbEFkZHJlc3M9bm9ib2R5QG5vZG9tYWluLm9y
-ZzEjMCEGA1UEAxMacXQtdGVzdC1zZXJ2ZXIucXQtdGVzdC1uZXQwgZ8wDQYJKoZI
-hvcNAQEBBQADgY0AMIGJAoGBAM2q22/WNMmn8cC+5EEYGeICySLmp9W6Ay6eKHr0
-Xxp3X3epETuPfvAuxp7rOtkS18EMUegkUj8jw0IMEcbyHKFC/rTCaYOt93CxGBXM
-IChiMPAsFeYzGa/D6xzAkfcRaJRQ+Ek3CDLXPnXfo7xpABXezYcPXAJrgsgBfWrw
-HdxzAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEASCKbqEX5ysC549mq90ydk4jyDW3m
-PUyet01fKpcRqVs+OJxdExFBTra3gho6WzzpTSPsuX2ZKOLF5k6KkCvdCGvhC1Kv
-HHPIExurfzvdlSRzj6HbKyPuSfxyOloH0bBp7/Gg5RIuBPKlbmfbnTLtwEjhhbMU
-SoYI8HZd3HfY87c=
+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-----
diff --git a/tests/auto/network/access/qnetworkreply/certs/server.key b/tests/auto/network/access/qnetworkreply/certs/server.key
index 9d1664d609..b8d0d0449b 100644
--- a/tests/auto/network/access/qnetworkreply/certs/server.key
+++ b/tests/auto/network/access/qnetworkreply/certs/server.key
@@ -1,15 +1,27 @@
-----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=
+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-----
diff --git a/tests/auto/network/access/qnetworkreply/certs/server.pem b/tests/auto/network/access/qnetworkreply/certs/server.pem
index 67eb495319..47c22d9f29 100644
--- a/tests/auto/network/access/qnetworkreply/certs/server.pem
+++ b/tests/auto/network/access/qnetworkreply/certs/server.pem
@@ -1,24 +1,22 @@
-----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
+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-----
diff --git a/tests/auto/network/access/qnetworkreply/data/gzip.rcc.cpp b/tests/auto/network/access/qnetworkreply/data/gzip.rcc.cpp
new file mode 100644
index 0000000000..4716ebbc02
--- /dev/null
+++ b/tests/auto/network/access/qnetworkreply/data/gzip.rcc.cpp
@@ -0,0 +1,1235 @@
+/****************************************************************************
+** 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.gz
+ 0x0,0x0,0x47,0x6b,
+ 0x0,
+ 0x47,0x80,0x16,0x78,0xda,0xec,0xce,0xb1,0xd,0x82,0x50,0x14,0x40,0xd1,0x27,0x36,
+ 0x34,0xce,0x40,0x18,0x81,0x41,0xc,0x33,0x58,0xd8,0x62,0x42,0x49,0x45,0x4f,0x6f,
+ 0xfd,0x37,0x70,0x3,0x92,0xef,0x3c,0xdf,0xd2,0x5a,0x61,0xa,0x9a,0x73,0xbb,0xdb,
+ 0x9d,0x66,0xa9,0xeb,0xd8,0xaa,0xce,0xd3,0x7d,0x7c,0x44,0xc9,0xdd,0x69,0xbb,0x77,
+ 0xfa,0xf6,0x9f,0xdb,0x25,0xc5,0xb1,0xd,0x25,0xb7,0x3b,0x67,0x4e,0xbf,0xd7,0x75,
+ 0x3d,0x18,0x13,0xcf,0xc0,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,
+ 0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xc1,0xf9,0xb7,0x3b,0xc7,0x34,0x0,0xc0,0x20,
+ 0x0,0xc0,0x92,0x89,0xc0,0xf5,0xcc,0xcd,0xc7,0x82,0x5,0x82,0xa,0x38,0xfa,0xf5,
+ 0xac,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,
+ 0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0x8e,0xce,0x40,0xe7,0x6f,
+ 0xea,0xc4,0xcd,0xf3,0x1a,0x5,0xc6,0xd3,0x33,0xd2,
+
+};
+
+static const unsigned char qt_resource_name[] = {
+ // 4G.gz
+ 0x0,0x5,
+ 0x0,0x38,0xa4,0xea,
+ 0x0,0x34,
+ 0x0,0x47,0x0,0x2e,0x0,0x67,0x0,0x7a,
+
+};
+
+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.gz
+ 0x0,0x0,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x0,0x0,
+0x0,0x0,0x1,0x72,0x1c,0xb0,0xf0,0xe2,
+
+};
+
+#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_gzip)();
+int QT_RCC_MANGLE_NAMESPACE(qInitResources_gzip)()
+{
+ 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_gzip)();
+int QT_RCC_MANGLE_NAMESPACE(qCleanupResources_gzip)()
+{
+ 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_gzip)(); }
+ ~initializer() { QT_RCC_MANGLE_NAMESPACE(qCleanupResources_gzip)(); }
+ } dummy;
+}
diff --git a/tests/auto/network/access/qnetworkreply/data/zstandard.rcc.cpp b/tests/auto/network/access/qnetworkreply/data/zstandard.rcc.cpp
new file mode 100644
index 0000000000..c8a435192f
--- /dev/null
+++ b/tests/auto/network/access/qnetworkreply/data/zstandard.rcc.cpp
@@ -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;
+}
diff --git a/tests/auto/network/access/qnetworkreply/echo/CMakeLists.txt b/tests/auto/network/access/qnetworkreply/echo/CMakeLists.txt
index 664b2f2fab..137b29110d 100644
--- a/tests/auto/network/access/qnetworkreply/echo/CMakeLists.txt
+++ b/tests/auto/network/access/qnetworkreply/echo/CMakeLists.txt
@@ -1,11 +1,12 @@
-# Generated from echo.pro.
+# 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} # special case
+ OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
SOURCES
main.cpp
)
diff --git a/tests/auto/network/access/qnetworkreply/echo/main.cpp b/tests/auto/network/access/qnetworkreply/echo/main.cpp
index 9085de7898..b10eaa745c 100644
--- a/tests/auto/network/access/qnetworkreply/echo/main.cpp
+++ b/tests/auto/network/access/qnetworkreply/echo/main.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtCore/QFile>
@@ -37,11 +12,13 @@ int main(int argc, char **)
}
QFile file;
- file.open(stdin, QFile::ReadWrite);
+ if (!file.open(stdin, QFile::ReadWrite))
+ return 1;
QByteArray data = file.readAll();
file.close();
- file.open(stdout, QFile::WriteOnly);
+ if (!file.open(stdout, QFile::WriteOnly))
+ return 1;
file.write(data);
file.close();
return 0;
diff --git a/tests/auto/network/access/qnetworkreply/qnetworkreply.qrc b/tests/auto/network/access/qnetworkreply/qnetworkreply.qrc
deleted file mode 100644
index 85ca6312af..0000000000
--- a/tests/auto/network/access/qnetworkreply/qnetworkreply.qrc
+++ /dev/null
@@ -1,5 +0,0 @@
-<!DOCTYPE RCC><RCC version="1.0">
-<qresource>
- <file>resource</file>
-</qresource>
-</RCC>
diff --git a/tests/auto/network/access/qnetworkreply/test/CMakeLists.txt b/tests/auto/network/access/qnetworkreply/test/CMakeLists.txt
index d1b664e2d6..fa353b2769 100644
--- a/tests/auto/network/access/qnetworkreply/test/CMakeLists.txt
+++ b/tests/auto/network/access/qnetworkreply/test/CMakeLists.txt
@@ -1,4 +1,5 @@
-# Generated from test.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qnetworkreply Test:
@@ -18,22 +19,23 @@ 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}/../" # special case
+ OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../"
SOURCES
../tst_qnetworkreply.cpp
- PUBLIC_LIBRARIES
+ ../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" # special case
+ QT_TEST_SERVER_LIST "vsftpd" "apache2" "ftp-proxy" "danted" "squid"
+ BUNDLE_ANDROID_OPENSSL_LIBS
)
+add_dependencies(tst_qnetworkreply echo)
# Resources:
-set_source_files_properties("../resource"
- PROPERTIES QT_RESOURCE_ALIAS "resource"
-)
set(qnetworkreply_resource_files
- "resource"
+ "../resource"
)
qt_internal_add_resource(tst_qnetworkreply "qnetworkreply"
@@ -44,15 +46,3 @@ qt_internal_add_resource(tst_qnetworkreply "qnetworkreply"
FILES
${qnetworkreply_resource_files}
)
-
-
-#### Keys ignored in scope 1:.:.:test.pro:<TRUE>:
-# QT_FOR_CONFIG = "gui-private"
-# QT_TEST_SERVER_LIST = "vsftpd" "apache2" "ftp-proxy" "danted" "squid"
-# testcase.timeout = "600"
-
-## Scopes:
-#####################################################################
-
-#### Keys ignored in scope 2:.:.:test.pro:NOT ANDROID:
-# TEST_HELPER_INSTALLS = "../echo/echo"
diff --git a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
index 5e8a5420e1..a4a05b18f5 100644
--- a/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
+++ b/tests/auto/network/access/qnetworkreply/tst_qnetworkreply.cpp
@@ -1,54 +1,38 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#include <QtNetwork/qtnetworkglobal.h>
#include <QTest>
#include <QSemaphore>
#include <QTestEventLoop>
#include <QSignalSpy>
+#if QT_CONFIG(process)
#include <QProcess>
+#endif
#include <QTimer>
#include <QWaitCondition>
#include <QScopeGuard>
#include <QBuffer>
+#include <QMap>
#include <QtCore/QCryptographicHash>
#include <QtCore/QDataStream>
-#include <QtCore/QUrl>
+#include <QtCore/QDateTime>
#include <QtCore/QEventLoop>
#include <QtCore/QElapsedTimer>
#include <QtCore/QFile>
+#include <QtCore/QList>
#include <QtCore/QRandomGenerator>
#include <QtCore/QRegularExpression>
#include <QtCore/QRegularExpressionMatch>
+#include <QtCore/QSet>
#include <QtCore/QSharedPointer>
#include <QtCore/QScopedPointer>
#include <QtCore/QTemporaryFile>
+#include <QtCore/QTimeZone>
+#include <QtCore/QUrl>
+
#include <QtNetwork/QTcpServer>
#include <QtNetwork/QTcpSocket>
#include <QtNetwork/QLocalSocket>
@@ -63,14 +47,16 @@
#include <QtNetwork/qnetworkdiskcache.h>
#include <QtNetwork/qnetworkrequest.h>
#include <QtNetwork/qnetworkreply.h>
+#include <QtNetwork/QHttp1Configuration>
#include <QtNetwork/qnetworkcookie.h>
#include <QtNetwork/QNetworkCookieJar>
#include <QtNetwork/QHttpPart>
#include <QtNetwork/QHttpMultiPart>
#include <QtNetwork/QNetworkProxyQuery>
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
#include <QtNetwork/qsslerror.h>
#include <QtNetwork/qsslconfiguration.h>
+#include <QtNetwork/qsslsocket.h>
#ifdef QT_BUILD_INTERNAL
#include <QtNetwork/private/qsslconfiguration_p.h>
#endif
@@ -82,6 +68,9 @@
Q_DECLARE_METATYPE(QSharedPointer<char>)
#endif
+#include <memory>
+#include <optional>
+
#ifdef Q_OS_UNIX
# include <sys/types.h>
# include <unistd.h> // for getuid()
@@ -90,23 +79,23 @@ Q_DECLARE_METATYPE(QSharedPointer<char>)
#include "../../../network-settings.h"
-// Non-OpenSSL backends are not able to report a specific error code
-// for self-signed certificates.
-#ifndef QT_NO_OPENSSL
-#define FLUKE_CERTIFICATE_ERROR QSslError::SelfSignedCertificate
-#else
-#define FLUKE_CERTIFICATE_ERROR QSslError::CertificateUntrusted
+#ifdef Q_OS_INTEGRITY
+#include "qplatformdefs.h"
#endif
Q_DECLARE_METATYPE(QAuthenticator*)
-#ifndef QT_NO_NETWORKPROXY
+#if QT_CONFIG(networkproxy)
Q_DECLARE_METATYPE(QNetworkProxyQuery)
#endif
typedef QSharedPointer<QNetworkReply> QNetworkReplyPtr;
-#ifndef QT_NO_OPENSSL
+using namespace Qt::StringLiterals;
+using namespace std::chrono_literals;
+
+#if QT_CONFIG(ssl)
QT_BEGIN_NAMESPACE
+// Technically, a workaround, and only needed for OpenSSL:
void qt_ForceTlsSecurityLevel();
QT_END_NAMESPACE
#endif
@@ -116,7 +105,7 @@ class tst_QNetworkReply: public QObject
{
Q_OBJECT
-#ifndef QT_NO_NETWORKPROXY
+#if QT_CONFIG(networkproxy)
struct ProxyData
{
ProxyData(const QNetworkProxy &p, const QByteArray &t, bool auth)
@@ -125,7 +114,7 @@ class tst_QNetworkReply: public QObject
QNetworkProxy proxy;
bool requiresAuthentication;
};
-#endif // !QT_NO_NETWORKPROXY
+#endif // QT_CONFIG(networkproxy)
static bool seedCreated;
static QString createUniqueExtension()
@@ -145,6 +134,29 @@ class tst_QNetworkReply: public QObject
"\r\n";
return s;
}
+ static QString movedReplyStr() {
+ QString s = "HTTP/1.1 301 Moved Permanently\r\n"
+ "Content-Type: text/plain\r\n"
+ "location: %1\r\n"
+ "\r\n";
+ return s;
+ }
+
+ static QString foundReplyStr() {
+ QString s = "HTTP/1.1 302 Found\r\n"
+ "Content-Type: text/plain\r\n"
+ "location: %1\r\n"
+ "\r\n";
+ return s;
+ }
+
+ static QString permRedirectReplyStr() {
+ QString s = "HTTP/1.1 308 Permanent Redirect\r\n"
+ "Content-Type: text/plain\r\n"
+ "location: %1\r\n"
+ "\r\n";
+ return s;
+ }
static const QByteArray httpEmpty200Response;
static const QString filePermissionFileName;
@@ -158,16 +170,18 @@ class tst_QNetworkReply: public QObject
QString wronlyFileName;
#endif
QString uniqueExtension;
-#ifndef QT_NO_NETWORKPROXY
+#if QT_CONFIG(networkproxy)
QList<ProxyData> proxies;
#endif
QNetworkAccessManager manager;
MyCookieJar *cookieJar;
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
QSslConfiguration storedSslConfiguration;
QList<QSslError> storedExpectedSslErrors;
static const QString certsFilePath;
-#endif
+ bool isSecureTransport = false;
+ bool isSchannel = false;
+#endif // QT_CONFIG(ssl)
using QObject::connect;
static bool connect(const QNetworkReplyPtr &ptr, const char *signal, const QObject *receiver, const char *slot, Qt::ConnectionType ct = Qt::AutoConnection)
@@ -195,7 +209,7 @@ public Q_SLOTS:
void pipeliningHelperSlot();
void emitErrorForAllRepliesSlot();
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
void sslErrors(QNetworkReply*,const QList<QSslError> &);
void storeSslConfiguration();
void ignoreSslErrorListSlot(QNetworkReply *reply, const QList<QSslError> &);
@@ -226,12 +240,18 @@ private Q_SLOTS:
void getFromFtpAfterError(); // QTBUG-40797
void getFromHttp_data();
void getFromHttp();
+ void getWithBodyFromHttp_data();
+ void getWithBodyFromHttp();
+ void getWithAndWithoutBodyFromHttp_data();
+ void getWithAndWithoutBodyFromHttp();
+ void getWithBodyRedirected_data();
+ void getWithBodyRedirected();
void getErrors_data();
void getErrors();
-#ifndef QT_NO_NETWORKPROXY
+#if QT_CONFIG(networkproxy)
void headFromHttp_data();
void headFromHttp();
-#endif // !QT_NO_NETWORKPROXY
+#endif // QT_CONFIG(networkproxy)
void putToFile_data();
void putToFile();
void putToFtp_data();
@@ -243,6 +263,8 @@ private Q_SLOTS:
void putToHttpSynchronous();
void putToHttpMultipart_data();
void putToHttpMultipart();
+ void putWithoutBody();
+ void putWithoutBody_data();
void postToHttp_data();
void postToHttp();
void postToHttpSynchronous_data();
@@ -250,7 +272,9 @@ private Q_SLOTS:
void postToHttpMultipart_data();
void postToHttpMultipart();
void multipartSkipIndices(); // QTBUG-32534
-#ifndef QT_NO_SSL
+ void postWithoutBody_data();
+ void postWithoutBody();
+#if QT_CONFIG(ssl)
void putToHttps_data();
void putToHttps();
void putToHttpsSynchronous_data();
@@ -277,6 +301,7 @@ private Q_SLOTS:
void ioGetFromFileSpecial();
void ioGetFromFile_data();
void ioGetFromFile();
+ void ioGetFromFileUrl();
void ioGetFromFtp_data();
void ioGetFromFtp();
void ioGetFromFtpWithReuse();
@@ -289,29 +314,29 @@ private Q_SLOTS:
void ioGetFromHttpWithAuth_data();
void ioGetFromHttpWithAuth();
void ioGetFromHttpWithAuthSynchronous();
-#ifndef QT_NO_NETWORKPROXY
+#if QT_CONFIG(networkproxy)
void ioGetFromHttpWithProxyAuth();
void ioGetFromHttpWithProxyAuthSynchronous();
void ioGetFromHttpWithSocksProxy();
-#endif // !QT_NO_NETWORKPROXY
-#ifndef QT_NO_SSL
+#endif // QT_CONFIG(networkproxy)
+#if QT_CONFIG(ssl)
void ioGetFromHttpsWithSslErrors();
void ioGetFromHttpsWithIgnoreSslErrors();
void ioGetFromHttpsWithSslHandshakeError();
#endif
void ioGetFromHttpBrokenServer_data();
void ioGetFromHttpBrokenServer();
- void ioGetFromHttpStatus100_data();
- void ioGetFromHttpStatus100();
+ void ioGetFromHttpStatusInformational_data();
+ void ioGetFromHttpStatusInformational();
void ioGetFromHttpNoHeaders_data();
void ioGetFromHttpNoHeaders();
void ioGetFromHttpWithCache_data();
void ioGetFromHttpWithCache();
-#ifndef QT_NO_NETWORKPROXY
+#if QT_CONFIG(networkproxy)
void ioGetWithManyProxies_data();
void ioGetWithManyProxies();
-#endif // !QT_NO_NETWORKPROXY
+#endif // QT_CONFIG(networkproxy)
void ioPutToFileFromFile_data();
void ioPutToFileFromFile();
@@ -327,12 +352,12 @@ private Q_SLOTS:
void ioPutToHttpFromFile();
void ioPostToHttpFromFile_data();
void ioPostToHttpFromFile();
-#ifndef QT_NO_NETWORKPROXY
+#if QT_CONFIG(networkproxy)
void ioPostToHttpFromSocket_data();
void ioPostToHttpFromSocket();
void ioPostToHttpFromSocketSynchronous();
void ioPostToHttpFromSocketSynchronous_data();
-#endif // !QT_NO_NETWORKPROXY
+#endif // QT_CONFIG(networkproxy)
void ioPostToHttpFromMiddleOfFileToEnd();
void ioPostToHttpFromMiddleOfFileFiveBytes();
void ioPostToHttpFromMiddleOfQBufferFiveBytes();
@@ -372,13 +397,13 @@ private Q_SLOTS:
void nestedEventLoops();
-#ifndef QT_NO_NETWORKPROXY
+#if QT_CONFIG(networkproxy)
void httpProxyCommands_data();
void httpProxyCommands();
void httpProxyCommandsSynchronous_data();
void httpProxyCommandsSynchronous();
void proxyChange();
-#endif // !QT_NO_NETWORKPROXY
+#endif // QT_CONFIG(networkproxy)
void authorizationError_data();
void authorizationError();
@@ -392,7 +417,7 @@ private Q_SLOTS:
void httpRecursiveCreation();
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
void ioPostToHttpsUploadProgress();
void ignoreSslErrorsList_data();
void ignoreSslErrorsList();
@@ -449,20 +474,27 @@ private Q_SLOTS:
void synchronousRequest_data();
void synchronousRequest();
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
void synchronousRequestSslFailure();
#endif
void httpAbort();
+ void closeClientSideConnectionEagerlyQtbug20726();
+ void varyingCacheExpiry_data();
+ void varyingCacheExpiry();
+
+ void amountOfHttp1ConnectionsQtbug25280_data();
+ void amountOfHttp1ConnectionsQtbug25280();
+
void dontInsertPartialContentIntoTheCache();
void httpUserAgent();
-#ifndef QT_NO_NETWORKPROXY
+#if QT_CONFIG(networkproxy)
void authenticationCacheAfterCancel_data();
void authenticationCacheAfterCancel();
void authenticationWithDifferentRealm();
-#endif // !QT_NO_NETWORKPROXY
+#endif // QT_CONFIG(networkproxy)
void synchronousAuthenticationCache();
void pipelining();
@@ -489,6 +521,7 @@ private Q_SLOTS:
void ioHttpCookiesDuringRedirect();
void ioHttpRedirect_data();
void ioHttpRedirect();
+ void ioHttpRedirectWithCache();
void ioHttpRedirectFromLocalToRemote();
void ioHttpRedirectPostPut_data();
void ioHttpRedirectPostPut();
@@ -498,7 +531,7 @@ private Q_SLOTS:
void ioHttpRedirectCustom();
void ioHttpRedirectWithUploadDevice_data();
void ioHttpRedirectWithUploadDevice();
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
void putWithServerClosingConnectionImmediately();
#endif
@@ -507,11 +540,30 @@ private Q_SLOTS:
void autoDeleteReplies_data();
void autoDeleteReplies();
- void getWithTimeout();
- void postWithTimeout();
+ void requestWithTimeout_data();
+ void requestWithTimeout();
+
+ void moreActivitySignals_data();
+ void moreActivitySignals();
void contentEncoding_data();
void contentEncoding();
+ void contentEncodingBigPayload_data();
+ void contentEncodingBigPayload();
+ void cacheWithContentEncoding_data();
+ void cacheWithContentEncoding();
+ void downloadProgressWithContentEncoding_data();
+ void downloadProgressWithContentEncoding();
+ void contentEncodingError_data();
+ void contentEncodingError();
+ void compressedReadyRead();
+ void notFoundWithCompression_data();
+ void notFoundWithCompression();
+
+ void qtbug68821proxyError_data();
+ void qtbug68821proxyError();
+
+ void abortAndError();
// NOTE: This test must be last!
void parentingRepliesToTheApp();
@@ -522,6 +574,9 @@ private:
bool notEnoughDataForFastSender;
bool ftpSupported = false;
+#if QT_CONFIG(ssl)
+ QSslError::SslError flukeCertTlsError = QSslError::CertificateUntrusted;
+#endif
};
const QByteArray tst_QNetworkReply::httpEmpty200Response =
@@ -548,7 +603,7 @@ static bool validateRedirectedResponseHeaders(QNetworkReplyPtr reply)
&& !reply->header(QNetworkRequest::LocationHeader).isValid();
}
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
static void setupSslServer(QSslSocket* serverSocket)
{
QString testDataDir = QFileInfo(QFINDTESTDATA("rfc3252.txt")).absolutePath();
@@ -574,7 +629,7 @@ const QString tst_QNetworkReply::certsFilePath = "/certs/qt-test-server-host-net
const QString tst_QNetworkReply::certsFilePath = "/certs/qt-test-server-cacert.pem";
#endif
-#endif // !QT_NO_SSL
+#endif // QT_CONFIG(ssl)
// NOTE: MiniHttpServer has a very limited support of PUT/POST requests! Make
// sure you understand the server's code before PUTting/POSTing data (and
@@ -594,7 +649,8 @@ public:
int totalConnections;
bool stopTransfer = false;
- bool hasContent = false;
+ bool checkedContentLength = false;
+ bool foundContentLength = false;
int contentRead = 0;
int contentLength = 0;
@@ -626,13 +682,14 @@ public:
{
contentLength = 0;
receivedData.clear();
+ foundContentLength = false;
}
protected:
void incomingConnection(qintptr socketDescriptor) override
{
//qDebug() << "incomingConnection" << socketDescriptor << "doSsl:" << doSsl << "ipv6:" << ipv6;
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
if (doSsl) {
QSslSocket *serverSocket = new QSslSocket(this);
if (!serverSocket->setSocketDescriptor(socketDescriptor)) {
@@ -682,8 +739,13 @@ private:
void parseContentLength()
{
- int index = receivedData.indexOf("Content-Length:");
- index += sizeof("Content-Length:") - 1;
+ int index = receivedData.indexOf("content-length:");
+ if (index == -1)
+ return;
+
+ foundContentLength = true;
+
+ index += sizeof("content-length:") - 1;
const auto end = std::find(receivedData.cbegin() + index, receivedData.cend(), '\r');
auto num = receivedData.mid(index, std::distance(receivedData.cbegin() + index, end));
bool ok;
@@ -693,7 +755,7 @@ private:
}
private slots:
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
void slotSslErrors(const QList<QSslError>& errors)
{
QTcpSocket *currentClient = qobject_cast<QTcpSocket *>(sender());
@@ -723,12 +785,14 @@ public slots:
if (doubleEndlPos != -1) {
const int endOfHeader = doubleEndlPos + 4;
- hasContent = receivedData.startsWith("POST") || receivedData.startsWith("PUT")
- || receivedData.startsWith("CUSTOM_WITH_PAYLOAD");
- if (hasContent && contentLength == 0)
+ contentRead = receivedData.size() - endOfHeader;
+
+ if (!checkedContentLength) {
parseContentLength();
- contentRead = receivedData.length() - endOfHeader;
- if (hasContent && contentRead < contentLength)
+ checkedContentLength = true;
+ }
+
+ if (contentRead < contentLength)
return;
// multiple requests incoming. remove the bytes of the current one
@@ -764,7 +828,7 @@ public:
{ QNetworkCookieJar::setAllCookies(cookieList); }
};
-#ifndef QT_NO_NETWORKPROXY
+#if QT_CONFIG(networkproxy)
class MyProxyFactory: public QNetworkProxyFactory
{
public:
@@ -787,7 +851,7 @@ public:
return toReturn;
}
};
-#endif // !QT_NO_NETWORKPROXY
+#endif // QT_CONFIG(networkproxy)
class MyMemoryCache: public QAbstractNetworkCache
{
@@ -829,7 +893,7 @@ public:
qint64 cacheSize() const override
{
qint64 total = 0;
- foreach (const CachedContent &entry, cache)
+ for (const auto &[_, entry] : cache.asKeyValueRange())
total += entry.second.size();
return total;
}
@@ -871,9 +935,15 @@ public:
{
}
- QIODevice *data(const QUrl &) override
+ QIODevice *data(const QUrl &url) override
{
- return 0;
+ QIODevice *device = nullptr;
+ auto it = m_buffers.constFind(url);
+ if (it != m_buffers.cend()) {
+ device = *it;
+ device->seek(0);
+ }
+ return device;
}
bool remove(const QUrl &url) override
@@ -900,7 +970,6 @@ public:
{
QUrl url = buffer->property("url").toUrl();
m_insertedUrls << url;
- delete m_buffers.take(url);
}
void clear() override { m_insertedUrls.clear(); }
@@ -1000,7 +1069,7 @@ public:
}
virtual void incomingConnection(qintptr socketDescriptor) override
{
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
if (doSsl) {
QSslSocket *serverSocket = new QSslSocket;
serverSocket->setParent(this);
@@ -1016,7 +1085,7 @@ public:
}
private slots:
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
void slotSslErrors(const QList<QSslError>& errors)
{
qDebug() << "slotSslErrors" << sslSocket->errorString() << errors;
@@ -1286,12 +1355,17 @@ tst_QNetworkReply::tst_QNetworkReply()
{
qRegisterMetaType<QNetworkReply *>(); // for QSignalSpy
qRegisterMetaType<QAuthenticator *>();
-#ifndef QT_NO_NETWORKPROXY
+#if QT_CONFIG(networkproxy)
qRegisterMetaType<QNetworkProxy>();
#endif
-#ifndef QT_NO_SSL
+
+#if QT_CONFIG(ssl)
qRegisterMetaType<QList<QSslError> >();
+ isSecureTransport = QSslSocket::activeBackend() == QStringLiteral("securetransport");
+ if (!isSecureTransport)
+ isSchannel = QSslSocket::activeBackend() == QStringLiteral("schannel");
#endif
+
qRegisterMetaType<QNetworkReply::NetworkError>();
uniqueExtension = createUniqueExtension();
@@ -1299,7 +1373,7 @@ tst_QNetworkReply::tst_QNetworkReply()
cookieJar = new MyCookieJar;
manager.setCookieJar(cookieJar);
-#ifndef QT_NO_NETWORKPROXY
+#if QT_CONFIG(networkproxy)
QHostInfo hostInfo = QHostInfo::fromName(QtNetworkSettings::httpProxyServerName());
proxies << ProxyData(QNetworkProxy::NoProxy, "", false);
@@ -1318,13 +1392,13 @@ tst_QNetworkReply::tst_QNetworkReply()
<< ProxyData(QNetworkProxy(QNetworkProxy::Socks5Proxy, socksProxy, 1081),
"+socksauth", true);
} else {
-#endif // !QT_NO_NETWORKPROXY
+#endif // QT_CONFIG(networkproxy)
fprintf(stderr, "==================================================================\n");
fprintf(stderr, "Proxy could not be looked up. No proxy will be used while testing!\n");
fprintf(stderr, "==================================================================\n");
-#ifndef QT_NO_NETWORKPROXY
+#if QT_CONFIG(networkproxy)
}
-#endif // !QT_NO_NETWORKPROXY
+#endif // QT_CONFIG(networkproxy)
ftpSupported = manager.supportedSchemes().contains("ftp");
}
@@ -1346,7 +1420,7 @@ void tst_QNetworkReply::proxyAuthenticationRequired(const QNetworkProxy &, QAuth
auth->setPassword("password");
}
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
void tst_QNetworkReply::sslErrors(QNetworkReply *reply, const QList<QSslError> &errors)
{
reply->ignoreSslErrors();
@@ -1444,11 +1518,11 @@ QString tst_QNetworkReply::runSimpleRequest(QNetworkAccessManager::Operation op,
while (!reply->isFinished()) {
QTimer::singleShot(20000, loop, SLOT(quit()));
code = loop->exec();
- if (count == spy.count() && !reply->isFinished()) {
+ if (count == spy.size() && !reply->isFinished()) {
code = Timeout;
break;
}
- count = spy.count();
+ count = spy.size();
}
delete loop;
loop = 0;
@@ -1514,11 +1588,11 @@ int tst_QNetworkReply::waitForFinish(QNetworkReplyPtr &reply)
QSignalSpy spy(reply.data(), SIGNAL(downloadProgress(qint64,qint64)));
while (!reply->isFinished()) {
QTimer::singleShot(5000, loop, SLOT(quit()));
- if (loop->exec() == Timeout && count == spy.count() && !reply->isFinished()) {
+ if (loop->exec() == Timeout && count == spy.size() && !reply->isFinished()) {
returnCode = Timeout;
break;
}
- count = spy.count();
+ count = spy.size();
}
delete loop;
loop = 0;
@@ -1546,8 +1620,10 @@ void tst_QNetworkReply::initTestCase()
testDataDir = QCoreApplication::applicationDirPath();
#if defined(QT_TEST_SERVER)
- QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::ftpServerName(), 21));
- QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::ftpProxyServerName(), 2121));
+ if (ftpSupported) {
+ QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::ftpServerName(), 21));
+ QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::ftpProxyServerName(), 2121));
+ }
QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpServerName(), 80));
QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpServerName(), 443));
QVERIFY(QtNetworkSettings::verifyConnection(QtNetworkSettings::httpProxyServerName(), 3128));
@@ -1568,7 +1644,7 @@ void tst_QNetworkReply::initTestCase()
#endif
QDir::setSearchPaths("testdata", QStringList() << testDataDir);
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
QSslConfiguration::defaultConfiguration().caCertificates(); //preload certificates
#endif
@@ -1577,10 +1653,18 @@ void tst_QNetworkReply::initTestCase()
QString::fromLatin1("Couldn't find echo dir starting from %1.").arg(QDir::currentPath())));
cleanupTestData();
-#ifndef QT_NO_OPENSSL
+#if QT_CONFIG(ssl)
QT_PREPEND_NAMESPACE(qt_ForceTlsSecurityLevel)();
-#endif // QT_NO_OPENSSL
+ if (QSslSocket::activeBackend() == QStringLiteral("openssl"))
+ flukeCertTlsError = QSslError::SelfSignedCertificate;
+#endif
+
+ // For content encoding tests
+ Q_INIT_RESOURCE(gzip);
+#if defined(QT_BUILD_INTERNAL) && QT_CONFIG(zstd)
+ Q_INIT_RESOURCE(zstandard);
+#endif
}
void tst_QNetworkReply::cleanupTestCase()
@@ -1589,6 +1673,12 @@ void tst_QNetworkReply::cleanupTestCase()
if (!wronlyFileName.isNull())
QFile::remove(wronlyFileName);
#endif
+
+ // For content encoding tests
+#if defined(QT_BUILD_INTERNAL) && QT_CONFIG(zstd)
+ Q_CLEANUP_RESOURCE(zstandard);
+#endif
+ Q_CLEANUP_RESOURCE(gzip);
}
void tst_QNetworkReply::cleanupTestData()
@@ -1598,7 +1688,7 @@ void tst_QNetworkReply::cleanupTestData()
// clear the internal cache
manager.clearAccessCache();
-#ifndef QT_NO_NETWORKPROXY
+#if QT_CONFIG(networkproxy)
manager.setProxy(QNetworkProxy());
#endif
manager.setCache(0);
@@ -1607,7 +1697,7 @@ void tst_QNetworkReply::cleanupTestData()
cookieJar->setAllCookies(QList<QNetworkCookie>());
// disconnect manager signals
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
manager.disconnect(SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)));
#endif
manager.disconnect(SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)));
@@ -1946,7 +2036,254 @@ void tst_QNetworkReply::getFromHttp()
QCOMPARE(reply->readAll(), reference.readAll());
}
-#ifndef QT_NO_NETWORKPROXY
+void tst_QNetworkReply::getWithBodyFromHttp_data()
+{
+ QTest::addColumn<QByteArray>("dataFromClientToServer");
+ QTest::addColumn<bool>("useDevice");
+ QTest::newRow("with-bytearray") << QByteArray("Body 1") << false;
+ QTest::newRow("with-bytearray2") << QByteArray("Body 2") << false;
+ QTest::newRow("with-bytearray3") << QByteArray("Body 3") << false;
+ QTest::newRow("with-device") << QByteArray("Body 1") << true;
+ QTest::newRow("with-device2") << QByteArray("Body 2") << true;
+ QTest::newRow("with-device3") << QByteArray("Body 3") << true;
+}
+
+void tst_QNetworkReply::getWithBodyFromHttp()
+{
+ QFETCH(QByteArray, dataFromClientToServer);
+ QFETCH(bool, useDevice);
+
+ QBuffer buff;
+ buff.setData(dataFromClientToServer);
+ buff.open(QIODevice::ReadOnly);
+
+ QByteArray dataFromServerToClient = QByteArray("Long first line\r\nLong second line");
+ QByteArray httpResponse = QByteArray("HTTP/1.0 200 OK\r\nContent-Length: ");
+ httpResponse += QByteArray::number(dataFromServerToClient.size());
+ httpResponse += "\r\n\r\n";
+ httpResponse += dataFromServerToClient;
+
+ MiniHttpServer server(httpResponse);
+ server.doClose = true;
+
+ QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
+ QNetworkReplyPtr reply;
+
+ if (useDevice)
+ reply.reset(manager.get(request, &buff));
+ else
+ reply.reset(manager.get(request, dataFromClientToServer));
+
+ QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
+ QCOMPARE(server.contentLength, dataFromClientToServer.size());
+ QCOMPARE(server.receivedData.right(dataFromClientToServer.size()), dataFromClientToServer);
+ QByteArray content = reply->readAll();
+ QCOMPARE(content, dataFromServerToClient);
+}
+
+void tst_QNetworkReply::getWithAndWithoutBodyFromHttp_data()
+{
+ QTest::addColumn<QByteArray>("dataFromClientToServer");
+ QTest::addColumn<bool>("alwaysCache");
+ QTest::addColumn<tst_QNetworkReply::RunSimpleRequestReturn>("requestReturn");
+ QTest::addColumn<bool>("useDevice");
+ QTest::newRow("with-bytearray") << QByteArray("Body 1") << false << Success << false;
+ QTest::newRow("with-bytearray2") << QByteArray("Body 2") << false << Success << false;
+ QTest::newRow("with-bytearray3") << QByteArray("Body 3") << false << Success << false;
+ QTest::newRow("with-bytearray-cache") << QByteArray("Body 1") << true << Failure << false;
+ QTest::newRow("with-bytearray-cache2") << QByteArray("Body 2") << true << Failure << false;
+ QTest::newRow("with-bytearray-cache3") << QByteArray("Body 3") << true << Failure << false;
+ QTest::newRow("with-device") << QByteArray("Body 1") << false << Success << true;
+ QTest::newRow("with-device2") << QByteArray("Body 2") << false << Success << true;
+ QTest::newRow("with-device3") << QByteArray("Body 3") << false << Success << true;
+ QTest::newRow("with-device-cache") << QByteArray("Body 1") << true << Failure << true;
+ QTest::newRow("with-device-cache2") << QByteArray("Body 2") << true << Failure << true;
+ QTest::newRow("with-device-cache3") << QByteArray("Body 3") << true << Failure << true;
+}
+
+void tst_QNetworkReply::getWithAndWithoutBodyFromHttp()
+{
+ QFETCH(QByteArray, dataFromClientToServer);
+ QFETCH(bool, alwaysCache);
+ QFETCH(tst_QNetworkReply::RunSimpleRequestReturn, requestReturn);
+ QFETCH(bool, useDevice);
+
+ QBuffer buff;
+ buff.setData(dataFromClientToServer);
+ buff.open(QIODevice::ReadOnly);
+
+ QNetworkAccessManager qnam;
+ MyMemoryCache *memoryCache = new MyMemoryCache(&qnam);
+ qnam.setCache(memoryCache);
+
+ const int sizeOfDataFromServerToClient =3;
+ QByteArray dataFromServerToClient1 = QByteArray("aaa");
+ QByteArray dataFromServerToClient2 = QByteArray("bbb");
+ QByteArray dataFromServerToClient3 = QByteArray("ccc");
+
+ QByteArray baseHttpResponse = QByteArray("HTTP/1.0 200 OK\r\nContent-Length: ");
+ baseHttpResponse += QByteArray::number(sizeOfDataFromServerToClient);
+ baseHttpResponse += "\r\n\r\n";
+
+ MiniHttpServer server(baseHttpResponse + dataFromServerToClient1);
+ server.doClose = true;
+
+ QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
+
+ // Send request without body
+ QNetworkReplyPtr reply(manager.get(request));
+ QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
+ QByteArray content = reply->readAll();
+ QCOMPARE(content, dataFromServerToClient1);
+
+ if (alwaysCache) {
+ request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
+ QNetworkRequest::AlwaysCache);
+ }
+
+ server.dataToTransmit = baseHttpResponse + dataFromServerToClient2;
+
+ // Send request with body
+ QNetworkReplyPtr reply2;
+ if (useDevice)
+ reply2.reset(manager.get(request, &buff));
+ else
+ reply2.reset(manager.get(request, dataFromClientToServer));
+
+ QVERIFY2(waitForFinish(reply2) == requestReturn, msgWaitForFinished(reply2));
+ content = reply2->readAll();
+
+ if (alwaysCache)
+ QVERIFY(content.isEmpty());
+ else
+ QCOMPARE(content, dataFromServerToClient2);
+
+ QCOMPARE(reply2->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(), false);
+
+ if (alwaysCache) {
+ request.setAttribute(QNetworkRequest::CacheLoadControlAttribute,
+ QNetworkRequest::PreferNetwork);
+ }
+
+ server.dataToTransmit = baseHttpResponse + dataFromServerToClient3;
+
+ // Send another request without a body
+ QNetworkReplyPtr reply3(manager.get(request));
+ QVERIFY2(waitForFinish(reply3) == Success, msgWaitForFinished(reply3));
+ content = reply3->readAll();
+ QCOMPARE(content, dataFromServerToClient3);
+ QCOMPARE(reply3->attribute(QNetworkRequest::SourceIsFromCacheAttribute).toBool(), false);
+}
+
+void tst_QNetworkReply::getWithBodyRedirected_data()
+{
+ QTest::addColumn<QByteArray>("dataFromClientToServer");
+ QTest::addColumn<bool>("useDevice");
+ QTest::addColumn<int>("status");
+ QTest::newRow("with-bytearray - 301") << QByteArray("Body 1") << false << 301;
+ QTest::newRow("with-bytearray2 - 301") << QByteArray("Body 2") << false << 301;
+ QTest::newRow("with-bytearray3 - 301") << QByteArray("Body 3") << false << 301;
+ QTest::newRow("with-device - 301") << QByteArray("Body 1") << true << 301;
+ QTest::newRow("with-device2 - 301") << QByteArray("Body 2") << true << 301;
+ QTest::newRow("with-device3 - 301") << QByteArray("Body 3") << true << 301;
+ QTest::newRow("with-bytearray - 302") << QByteArray("Body 1") << false << 302;
+ QTest::newRow("with-bytearray2 - 302") << QByteArray("Body 2") << false << 302;
+ QTest::newRow("with-bytearray3 - 302") << QByteArray("Body 3") << false << 302;
+ QTest::newRow("with-device - 302") << QByteArray("Body 1") << true << 302;
+ QTest::newRow("with-device2 - 302") << QByteArray("Body 2") << true << 302;
+ QTest::newRow("with-device3 - 302") << QByteArray("Body 3") << true << 302;
+ QTest::newRow("with-bytearray - 307") << QByteArray("Body 1") << false << 307;
+ QTest::newRow("with-bytearray2 - 307") << QByteArray("Body 2") << false << 307;
+ QTest::newRow("with-bytearray3 - 307") << QByteArray("Body 3") << false << 307;
+ QTest::newRow("with-device - 307") << QByteArray("Body 1") << true << 307;
+ QTest::newRow("with-device2 - 307") << QByteArray("Body 2") << true << 307;
+ QTest::newRow("with-device3 - 307") << QByteArray("Body 3") << true << 307;
+ QTest::newRow("with-bytearray - 308") << QByteArray("Body 1") << false << 308;
+ QTest::newRow("with-bytearray2 - 308") << QByteArray("Body 2") << false << 308;
+ QTest::newRow("with-bytearray3 - 308") << QByteArray("Body 3") << false << 308;
+ QTest::newRow("with-device - 308") << QByteArray("Body 1") << true << 308;
+ QTest::newRow("with-device2 - 308") << QByteArray("Body 2") << true << 308;
+ QTest::newRow("with-device3 - 308") << QByteArray("Body 3") << true << 308;
+}
+
+void tst_QNetworkReply::getWithBodyRedirected()
+{
+ QFETCH(QByteArray, dataFromClientToServer);
+ QFETCH(bool, useDevice);
+ QFETCH(int, status);
+
+ QBuffer buff;
+ buff.setData(dataFromClientToServer);
+ buff.open(QIODevice::ReadOnly);
+
+ QUrl localhost = QUrl("http://localhost");
+
+ // Setup server to which the second server will redirect to
+ MiniHttpServer server2(httpEmpty200Response);
+
+ QUrl redirectUrl = QUrl(localhost);
+ redirectUrl.setPort(server2.serverPort());
+
+ QByteArray redirectReply;
+ switch (status) {
+ case 301: redirectReply =
+ foundReplyStr().arg(QString(redirectUrl.toEncoded())).toLatin1(); break;
+ case 302: redirectReply =
+ movedReplyStr().arg(QString(redirectUrl.toEncoded())).toLatin1(); break;
+ case 307: redirectReply =
+ tempRedirectReplyStr().arg(QString(redirectUrl.toEncoded())).toLatin1(); break;
+ case 308: redirectReply =
+ permRedirectReplyStr().arg(QString(redirectUrl.toEncoded())).toLatin1(); break;
+ default: QFAIL("Unexpected status code"); break;
+ }
+
+ // Setup redirect server
+ MiniHttpServer server(redirectReply);
+
+ localhost.setPort(server.serverPort());
+ QNetworkRequest request(localhost);
+ request.setAttribute(QNetworkRequest::RedirectPolicyAttribute,
+ QNetworkRequest::NoLessSafeRedirectPolicy);
+
+ QNetworkReplyPtr reply;
+ if (useDevice)
+ reply.reset(manager.get(request, &buff));
+ else
+ reply.reset(manager.get(request, dataFromClientToServer));
+
+ QSignalSpy redSpy(reply.data(), SIGNAL(redirected(QUrl)));
+ QSignalSpy finSpy(reply.data(), SIGNAL(finished()));
+
+ QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
+
+ // Redirected and finished should be emitted exactly once
+ QCOMPARE(redSpy.size(), 1);
+ QCOMPARE(finSpy.size(), 1);
+
+ // Original URL should not be changed after redirect
+ QCOMPARE(request.url(), localhost);
+
+ // Verify Redirect url
+ QList<QVariant> args = redSpy.takeFirst();
+ QCOMPARE(args.at(0).toUrl(), redirectUrl);
+
+ // Reply url is set to the redirect url
+ QCOMPARE(reply->url(), redirectUrl);
+ QCOMPARE(reply->error(), QNetworkReply::NoError);
+ QVERIFY(validateRedirectedResponseHeaders(reply));
+
+ // Verify that the message body has arrived to the server
+ if (status > 302) {
+ QVERIFY(server2.contentLength != 0);
+ QCOMPARE(server2.contentLength, dataFromClientToServer.size());
+ QCOMPARE(server2.receivedData.right(dataFromClientToServer.size()), dataFromClientToServer);
+ } else {
+ // In these cases the message body should not reach the server
+ QVERIFY(server2.contentLength == 0);
+ }
+}
+
+#if QT_CONFIG(networkproxy)
void tst_QNetworkReply::headFromHttp_data()
{
QTest::addColumn<qint64>("referenceSize");
@@ -1965,7 +2302,7 @@ void tst_QNetworkReply::headFromHttp_data()
QString httpServer = QtNetworkSettings::httpServerName();
//testing proxies, mainly for the 407 response from http proxy
- for (int i = 0; i < proxies.count(); ++i) {
+ for (int i = 0; i < proxies.size(); ++i) {
QTest::newRow("rfc" + proxies.at(i).tag)
<< rfcsize
<< QUrl("http://" + httpServer + "/qtest/rfc3252.txt")
@@ -2035,7 +2372,7 @@ void tst_QNetworkReply::headFromHttp()
if (reply->header(QNetworkRequest::ContentTypeHeader).isValid())
QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader).toString(), contentType);
}
-#endif // !QT_NO_NETWORKPROXY
+#endif // QT_CONFIG(networkproxy)
void tst_QNetworkReply::getErrors_data()
{
@@ -2247,9 +2584,9 @@ void tst_QNetworkReply::putToFtp()
QSignalSpy spy(r, SIGNAL(downloadProgress(qint64,qint64)));
while (!r->isFinished()) {
QTestEventLoop::instance().enterLoop(10);
- if (count == spy.count() && !r->isFinished())
+ if (count == spy.size() && !r->isFinished())
break;
- count = spy.count();
+ count = spy.size();
}
QObject::disconnect(r, SIGNAL(finished()), &QTestEventLoop::instance(), SLOT(exitLoop()));
@@ -2364,6 +2701,48 @@ void tst_QNetworkReply::putToHttpSynchronous()
QCOMPARE(uploadedData, data);
}
+void tst_QNetworkReply::putWithoutBody_data()
+{
+ QTest::addColumn<bool>("client_data");
+
+ QTest::newRow("client_has_data") << true;
+ QTest::newRow("client_does_not_have_data") << false;
+}
+
+void tst_QNetworkReply::putWithoutBody()
+{
+ QFETCH(bool, client_data);
+
+ QBuffer buff;
+
+ if (client_data) {
+ buff.setData("Dummy data from client to server");
+ buff.open(QIODevice::ReadOnly);
+ }
+
+ QByteArray dataFromServerToClient = QByteArray("Some ridiculous dummy data");
+ QByteArray httpResponse = QByteArray("HTTP/1.0 200 OK\r\nContent-Length: ");
+ httpResponse += QByteArray::number(dataFromServerToClient.size());
+ httpResponse += "\r\n\r\n";
+ httpResponse += dataFromServerToClient;
+
+ MiniHttpServer server(httpResponse);
+ server.doClose = true;
+
+ QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
+ request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain"));
+
+ QNetworkReplyPtr reply;
+ if (client_data)
+ reply.reset(manager.put(request, &buff));
+ else
+ reply.reset(manager.put(request, nullptr));
+
+ QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
+ QCOMPARE(server.foundContentLength, client_data);
+}
+
+
void tst_QNetworkReply::postToHttp_data()
{
putToFile_data();
@@ -2681,8 +3060,8 @@ void tst_QNetworkReply::postToHttpMultipart()
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
- QVERIFY(multiPart->boundary().count() > 20); // check that there is randomness after the "boundary_.oOo._" string
- QVERIFY(multiPart->boundary().count() < 70);
+ QVERIFY(multiPart->boundary().size() > 20); // check that there is randomness after the "boundary_.oOo._" string
+ QVERIFY(multiPart->boundary().size() < 70);
QByteArray replyData = reply->readAll();
expectedReplyData.prepend("content type: multipart/" + contentType + "; boundary=\"" + multiPart->boundary() + "\"\n");
@@ -2733,6 +3112,47 @@ void tst_QNetworkReply::multipartSkipIndices() // QTBUG-32534
multiPart->deleteLater();
}
+void tst_QNetworkReply::postWithoutBody_data()
+{
+ QTest::addColumn<bool>("client_data");
+
+ QTest::newRow("client_has_data") << true;
+ QTest::newRow("client_does_not_have_data") << false;
+}
+
+void tst_QNetworkReply::postWithoutBody()
+{
+ QFETCH(bool, client_data);
+
+ QBuffer buff;
+
+ if (client_data) {
+ buff.setData("Dummy data from client to server");
+ buff.open(QIODevice::ReadOnly);
+ }
+
+ QByteArray dataFromServerToClient = QByteArray("Some ridiculous dummy data");
+ QByteArray httpResponse = QByteArray("HTTP/1.0 200 OK\r\nContent-Length: ");
+ httpResponse += QByteArray::number(dataFromServerToClient.size());
+ httpResponse += "\r\n\r\n";
+ httpResponse += dataFromServerToClient;
+
+ MiniHttpServer server(httpResponse);
+ server.doClose = true;
+
+ QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
+ request.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain"));
+
+ QNetworkReplyPtr reply;
+ if (client_data)
+ reply.reset(manager.post(request, &buff));
+ else
+ reply.reset(manager.post(request, nullptr));
+
+ QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
+ QCOMPARE(server.foundContentLength, client_data);
+}
+
void tst_QNetworkReply::putToHttpMultipart_data()
{
postToHttpMultipart_data();
@@ -2769,8 +3189,8 @@ void tst_QNetworkReply::putToHttpMultipart()
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
- QVERIFY(multiPart->boundary().count() > 20); // check that there is randomness after the "boundary_.oOo._" string
- QVERIFY(multiPart->boundary().count() < 70);
+ QVERIFY(multiPart->boundary().size() > 20); // check that there is randomness after the "boundary_.oOo._" string
+ QVERIFY(multiPart->boundary().size() < 70);
QByteArray replyData = reply->readAll();
expectedReplyData.prepend("content type: multipart/" + contentType + "; boundary=\"" + multiPart->boundary() + "\"\n");
@@ -2778,12 +3198,12 @@ void tst_QNetworkReply::putToHttpMultipart()
QCOMPARE(replyData, expectedReplyData);
}
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
void tst_QNetworkReply::putToHttps_data()
{
-#if QT_CONFIG(securetransport)
- QSKIP("SecTrustEvaluate() returns recoverable error, update the certificate on server");
-#endif
+ if (isSecureTransport)
+ QSKIP("SecTrustEvaluate() returns recoverable error, update the certificate on server");
+
uniqueExtension = createUniqueExtension();
putToFile_data();
}
@@ -2825,9 +3245,9 @@ void tst_QNetworkReply::putToHttps()
void tst_QNetworkReply::putToHttpsSynchronous_data()
{
-#if QT_CONFIG(securetransport)
- QSKIP("SecTrustEvalueate() retruns recoverable error, update the server's certificate");
-#endif
+ if (isSecureTransport)
+ QSKIP("SecTrustEvalueate() retruns recoverable error, update the server's certificate");
+
uniqueExtension = createUniqueExtension();
putToFile_data();
}
@@ -2873,9 +3293,9 @@ void tst_QNetworkReply::putToHttpsSynchronous()
void tst_QNetworkReply::postToHttps_data()
{
-#if QT_CONFIG(securetransport)
- QSKIP("SecTrustEvaluate() returns recoverable error, update the certificate on server");
-#endif
+ if (isSecureTransport)
+ QSKIP("SecTrustEvaluate() returns recoverable error, update the certificate on server");
+
putToFile_data();
}
@@ -2907,9 +3327,9 @@ void tst_QNetworkReply::postToHttps()
void tst_QNetworkReply::postToHttpsSynchronous_data()
{
-#if QT_CONFIG(securetransport)
- QSKIP("SecTrustEvaluate() returns recoverable error, update the certificate on server");
-#endif
+ if (isSecureTransport)
+ QSKIP("SecTrustEvaluate() returns recoverable error, update the certificate on server");
+
putToFile_data();
}
@@ -2946,9 +3366,9 @@ void tst_QNetworkReply::postToHttpsSynchronous()
void tst_QNetworkReply::postToHttpsMultipart_data()
{
-#if QT_CONFIG(securetransport)
- QSKIP("SecTrustEvaluate() returns recoverable error, update the certificate on server");
-#endif
+ if (isSecureTransport)
+ QSKIP("SecTrustEvaluate() returns recoverable error, update the certificate on server");
+
postToHttpMultipart_data();
}
@@ -2987,15 +3407,15 @@ void tst_QNetworkReply::postToHttpsMultipart()
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 Ok
- QVERIFY(multiPart->boundary().count() > 20); // check that there is randomness after the "boundary_.oOo._" string
- QVERIFY(multiPart->boundary().count() < 70);
+ QVERIFY(multiPart->boundary().size() > 20); // check that there is randomness after the "boundary_.oOo._" string
+ QVERIFY(multiPart->boundary().size() < 70);
QByteArray replyData = reply->readAll();
expectedReplyData.prepend("content type: multipart/" + contentType + "; boundary=\"" + multiPart->boundary() + "\"\n");
QCOMPARE(replyData, expectedReplyData);
}
-#endif // QT_NO_SSL
+#endif // QT_CONFIG(ssl)
void tst_QNetworkReply::deleteFromHttp_data()
{
@@ -3124,7 +3544,7 @@ void tst_QNetworkReply::connectToIPv6Address()
if (!QtNetworkSettings::hasIPv6())
QSKIP("system doesn't support ipv6!");
- QByteArray httpResponse = QByteArray("HTTP/1.0 200 OK\r\nContent-Length: ");
+ QByteArray httpResponse = QByteArray("HTTP/1.0 200 OK\r\ncontent-length: ");
httpResponse += QByteArray::number(dataToSend.size());
httpResponse += "\r\n\r\n";
httpResponse += dataToSend;
@@ -3139,7 +3559,7 @@ void tst_QNetworkReply::connectToIPv6Address()
QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
QByteArray content = reply->readAll();
//qDebug() << server.receivedData;
- QByteArray hostinfo = "\r\nHost: " + hostfield + ':' + QByteArray::number(server.serverPort()) + "\r\n";
+ QByteArray hostinfo = "\r\nhost: " + hostfield + ':' + QByteArray::number(server.serverPort()) + "\r\n";
QVERIFY(server.receivedData.contains(hostinfo));
QCOMPARE(content, dataToSend);
QCOMPARE(reply->url(), request.url());
@@ -3301,6 +3721,18 @@ void tst_QNetworkReply::ioGetFromFile()
QCOMPARE(reader.data, data);
}
+void tst_QNetworkReply::ioGetFromFileUrl()
+{
+ // This immediately fails on non-windows platforms:
+ QNetworkRequest request(QUrl("file://unc-server/some/path"));
+ QNetworkReplyPtr reply(manager.get(request));
+ QSignalSpy finishedSpy(reply.get(), &QNetworkReply::finished);
+ // QTBUG-105618: This would, on non-Windows platforms, never happen because the signal
+ // was emitted before the constructor finished, leaving no chance at all to connect to the
+ // signal
+ QVERIFY(finishedSpy.wait());
+}
+
void tst_QNetworkReply::ioGetFromFtp_data()
{
if (!ftpSupported)
@@ -3318,7 +3750,9 @@ void tst_QNetworkReply::ioGetFromFtp()
{
QFETCH(QString, fileName);
QFile reference(fileName);
- reference.open(QIODevice::ReadOnly); // will fail for bigfile
+ const bool ok = reference.open(QIODevice::ReadOnly); // will fail for bigfile
+ if (fileName != "bigfile")
+ QVERIFY(ok);
QNetworkRequest request("ftp://" + QtNetworkSettings::ftpServerName() + "/qtest/" + fileName);
QNetworkReplyPtr reply(manager.get(request));
@@ -3343,7 +3777,7 @@ void tst_QNetworkReply::ioGetFromFtpWithReuse()
QSKIP("FTP is not supported");
QString fileName = testDataDir + "/rfc3252.txt";
QFile reference(fileName);
- reference.open(QIODevice::ReadOnly);
+ QVERIFY(reference.open(QIODevice::ReadOnly));
QNetworkRequest request(QUrl("ftp://" + QtNetworkSettings::ftpServerName() + "/qtest/rfc3252.txt"));
@@ -3473,7 +3907,7 @@ void tst_QNetworkReply::ioGetFromHttpWithAuth_data()
QTest::addColumn<int>("expectedAuth");
QFile reference(testDataDir + "/rfc3252.txt");
- reference.open(QIODevice::ReadOnly);
+ QVERIFY(reference.open(QIODevice::ReadOnly));
QByteArray referenceData = reference.readAll();
QString httpServer = QtNetworkSettings::httpServerName();
QTest::newRow("basic")
@@ -3547,7 +3981,7 @@ void tst_QNetworkReply::ioGetFromHttpWithAuth()
QCOMPARE(reader1.data, expectedData);
QCOMPARE(reader2.data, expectedData);
- QCOMPARE(authspy.count(), (expectedAuth ? 1 : 0));
+ QCOMPARE(authspy.size(), (expectedAuth ? 1 : 0));
expectedAuth = qMax(0, expectedAuth - 1);
}
@@ -3568,7 +4002,7 @@ void tst_QNetworkReply::ioGetFromHttpWithAuth()
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(reader.data, expectedData);
- QCOMPARE(authspy.count(), (expectedAuth ? 1 : 0));
+ QCOMPARE(authspy.size(), (expectedAuth ? 1 : 0));
expectedAuth = qMax(0, expectedAuth - 1);
}
@@ -3585,7 +4019,7 @@ void tst_QNetworkReply::ioGetFromHttpWithAuth()
// bad credentials in a synchronous request should just fail
QCOMPARE(replySync->error(), QNetworkReply::AuthenticationRequiredError);
} else {
- QCOMPARE(authspy.count(), 0);
+ QCOMPARE(authspy.size(), 0);
// we cannot use a data reader here, since that connects to the readyRead signal,
// just use readAll()
@@ -3611,7 +4045,7 @@ void tst_QNetworkReply::ioGetFromHttpWithAuth()
// bad credentials in a synchronous request should just fail
QCOMPARE(replySync->error(), QNetworkReply::AuthenticationRequiredError);
} else {
- QCOMPARE(authspy.count(), 0);
+ QCOMPARE(authspy.size(), 0);
// we cannot use a data reader here, since that connects to the readyRead signal,
// just use readAll()
@@ -3637,11 +4071,11 @@ void tst_QNetworkReply::ioGetFromHttpWithAuthSynchronous()
QNetworkReplyPtr replySync(manager.get(request));
QVERIFY(replySync->isFinished()); // synchronous
QCOMPARE(replySync->error(), QNetworkReply::AuthenticationRequiredError);
- QCOMPARE(authspy.count(), 0);
+ QCOMPARE(authspy.size(), 0);
QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 401);
}
-#ifndef QT_NO_NETWORKPROXY
+#if QT_CONFIG(networkproxy)
void tst_QNetworkReply::ioGetFromHttpWithProxyAuth()
{
// This test sends three requests
@@ -3678,7 +4112,7 @@ void tst_QNetworkReply::ioGetFromHttpWithProxyAuth()
QCOMPARE(reader1.data, referenceData);
QCOMPARE(reader2.data, referenceData);
- QCOMPARE(authspy.count(), 1);
+ QCOMPARE(authspy.size(), 1);
}
reference.seek(0);
@@ -3701,7 +4135,7 @@ void tst_QNetworkReply::ioGetFromHttpWithProxyAuth()
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(reader.data, reference.readAll());
- QCOMPARE(authspy.count(), 0);
+ QCOMPARE(authspy.size(), 0);
}
// now check with synchronous calls:
@@ -3714,7 +4148,7 @@ void tst_QNetworkReply::ioGetFromHttpWithProxyAuth()
QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
QNetworkReplyPtr replySync(manager.get(request));
QVERIFY(replySync->isFinished()); // synchronous
- QCOMPARE(authspy.count(), 0);
+ QCOMPARE(authspy.size(), 0);
// we cannot use a data reader here, since that connects to the readyRead signal,
// just use readAll()
@@ -3742,7 +4176,7 @@ void tst_QNetworkReply::ioGetFromHttpWithProxyAuthSynchronous()
manager.setProxy(QNetworkProxy()); // reset
QVERIFY(replySync->isFinished()); // synchronous
QCOMPARE(replySync->error(), QNetworkReply::ProxyAuthenticationRequiredError);
- QCOMPARE(authspy.count(), 0);
+ QCOMPARE(authspy.size(), 0);
QCOMPARE(replySync->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 407);
}
@@ -3774,7 +4208,7 @@ void tst_QNetworkReply::ioGetFromHttpWithSocksProxy()
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(reader.data, reference.readAll());
- QCOMPARE(authspy.count(), 0);
+ QCOMPARE(authspy.size(), 0);
}
// set an invalid proxy just to make sure that we can't load
@@ -3798,15 +4232,14 @@ void tst_QNetworkReply::ioGetFromHttpWithSocksProxy()
QVERIFY(reader.data.isEmpty());
QVERIFY(int(reply->error()) > 0);
- QEXPECT_FAIL("", "QTcpSocket doesn't return enough information yet", Continue);
QCOMPARE(int(reply->error()), int(QNetworkReply::ProxyConnectionRefusedError));
- QCOMPARE(authspy.count(), 0);
+ QCOMPARE(authspy.size(), 0);
}
}
-#endif // !QT_NO_NETWORKPROXY
+#endif // QT_CONFIG(networkproxy)
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
void tst_QNetworkReply::ioGetFromHttpsWithSslErrors()
{
QFile reference(testDataDir + "/rfc3252.txt");
@@ -3829,7 +4262,7 @@ void tst_QNetworkReply::ioGetFromHttpsWithSslErrors()
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(reader.data, reference.readAll());
- QCOMPARE(sslspy.count(), 1);
+ QCOMPARE(sslspy.size(), 1);
QVERIFY(!storedSslConfiguration.isNull());
QVERIFY(!reply->sslConfiguration().isNull());
@@ -3857,7 +4290,7 @@ void tst_QNetworkReply::ioGetFromHttpsWithIgnoreSslErrors()
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(reader.data, reference.readAll());
- QCOMPARE(sslspy.count(), 1);
+ QCOMPARE(sslspy.size(), 1);
QVERIFY(!storedSslConfiguration.isNull());
QVERIFY(!reply->sslConfiguration().isNull());
@@ -3880,7 +4313,7 @@ void tst_QNetworkReply::ioGetFromHttpsWithSslHandshakeError()
QCOMPARE(waitForFinish(reply), int(Failure));
QCOMPARE(reply->error(), QNetworkReply::SslHandshakeFailedError);
- QCOMPARE(sslspy.count(), 0);
+ QCOMPARE(sslspy.size(), 0);
}
#endif
@@ -3938,11 +4371,11 @@ void tst_QNetworkReply::ioGetFromHttpBrokenServer()
QCOMPARE(waitForFinish(reply), int(Failure));
QCOMPARE(reply->url(), request.url());
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QVERIFY(reply->error() != QNetworkReply::NoError);
}
-void tst_QNetworkReply::ioGetFromHttpStatus100_data()
+void tst_QNetworkReply::ioGetFromHttpStatusInformational_data()
{
QTest::addColumn<QByteArray>("dataToSend");
QTest::addColumn<int>("statusCode");
@@ -3953,9 +4386,25 @@ void tst_QNetworkReply::ioGetFromHttpStatus100_data()
QTest::newRow("minimal+404") << QByteArray("HTTP/1.1 100 Continue\n\nHTTP/1.0 204 No Content\r\n\r\n") << 204;
QTest::newRow("with_headers") << QByteArray("HTTP/1.1 100 Continue\r\nBla: x\r\n\r\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200;
QTest::newRow("with_headers2") << QByteArray("HTTP/1.1 100 Continue\nBla: x\n\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200;
+
+ QTest::newRow("normal-custom") << QByteArray("HTTP/1.1 133 Custom\r\n\r\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200;
+ QTest::newRow("minimal-custom") << QByteArray("HTTP/1.1 133 Custom\n\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200;
+ QTest::newRow("minimal2-custom") << QByteArray("HTTP/1.1 133 Custom\n\nHTTP/1.0 200 OK\r\n\r\n") << 200;
+ QTest::newRow("minimal3-custom") << QByteArray("HTTP/1.1 133 Custom\n\nHTTP/1.0 200 OK\n\n") << 200;
+ QTest::newRow("minimal+404-custom") << QByteArray("HTTP/1.1 133 Custom\n\nHTTP/1.0 204 No Content\r\n\r\n") << 204;
+ QTest::newRow("with_headers-custom") << QByteArray("HTTP/1.1 133 Custom\r\nBla: x\r\n\r\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200;
+ QTest::newRow("with_headers2-custom") << QByteArray("HTTP/1.1 133 Custom\nBla: x\n\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200;
+
+ QTest::newRow("normal-custom2") << QByteArray("HTTP/1.1 179 Custom2\r\n\r\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200;
+ QTest::newRow("minimal-custom2") << QByteArray("HTTP/1.1 179 Custom2\n\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200;
+ QTest::newRow("minimal2-custom2") << QByteArray("HTTP/1.1 179 Custom2\n\nHTTP/1.0 200 OK\r\n\r\n") << 200;
+ QTest::newRow("minimal3-custom2") << QByteArray("HTTP/1.1 179 Custom2\n\nHTTP/1.0 200 OK\n\n") << 200;
+ QTest::newRow("minimal+404-custom2") << QByteArray("HTTP/1.1 179 Custom2\n\nHTTP/1.0 204 No Content\r\n\r\n") << 204;
+ QTest::newRow("with_headers-custom2") << QByteArray("HTTP/1.1 179 Custom2\r\nBla: x\r\n\r\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200;
+ QTest::newRow("with_headers2-custom2") << QByteArray("HTTP/1.1 179 Custom2\nBla: x\n\nHTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") << 200;
}
-void tst_QNetworkReply::ioGetFromHttpStatus100()
+void tst_QNetworkReply::ioGetFromHttpStatusInformational()
{
QFETCH(QByteArray, dataToSend);
QFETCH(int, statusCode);
@@ -4193,7 +4642,7 @@ void tst_QNetworkReply::ioGetFromHttpWithCache()
QCOMPARE(reply->readAll().constData(), qPrintable(body));
}
-#ifndef QT_NO_NETWORKPROXY
+#if QT_CONFIG(networkproxy)
void tst_QNetworkReply::ioGetWithManyProxies_data()
{
QTest::addColumn<QList<QNetworkProxy> >("proxyList");
@@ -4254,7 +4703,7 @@ void tst_QNetworkReply::ioGetWithManyProxies_data()
<< QNetworkReply::NoError;
}
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
// HTTPS with HTTP transparent proxy
proxyList.clear();
proxyList << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::httpProxyServerName(), 3129);
@@ -4274,15 +4723,15 @@ void tst_QNetworkReply::ioGetWithManyProxies_data()
// Tests that fail:
- // HTTP request with FTP caching proxy
- proxyList.clear();
- proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121);
- QTest::newRow("http-on-ftp")
- << proxyList << QNetworkProxy()
- << "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
- << QNetworkReply::ProxyNotFoundError;
-
if (ftpSupported) {
+ // HTTP request with FTP caching proxy
+ proxyList.clear();
+ proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121);
+ QTest::newRow("http-on-ftp")
+ << proxyList << QNetworkProxy()
+ << "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
+ << QNetworkReply::ProxyNotFoundError;
+
// FTP request with HTTP caching proxy
proxyList.clear();
proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy,
@@ -4304,7 +4753,7 @@ void tst_QNetworkReply::ioGetWithManyProxies_data()
<< QNetworkReply::ProxyNotFoundError;
}
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
// HTTPS with HTTP caching proxy
proxyList.clear();
proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129);
@@ -4313,13 +4762,15 @@ void tst_QNetworkReply::ioGetWithManyProxies_data()
<< "https://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
<< QNetworkReply::ProxyNotFoundError;
- // HTTPS with FTP caching proxy
- proxyList.clear();
- proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121);
- QTest::newRow("https-on-ftp")
- << proxyList << QNetworkProxy()
- << "https://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
- << QNetworkReply::ProxyNotFoundError;
+ if (ftpSupported) {
+ // HTTPS with FTP caching proxy
+ proxyList.clear();
+ proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121);
+ QTest::newRow("https-on-ftp")
+ << proxyList << QNetworkProxy()
+ << "https://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
+ << QNetworkReply::ProxyNotFoundError;
+ }
#endif
// Complex requests:
@@ -4342,15 +4793,17 @@ void tst_QNetworkReply::ioGetWithManyProxies_data()
<< "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
<< QNetworkReply::NoError;
- // HTTP request with FTP + HTTP + SOCKS
- proxyList.clear();
- proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121)
- << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129)
- << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1081);
- QTest::newRow("http-on-ftp+http+socks")
- << proxyList << proxyList.at(1) // second proxy should be used
- << "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
- << QNetworkReply::NoError;
+ if (ftpSupported) {
+ // HTTP request with FTP + HTTP + SOCKS
+ proxyList.clear();
+ proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121)
+ << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129)
+ << QNetworkProxy(QNetworkProxy::Socks5Proxy, QtNetworkSettings::socksProxyServerName(), 1081);
+ QTest::newRow("http-on-ftp+http+socks")
+ << proxyList << proxyList.at(1) // second proxy should be used
+ << "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
+ << QNetworkReply::NoError;
+ }
// HTTP request with NoProxy + HTTP
proxyList.clear();
@@ -4362,15 +4815,15 @@ void tst_QNetworkReply::ioGetWithManyProxies_data()
<< QNetworkReply::NoError;
// HTTP request with FTP + NoProxy
- proxyList.clear();
- proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121)
- << QNetworkProxy(QNetworkProxy::NoProxy);
- QTest::newRow("http-on-ftp+noproxy")
- << proxyList << proxyList.at(1) // second proxy should be used
- << "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
- << QNetworkReply::NoError;
-
if (ftpSupported) {
+ proxyList.clear();
+ proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121)
+ << QNetworkProxy(QNetworkProxy::NoProxy);
+ QTest::newRow("http-on-ftp+noproxy")
+ << proxyList << proxyList.at(1) // second proxy should be used
+ << "http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
+ << QNetworkReply::NoError;
+
// FTP request with HTTP Caching + FTP
proxyList.clear();
proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy,
@@ -4383,7 +4836,7 @@ void tst_QNetworkReply::ioGetWithManyProxies_data()
<< QNetworkReply::NoError;
}
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
// HTTPS request with HTTP Caching + HTTP transparent
proxyList.clear();
proxyList << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129)
@@ -4393,15 +4846,17 @@ void tst_QNetworkReply::ioGetWithManyProxies_data()
<< "https://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
<< QNetworkReply::NoError;
- // HTTPS request with FTP + HTTP C + HTTP T
- proxyList.clear();
- proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121)
- << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129)
- << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::httpProxyServerName(), 3129);
- QTest::newRow("https-on-ftp+httpcaching+http")
- << proxyList << proxyList.at(2) // skip the first two
- << "https://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
- << QNetworkReply::NoError;
+ if (ftpSupported) {
+ // HTTPS request with FTP + HTTP C + HTTP T
+ proxyList.clear();
+ proxyList << QNetworkProxy(QNetworkProxy::FtpCachingProxy, QtNetworkSettings::ftpProxyServerName(), 2121)
+ << QNetworkProxy(QNetworkProxy::HttpCachingProxy, QtNetworkSettings::httpProxyServerName(), 3129)
+ << QNetworkProxy(QNetworkProxy::HttpProxy, QtNetworkSettings::httpProxyServerName(), 3129);
+ QTest::newRow("https-on-ftp+httpcaching+http")
+ << proxyList << proxyList.at(2) // skip the first two
+ << "https://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt"
+ << QNetworkReply::NoError;
+ }
#endif
}
@@ -4427,7 +4882,7 @@ void tst_QNetworkReply::ioGetWithManyProxies()
QSignalSpy authspy(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
connect(&manager, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
connect(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
#endif
@@ -4436,7 +4891,7 @@ void tst_QNetworkReply::ioGetWithManyProxies()
manager.disconnect(SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
this, SLOT(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)));
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
manager.disconnect(SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
this, SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
#endif
@@ -4456,19 +4911,19 @@ void tst_QNetworkReply::ioGetWithManyProxies()
// now verify that the proxies worked:
QFETCH(QNetworkProxy, proxyUsed);
if (proxyUsed.type() == QNetworkProxy::NoProxy) {
- QCOMPARE(authspy.count(), 0);
+ QCOMPARE(authspy.size(), 0);
} else {
if (QByteArray(QTest::currentDataTag()).startsWith("ftp-"))
return; // No authentication with current FTP or with FTP proxies
- QCOMPARE(authspy.count(), 1);
+ QCOMPARE(authspy.size(), 1);
QCOMPARE(qvariant_cast<QNetworkProxy>(authspy.at(0).at(0)), proxyUsed);
}
} else {
// request failed
- QCOMPARE(authspy.count(), 0);
+ QCOMPARE(authspy.size(), 0);
}
}
-#endif // !QT_NO_NETWORKPROXY
+#endif // QT_CONFIG(networkproxy)
void tst_QNetworkReply::ioPutToFileFromFile_data()
{
@@ -4571,10 +5026,6 @@ void tst_QNetworkReply::ioPutToFileFromLocalSocket()
QNetworkReplyPtr reply(manager.put(QNetworkRequest(url), passive));
passive->setParent(reply.data());
-#ifdef Q_OS_WIN
- if (!data.isEmpty())
- QEXPECT_FAIL("", "QTBUG-18385", Abort);
-#endif
QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
QCOMPARE(reply->error(), QNetworkReply::NoError);
@@ -4637,6 +5088,10 @@ void tst_QNetworkReply::ioPutToFileFromProcess()
QByteArray contents = file.readAll();
QCOMPARE(contents, data);
+ if (process.state() == QProcess::Running)
+ QVERIFY(process.waitForFinished());
+ QCOMPARE(process.exitCode(), 0);
+
#endif // QT_CONFIG(process)
}
@@ -4765,7 +5220,7 @@ void tst_QNetworkReply::ioPostToHttpFromFile()
QCOMPARE(reply->readAll().trimmed(), md5sum(sourceFile.readAll()).toHex());
}
-#ifndef QT_NO_NETWORKPROXY
+#if QT_CONFIG(networkproxy)
void tst_QNetworkReply::ioPostToHttpFromSocket_data()
{
QTest::addColumn<QByteArray>("data");
@@ -4775,7 +5230,7 @@ void tst_QNetworkReply::ioPostToHttpFromSocket_data()
QTest::addColumn<int>("authenticationRequiredCount");
QTest::addColumn<int>("proxyAuthenticationRequiredCount");
- for (int i = 0; i < proxies.count(); ++i)
+ for (int i = 0; i < proxies.size(); ++i)
for (int auth = 0; auth < 2; ++auth) {
QUrl url;
if (auth)
@@ -4853,8 +5308,8 @@ void tst_QNetworkReply::ioPostToHttpFromSocket()
QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex());
- QTEST(int(authenticationRequiredSpy.count()), "authenticationRequiredCount");
- QTEST(int(proxyAuthenticationRequiredSpy.count()), "proxyAuthenticationRequiredCount");
+ QTEST(int(authenticationRequiredSpy.size()), "authenticationRequiredCount");
+ QTEST(int(proxyAuthenticationRequiredSpy.size()), "proxyAuthenticationRequiredCount");
}
void tst_QNetworkReply::ioPostToHttpFromSocketSynchronous_data()
@@ -4916,7 +5371,7 @@ void tst_QNetworkReply::ioPostToHttpFromSocketSynchronous()
QCOMPARE(reply->readAll().trimmed(), md5sum(data).toHex());
}
-#endif // !QT_NO_NETWORKPROXY
+#endif // QT_CONFIG(networkproxy)
// this tests checks if rewinding the POST-data to some place in the middle
// worked.
@@ -5033,7 +5488,7 @@ void tst_QNetworkReply::ioPostToHttpNoBufferFlag()
QCOMPARE(reply->error(), QNetworkReply::ContentReSendError);
}
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
class SslServer : public QTcpServer
{
Q_OBJECT
@@ -5149,7 +5604,7 @@ void tst_QNetworkReply::ioGetFromBuiltinHttp_data()
QTest::addColumn<int>("bufferSize");
QTest::newRow("http+unlimited") << false << 0;
QTest::newRow("http+limited") << false << 4096;
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
QTest::newRow("https+unlimited") << true << 0;
QTest::newRow("https+limited") << true << 4096;
#endif
@@ -5326,13 +5781,9 @@ void tst_QNetworkReply::emitAllUploadProgressSignals()
QNetworkRequest catchAllSignalsRequest(normalRequest);
catchAllSignalsRequest.setAttribute(QNetworkRequest::EmitAllUploadProgressSignalsAttribute, true);
- QList<QNetworkRequest> requests;
- requests << normalRequest << catchAllSignalsRequest;
-
QList<int> signalCount;
- foreach (const QNetworkRequest &request, requests) {
-
+ for (const QNetworkRequest &request : {normalRequest, catchAllSignalsRequest}) {
sourceFile.seek(0);
QNetworkReplyPtr reply(manager.post(request, &sourceFile));
QSignalSpy spy(reply.data(), SIGNAL(uploadProgress(qint64,qint64)));
@@ -5353,7 +5804,7 @@ void tst_QNetworkReply::emitAllUploadProgressSignals()
QVERIFY(!QTestEventLoop::instance().timeout());
incomingSocket->close();
- signalCount.append(spy.count());
+ signalCount.append(spy.size());
reply->deleteLater();
}
server.close();
@@ -5399,7 +5850,7 @@ void tst_QNetworkReply::ioPostToHttpEmptyUploadProgress()
QVERIFY(!QTestEventLoop::instance().timeout());
// final check: only 1 uploadProgress has been emitted
- QCOMPARE(spy.length(), 1);
+ QCOMPARE(spy.size(), 1);
QList<QVariant> args = spy.last();
QVERIFY(!args.isEmpty());
QCOMPARE(args.at(0).toLongLong(), buffer.size());
@@ -5437,7 +5888,7 @@ void tst_QNetworkReply::lastModifiedHeaderForHttp()
QDateTime header = reply->header(QNetworkRequest::LastModifiedHeader).toDateTime();
QDateTime realDate = QDateTime::fromString("2007-05-22T12:04:57", Qt::ISODate);
- realDate.setTimeSpec(Qt::UTC);
+ realDate.setTimeZone(QTimeZone::UTC);
QCOMPARE(header, realDate);
}
@@ -5570,7 +6021,7 @@ void tst_QNetworkReply::downloadProgress()
QVERIFY(!QTestEventLoop::instance().timeout());
QVERIFY(reply->isFinished());
- QVERIFY(spy.count() > 0);
+ QVERIFY(spy.size() > 0);
//final progress should have equal current & total
QList<QVariant> args = spy.takeLast();
@@ -5616,14 +6067,14 @@ void tst_QNetworkReply::uploadProgress()
QVERIFY(server.hasPendingConnections());
QTcpSocket *receiver = server.nextPendingConnection();
- if (finished.count() == 0) {
+ if (finished.size() == 0) {
// it's not finished yet, so wait for it to be
QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
}
delete receiver;
- QVERIFY(finished.count() > 0);
- QVERIFY(spy.count() > 0);
+ QVERIFY(finished.size() > 0);
+ QVERIFY(spy.size() > 0);
QList<QVariant> args = spy.last();
QCOMPARE(args.at(0).toInt(), data.size());
@@ -5905,11 +6356,11 @@ void tst_QNetworkReply::nestedEventLoops()
QTestEventLoop::instance().enterLoop(20);
QVERIFY2(!QTestEventLoop::instance().timeout(), "Network timeout");
- QCOMPARE(finishedspy.count(), 1);
- QCOMPARE(errorspy.count(), 0);
+ QCOMPARE(finishedspy.size(), 1);
+ QCOMPARE(errorspy.size(), 0);
}
-#ifndef QT_NO_NETWORKPROXY
+#if QT_CONFIG(networkproxy)
void tst_QNetworkReply::httpProxyCommands_data()
{
QTest::addColumn<QUrl>("url");
@@ -5920,7 +6371,7 @@ void tst_QNetworkReply::httpProxyCommands_data()
<< QUrl("http://0.0.0.0:4443/http-request")
<< QByteArray("HTTP/1.0 200 OK\r\nProxy-Connection: close\r\nContent-Length: 1\r\n\r\n1")
<< "GET http://0.0.0.0:4443/http-request HTTP/1.";
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
QTest::newRow("https")
<< QUrl("https://0.0.0.0:4443/https-request")
<< QByteArray("HTTP/1.0 200 Connection Established\r\n\r\n")
@@ -5939,7 +6390,7 @@ void tst_QNetworkReply::httpProxyCommands()
manager.setProxy(proxy);
QNetworkRequest request(url);
- request.setRawHeader("User-Agent", "QNetworkReplyAutoTest/1.0");
+ request.setRawHeader("user-agent", "QNetworkReplyAutoTest/1.0");
QNetworkReplyPtr reply(manager.get(request));
// wait for the finished signal
@@ -5952,14 +6403,15 @@ void tst_QNetworkReply::httpProxyCommands()
// especially since it won't succeed in the HTTPS case
// so just check that the command was correct
- QString receivedHeader = proxyServer.receivedData.left(expectedCommand.length());
+ QString receivedHeader = proxyServer.receivedData.left(expectedCommand.size());
QCOMPARE(receivedHeader, expectedCommand);
//QTBUG-17223 - make sure the user agent from the request is sent to proxy server even for CONNECT
- int uapos = proxyServer.receivedData.indexOf("User-Agent");
+ const QByteArray cUserAgent = "user-agent: ";
+ int uapos = proxyServer.receivedData.toLower().indexOf(cUserAgent) + cUserAgent.size();
int uaend = proxyServer.receivedData.indexOf("\r\n", uapos);
QByteArray uaheader = proxyServer.receivedData.mid(uapos, uaend - uapos);
- QCOMPARE(uaheader, QByteArray("User-Agent: QNetworkReplyAutoTest/1.0"));
+ QCOMPARE(uaheader, QByteArray("QNetworkReplyAutoTest/1.0"));
}
class ProxyChangeHelper : public QObject
@@ -5982,7 +6434,7 @@ void tst_QNetworkReply::httpProxyCommandsSynchronous_data()
{
httpProxyCommands_data();
}
-#endif // !QT_NO_NETWORKPROXY
+#endif // QT_CONFIG(networkproxy)
struct QThreadCleanup
{
@@ -5996,15 +6448,7 @@ struct QThreadCleanup
}
};
-struct QDeleteLaterCleanup
-{
- static inline void cleanup(QObject *o)
- {
- o->deleteLater();
- }
-};
-
-#ifndef QT_NO_NETWORKPROXY
+#if QT_CONFIG(networkproxy)
void tst_QNetworkReply::httpProxyCommandsSynchronous()
{
QFETCH(QUrl, url);
@@ -6015,7 +6459,7 @@ void tst_QNetworkReply::httpProxyCommandsSynchronous()
// the server thread, because the client is never returning to the
// event loop
QScopedPointer<QThread, QThreadCleanup> serverThread(new QThread);
- QScopedPointer<MiniHttpServer, QDeleteLaterCleanup> proxyServer(new MiniHttpServer(responseToSend, false, serverThread.data()));
+ QScopedPointer<MiniHttpServer, QScopedPointerDeleteLater> proxyServer(new MiniHttpServer(responseToSend, false, serverThread.data()));
QNetworkProxy proxy(QNetworkProxy::HttpProxy, "127.0.0.1", proxyServer->serverPort());
manager.setProxy(proxy);
@@ -6036,7 +6480,7 @@ void tst_QNetworkReply::httpProxyCommandsSynchronous()
// especially since it won't succeed in the HTTPS case
// so just check that the command was correct
- QString receivedHeader = proxyServer->receivedData.left(expectedCommand.length());
+ QString receivedHeader = proxyServer->receivedData.left(expectedCommand.size());
QCOMPARE(receivedHeader, expectedCommand);
}
@@ -6094,7 +6538,7 @@ void tst_QNetworkReply::proxyChange()
QVERIFY(int(reply3->error()) > 0);
}
-#endif // !QT_NO_NETWORKPROXY
+#endif // QT_CONFIG(networkproxy)
void tst_QNetworkReply::authorizationError_data()
{
@@ -6133,9 +6577,9 @@ void tst_QNetworkReply::authorizationError()
QCOMPARE(waitForFinish(reply), int(Failure));
QFETCH(int, errorSignalCount);
- QCOMPARE(errorSpy.count(), errorSignalCount);
+ QCOMPARE(errorSpy.size(), errorSignalCount);
QFETCH(int, finishedSignalCount);
- QCOMPARE(finishedSpy.count(), finishedSignalCount);
+ QCOMPARE(finishedSpy.size(), finishedSignalCount);
QFETCH(int, error);
QCOMPARE(reply->error(), QNetworkReply::NetworkError(error));
@@ -6173,7 +6617,6 @@ void tst_QNetworkReply::httpConnectionCount()
}
QVERIFY(server->listen());
- QCoreApplication::instance()->processEvents();
QUrl url("http://127.0.0.1:" + QString::number(server->serverPort()) + QLatin1Char('/'));
if (encrypted)
@@ -6184,7 +6627,7 @@ void tst_QNetworkReply::httpConnectionCount()
QUrl urlCopy = url;
urlCopy.setPath(u'/' + QString::number(i)); // Differentiate the requests a bit
QNetworkRequest request(urlCopy);
- request.setAttribute(QNetworkRequest::Http2AllowedAttribute, http2Enabled);
+ request.setAttribute(QNetworkRequest::Http2CleartextAllowedAttribute, http2Enabled);
QNetworkReply* reply = manager.get(request);
reply->setParent(server.data());
if (encrypted)
@@ -6192,26 +6635,40 @@ void tst_QNetworkReply::httpConnectionCount()
}
int pendingConnectionCount = 0;
- QElapsedTimer timer;
- timer.start();
- while(pendingConnectionCount <= 20) {
- QTestEventLoop::instance().enterLoop(1);
+ using namespace std::chrono_literals;
+ const auto newPendingConnection = [&server]() { return server->hasPendingConnections(); };
+ // If we have http2 enabled then the second connection will take a little
+ // longer to be established because we will wait for the first one to finish
+ // to see if we should upgrade:
+ const int rampDown = http2Enabled ? 2 : 1;
+ while (pendingConnectionCount <= 6) {
+ if (!QTest::qWaitFor(newPendingConnection, pendingConnectionCount >= rampDown ? 3s : 7s))
+ break;
QTcpSocket *socket = server->nextPendingConnection();
- while (socket != 0) {
- if (pendingConnectionCount == 0) {
- // respond to the first connection so we know to transition to HTTP/1.1 when using
- // HTTP/2
- socket->write(httpEmpty200Response);
+ while (socket) {
+ if (pendingConnectionCount == 0 && http2Enabled) {
+ // Respond to the first connection so we know to transition to HTTP/1.1 when using
+ // HTTP/2.
+ // Because of some internal state machinery we need to wait until the request has
+ // actually been written to the server before we can reply.
+ auto connection = std::make_shared<QMetaObject::Connection>();
+ auto replyOnRequest = [=, buffer = QByteArray()]() mutable {
+ buffer += socket->readAll();
+ if (!buffer.contains("\r\n\r\n"))
+ return;
+ socket->write(httpEmpty200Response);
+ QObject::disconnect(*connection);
+ };
+ *connection = QObject::connect(socket, &QTcpSocket::readyRead, socket,
+ std::move(replyOnRequest));
+ if (socket->bytesAvailable()) // If we already have data, check it now
+ emit socket->readyRead();
}
pendingConnectionCount++;
socket->setParent(server.data());
socket = server->nextPendingConnection();
}
-
- // at max. wait 10 sec
- if (timer.elapsed() > 10000)
- break;
}
QCOMPARE(pendingConnectionCount, 6);
@@ -6391,7 +6848,7 @@ void tst_QNetworkReply::httpRecursiveCreation()
QVERIFY(!QTestEventLoop::instance().timeout());
}
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
void tst_QNetworkReply::ignoreSslErrorsList_data()
{
QTest::addColumn<QList<QSslError> >("expectedSslErrors");
@@ -6399,8 +6856,8 @@ void tst_QNetworkReply::ignoreSslErrorsList_data()
QList<QSslError> expectedSslErrors;
QList<QSslCertificate> certs = QSslCertificate::fromPath(testDataDir + certsFilePath);
- QSslError rightError(FLUKE_CERTIFICATE_ERROR, certs.at(0));
- QSslError wrongError(FLUKE_CERTIFICATE_ERROR);
+ QSslError rightError(flukeCertTlsError, certs.at(0));
+ QSslError wrongError(flukeCertTlsError);
QTest::newRow("SSL-failure-empty-list") << expectedSslErrors << QNetworkReply::SslHandshakeFailedError;
expectedSslErrors.append(wrongError);
@@ -6467,23 +6924,23 @@ void tst_QNetworkReply::sslConfiguration_data()
QTest::newRow("empty") << QSslConfiguration() << false;
QSslConfiguration conf = QSslConfiguration::defaultConfiguration();
QTest::newRow("default") << conf << false; // does not contain test server cert
-#if QT_CONFIG(securetransport)
- qWarning("SecTrustEvaluate() will fail, update the certificate on server");
-#else
- QList<QSslCertificate> testServerCert = QSslCertificate::fromPath(testDataDir + certsFilePath);
- conf.setCaCertificates(testServerCert);
+ if (isSecureTransport) {
+ qWarning("SecTrustEvaluate() will fail, update the certificate on server");
+ } else {
+ QList<QSslCertificate> testServerCert = QSslCertificate::fromPath(testDataDir + certsFilePath);
+ conf.setCaCertificates(testServerCert);
- QTest::newRow("set-root-cert") << conf << true;
- conf.setProtocol(QSsl::SecureProtocols);
- QTest::newRow("secure") << conf << true;
-#endif
+ QTest::newRow("set-root-cert") << conf << true;
+ conf.setProtocol(QSsl::SecureProtocols);
+ QTest::newRow("secure") << conf << true;
+ }
}
void tst_QNetworkReply::encrypted()
{
-#if QT_CONFIG(securetransport)
- QSKIP("SecTrustEvalute() fails with old server certificate");
-#endif
+ if (isSecureTransport)
+ QSKIP("SecTrustEvalute() fails with old server certificate");
+
QUrl url("https://" + QtNetworkSettings::httpServerName());
QNetworkRequest request(url);
QNetworkReply *reply = manager.get(request);
@@ -6494,7 +6951,7 @@ void tst_QNetworkReply::encrypted()
QTestEventLoop::instance().enterLoop(20);
QVERIFY(!QTestEventLoop::instance().timeout());
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
reply->deleteLater();
}
@@ -6509,8 +6966,7 @@ void tst_QNetworkReply::abortOnEncrypted()
server.connect(&server, &SslServer::newEncryptedConnection, [&server]() {
// MSVC 201X C4573-misunderstands connect() or QObject::connect(), so use server.connect():
server.connect(server.socket, &QTcpSocket::readyRead, server.socket, []() {
- // This slot must not be invoked!
- QVERIFY(false);
+ QFAIL("This slot must not be invoked!");
});
});
@@ -6524,7 +6980,7 @@ void tst_QNetworkReply::abortOnEncrypted()
});
QSignalSpy spyEncrypted(reply, &QNetworkReply::encrypted);
- QTRY_COMPARE(spyEncrypted.count(), 1);
+ QTRY_COMPARE(spyEncrypted.size(), 1);
// Wait for the socket to be closed again in order to be sure QTcpSocket::readyRead would have been emitted.
QTRY_VERIFY(server.socket != nullptr);
@@ -6556,9 +7012,8 @@ void tst_QNetworkReply::sslSessionSharing_data()
void tst_QNetworkReply::sslSessionSharing()
{
-#if QT_CONFIG(schannel) || defined(QT_SECURETRANSPORT)
- QSKIP("Not implemented with SecureTransport/Schannel");
-#endif
+ if (isSchannel || isSecureTransport)
+ QSKIP("Not implemented with SecureTransport/Schannel");
QString urlString("https://" + QtNetworkSettings::httpServerName());
QList<QNetworkReplyPtr> replies;
@@ -6627,9 +7082,8 @@ void tst_QNetworkReply::sslSessionSharingFromPersistentSession_data()
void tst_QNetworkReply::sslSessionSharingFromPersistentSession()
{
-#if QT_CONFIG(schannel) || defined(QT_SECURETRANSPORT)
- QSKIP("Not implemented with SecureTransport/Schannel");
-#endif
+ if (isSchannel || isSecureTransport)
+ QSKIP("Not implemented with SecureTransport/Schannel");
QString urlString("https://" + QtNetworkSettings::httpServerName());
@@ -6682,7 +7136,7 @@ void tst_QNetworkReply::sslSessionSharingFromPersistentSession()
}
#endif // QT_BUILD_INTERNAL
-#endif // QT_NO_SSL
+#endif // QT_CONFIG(ssl)
void tst_QNetworkReply::getAndThenDeleteObject_data()
{
@@ -6720,7 +7174,20 @@ void tst_QNetworkReply::getAndThenDeleteObject()
// see https://bugs.webkit.org/show_bug.cgi?id=38935
void tst_QNetworkReply::symbianOpenCDataUrlCrash()
{
- QString requestUrl("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABkAAAAWCAYAAAA1vze2AAAAB3RJTUUH2AUSEgolrgBvVQAAAAlwSFlzAAALEwAACxMBAJqcGAAAAARnQU1BAACxjwv8YQUAAAHlSURBVHja5VbNShxBEK6ZaXtnHTebQPA1gngNmfaeq+QNPIlIXkC9iQdJxJNvEHLN3VkxhxxE8gTmEhAVddXZ6Z3f9Ndriz89/sHmkBQUVVT1fB9d9c3uOERUKTunIdn3HzstxGpYBDS4wZk7TAJj/wlJ90J+jnuygqs8svSj+/rGHBos3rE18XBvfU3no7NzlJfUaY/5whAwl8Lr/WDUv4ODxTMb+P5xLExe5LmO559WqTX/MQR4WZYEAtSePS4pE0qSnuhnRUcBU5Gm2k9XljU4Z26I3NRxBrd80rj2fh+KNE0FY4xevRgTjREvPFpasAK8Xli6MUbbuKw3afAGgSBXozo5u4hkmncAlkl5wx8iMGbdyQjnCFEiEwGiosj1UQA/x2rVddiVoi+l4IxE0PTDnx+mrQBvvnx9cFz3krhVvuhzFn579/aq/n5rW8fbtTqiWhIQZEo17YBvbkxOXNVndnYpTvod7AtiuN2re0+siwcB9oH8VxxrNwQQAhzyRs30n7wTI2HIN2g2QtQwjjhJIQatOq7E8bIVCLwzpl83Lvtvl+NohWWlE8UZTWEMAGCcR77fHKhPnZF5tYie6dfdxCphACmLPM+j8bYfmTryg64kV9Vh3mV8jP0b/4wO/YUPiT/8i0MLf55lSQAAAABJRU5ErkJggg==");
+ QString requestUrl("data:image/"
+ "png;base64,"
+ "iVBORw0KGgoAAAANSUhEUgAAABkAAAAWCAYAAAA1vze2AAAAB3RJTUUH2AUSEgolrgBvVQAAAAl"
+ "wSFlzAAALEwAACxMBAJqcGAAAAARnQU1BAACxjwv8YQUAAAHlSURBVHja5VbNShxBEK6ZaXtnHT"
+ "ebQPA1gngNmfaeq+QNPIlIXkC9iQdJxJNvEHLN3VkxhxxE8gTmEhAVddXZ6Z3f9Ndriz89/"
+ "sHmkBQUVVT1fB9d9c3uOERUKTunIdn3HzstxGpYBDS4wZk7TAJj/wlJ90J+jnuygqs8svSj+/"
+ "rGHBos3rE18XBvfU3no7NzlJfUaY/5whAwl8Lr/WDUv4ODxTMb+P5xLExe5LmO559WqTX/"
+ "MQR4WZYEAtSePS4pE0qSnuhnRUcBU5Gm2k9XljU4Z26I3NRxBrd80rj2fh+"
+ "KNE0FY4xevRgTjREvPFpasAK8Xli6MUbbuKw3afAGgSBXozo5u4hkmncAlkl5wx8iMGbdyQjnCF"
+ "EiEwGiosj1UQA/x2rVddiVoi+l4IxE0PTDnx+mrQBvvnx9cFz3krhVvuhzFn579/aq/"
+ "n5rW8fbtTqiWhIQZEo17YBvbkxOXNVndnYpTvod7AtiuN2re0+"
+ "siwcB9oH8VxxrNwQQAhzyRs30n7wTI2HIN2g2QtQwjjhJIQatOq7E8bIVCLwzpl83Lvtvl+"
+ "NohWWlE8UZTWEMAGCcR77fHKhPnZF5tYie6dfdxCphACmLPM+j8bYfmTryg64kV9Vh3mV8jP0b/"
+ "4wO/YUPiT/8i0MLf55lSQAAAABJRU5ErkJggg==");
QUrl url = QUrl::fromEncoded(requestUrl.toLatin1());
QNetworkRequest req(url);
QNetworkReplyPtr reply;
@@ -7037,8 +7504,7 @@ void tst_QNetworkReply::qtbug12908compressedHttpReply()
QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
// QDecompressHelper will abort the download if the compressed to decompressed size ratio
// differs too much, so we override it
- request.setAttribute(QNetworkRequest::Attribute(QNetworkRequest::User - 1),
- QByteArray("__qdecompresshelper_ignore_download_ratio"));
+ request.setDecompressedSafetyCheckThreshold(-1);
QNetworkReplyPtr reply(manager.get(request));
QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
@@ -7066,7 +7532,7 @@ void tst_QNetworkReply::compressedHttpReplyBrokenGzip()
QCOMPARE(waitForFinish(reply), int(Failure));
- QCOMPARE(reply->error(), QNetworkReply::ProtocolFailure);
+ QCOMPARE(reply->error(), QNetworkReply::UnknownContentError);
}
// TODO add similar test for FTP
@@ -7110,25 +7576,25 @@ void tst_QNetworkReply::qtbug4121unknownAuthentication()
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
- QCOMPARE(authSpy.count(), 0);
- QCOMPARE(finishedSpy.count(), 1);
- QCOMPARE(errorSpy.count(), 1);
+ QCOMPARE(authSpy.size(), 0);
+ QCOMPARE(finishedSpy.size(), 1);
+ QCOMPARE(errorSpy.size(), 1);
QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError);
}
-#ifndef QT_NO_NETWORKPROXY
+#if QT_CONFIG(networkproxy)
void tst_QNetworkReply::authenticationCacheAfterCancel_data()
{
QTest::addColumn<QNetworkProxy>("proxy");
QTest::addColumn<bool>("proxyAuth");
QTest::addColumn<QUrl>("url");
- for (int i = 0; i < proxies.count(); ++i) {
+ for (int i = 0; i < proxies.size(); ++i) {
QTest::newRow("http" + proxies.at(i).tag)
<< proxies.at(i).proxy
<< proxies.at(i).requiresAuthentication
<< QUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/rfcs-auth/rfc3252.txt");
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
QTest::newRow("https" + proxies.at(i).tag)
<< proxies.at(i).proxy
<< proxies.at(i).requiresAuthentication
@@ -7184,7 +7650,7 @@ void tst_QNetworkReply::authenticationCacheAfterCancel()
QFETCH(bool, proxyAuth);
QFETCH(QUrl, url);
QNetworkAccessManager manager;
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
connect(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
#endif
@@ -7206,8 +7672,8 @@ void tst_QNetworkReply::authenticationCacheAfterCancel()
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->error(), QNetworkReply::ProxyAuthenticationRequiredError);
- QCOMPARE(authSpy.count(), 0);
- QCOMPARE(proxyAuthSpy.count(), 1);
+ QCOMPARE(authSpy.size(), 0);
+ QCOMPARE(proxyAuthSpy.size(), 1);
proxyAuthSpy.clear();
//should fail due to bad credentials
@@ -7221,8 +7687,8 @@ void tst_QNetworkReply::authenticationCacheAfterCancel()
// Work round known quirk in the old test server (danted -v < v1.1.19):
if (reply->error() != QNetworkReply::HostNotFoundError)
QCOMPARE(reply->error(), QNetworkReply::ProxyAuthenticationRequiredError);
- QCOMPARE(authSpy.count(), 0);
- QVERIFY(proxyAuthSpy.count() > 0);
+ QCOMPARE(authSpy.size(), 0);
+ QVERIFY(proxyAuthSpy.size() > 0);
proxyAuthSpy.clear();
// QTBUG-23136 workaround (needed even with danted v1.1.19):
@@ -7247,10 +7713,10 @@ void tst_QNetworkReply::authenticationCacheAfterCancel()
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError);
- QVERIFY(authSpy.count() > 0);
+ QVERIFY(authSpy.size() > 0);
authSpy.clear();
if (proxyAuth) {
- QVERIFY(proxyAuthSpy.count() > 0);
+ QVERIFY(proxyAuthSpy.size() > 0);
proxyAuthSpy.clear();
}
@@ -7263,11 +7729,11 @@ void tst_QNetworkReply::authenticationCacheAfterCancel()
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError);
- QVERIFY(authSpy.count() > 0);
+ QVERIFY(authSpy.size() > 0);
authSpy.clear();
if (proxyAuth) {
//should be supplied from cache
- QCOMPARE(proxyAuthSpy.count(), 0);
+ QCOMPARE(proxyAuthSpy.size(), 0);
proxyAuthSpy.clear();
}
@@ -7281,11 +7747,11 @@ void tst_QNetworkReply::authenticationCacheAfterCancel()
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->error(), QNetworkReply::NoError);
- QVERIFY(authSpy.count() > 0);
+ QVERIFY(authSpy.size() > 0);
authSpy.clear();
if (proxyAuth) {
//should be supplied from cache
- QCOMPARE(proxyAuthSpy.count(), 0);
+ QCOMPARE(proxyAuthSpy.size(), 0);
proxyAuthSpy.clear();
}
@@ -7297,11 +7763,11 @@ void tst_QNetworkReply::authenticationCacheAfterCancel()
QCOMPARE(reply->error(), QNetworkReply::NoError);
//should be supplied from cache
- QCOMPARE(authSpy.count(), 0);
+ QCOMPARE(authSpy.size(), 0);
authSpy.clear();
if (proxyAuth) {
//should be supplied from cache
- QCOMPARE(proxyAuthSpy.count(), 0);
+ QCOMPARE(proxyAuthSpy.size(), 0);
proxyAuthSpy.clear();
}
@@ -7311,7 +7777,7 @@ void tst_QNetworkReply::authenticationWithDifferentRealm()
{
AuthenticationCacheHelper helper;
QNetworkAccessManager manager;
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
connect(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)),
SLOT(sslErrors(QNetworkReply*,QList<QSslError>)));
#endif
@@ -7338,7 +7804,7 @@ void tst_QNetworkReply::authenticationWithDifferentRealm()
QVERIFY(!QTestEventLoop::instance().timeout());
QCOMPARE(reply->error(), QNetworkReply::NoError);
}
-#endif // !QT_NO_NETWORKPROXY
+#endif // QT_CONFIG(networkproxy)
class QtBug13431Helper : public QObject
{
@@ -7403,8 +7869,8 @@ void tst_QNetworkReply::httpWithNoCredentialUsage()
QNetworkReplyPtr reply(manager.get(request));
QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
// credentials in URL, so don't expect authentication signal
- QCOMPARE(authSpy.count(), 0);
- QCOMPARE(finishedSpy.count(), 1);
+ QCOMPARE(authSpy.size(), 0);
+ QCOMPARE(finishedSpy.size(), 1);
finishedSpy.clear();
}
@@ -7414,8 +7880,8 @@ void tst_QNetworkReply::httpWithNoCredentialUsage()
QNetworkReplyPtr reply(manager.get(request));
QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
// credentials in cache, so don't expect authentication signal
- QCOMPARE(authSpy.count(), 0);
- QCOMPARE(finishedSpy.count(), 1);
+ QCOMPARE(authSpy.size(), 0);
+ QCOMPARE(finishedSpy.size(), 1);
finishedSpy.clear();
}
@@ -7432,9 +7898,9 @@ void tst_QNetworkReply::httpWithNoCredentialUsage()
QVERIFY(!QTestEventLoop::instance().timeout());
// We check if authenticationRequired was emitted, however we do not anything in it so it should be 401
- QCOMPARE(authSpy.count(), 1);
- QCOMPARE(finishedSpy.count(), 1);
- QCOMPARE(errorSpy.count(), 1);
+ QCOMPARE(authSpy.size(), 1);
+ QCOMPARE(finishedSpy.size(), 1);
+ QCOMPARE(errorSpy.size(), 1);
QCOMPARE(reply->error(), QNetworkReply::AuthenticationRequiredError);
}
@@ -7719,8 +8185,8 @@ void tst_QNetworkReply::qtbug45581WrongReplyStatusCode()
QCOMPARE(reply->readAll(), expectedContent);
- QCOMPARE(finishedSpy.count(), 0);
- QCOMPARE(sslErrorsSpy.count(), 0);
+ QCOMPARE(finishedSpy.size(), 0);
+ QCOMPARE(sslErrorsSpy.size(), 0);
QCOMPARE(reply->header(QNetworkRequest::ContentLengthHeader).toLongLong(), expectedContent.size());
@@ -7752,17 +8218,17 @@ void tst_QNetworkReply::synchronousRequest_data()
// ### we would need to enflate (un-deflate) the file content and compare the sizes
<< QString("text/plain");
-#ifndef QT_NO_SSL
-#if QT_CONFIG(securetransport)
- qWarning("Skipping https scheme, SecTrustEvalue() fails, update the certificate on server");
-#else
- QTest::newRow("https")
- << QUrl("https://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt")
- << QString("file:" + testDataDir + "/rfc3252.txt")
- << true
- << QString("text/plain");
-#endif
-#endif
+#if QT_CONFIG(ssl)
+ if (isSecureTransport) {
+ qWarning("Skipping https scheme, SecTrustEvalue() fails, update the certificate on server");
+ } else {
+ QTest::newRow("https")
+ << QUrl("https://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt")
+ << QString("file:" + testDataDir + "/rfc3252.txt")
+ << true
+ << QString("text/plain");
+ }
+#endif // QT_CONFIG(ssl)
QTest::newRow("data")
<< QUrl(QString::fromLatin1("data:text/plain,hello world"))
@@ -7787,7 +8253,7 @@ void tst_QNetworkReply::synchronousRequest()
QNetworkRequest request(url);
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
// workaround for HTTPS requests: add self-signed server cert to list of CA certs,
// since we cannot react to the sslErrors() signal
// to fix this properly we would need to have an ignoreSslErrors() method in the
@@ -7809,8 +8275,8 @@ void tst_QNetworkReply::synchronousRequest()
QSignalSpy sslErrorsSpy(&manager, SIGNAL(sslErrors(QNetworkReply*,QList<QSslError>)));
RUN_REQUEST(runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply, 0));
QVERIFY(reply->isFinished());
- QCOMPARE(finishedSpy.count(), 0);
- QCOMPARE(sslErrorsSpy.count(), 0);
+ QCOMPARE(finishedSpy.size(), 0);
+ QCOMPARE(sslErrorsSpy.size(), 0);
QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader).toString(), mimeType);
@@ -7819,7 +8285,7 @@ void tst_QNetworkReply::synchronousRequest()
if (expected.startsWith("file:")) {
QString path = expected.mid(5);
QFile file(path);
- file.open(QIODevice::ReadOnly);
+ QVERIFY(file.open(QIODevice::ReadOnly));
expectedContent = file.readAll();
} else if (expected.startsWith("data:")) {
expectedContent = expected.mid(5).toUtf8();
@@ -7832,7 +8298,7 @@ void tst_QNetworkReply::synchronousRequest()
reply->deleteLater();
}
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
void tst_QNetworkReply::synchronousRequestSslFailure()
{
// test that SSL won't be accepted with self-signed certificate,
@@ -7849,7 +8315,7 @@ void tst_QNetworkReply::synchronousRequestSslFailure()
runSimpleRequest(QNetworkAccessManager::GetOperation, request, reply, 0);
QVERIFY(reply->isFinished());
QCOMPARE(reply->error(), QNetworkReply::SslHandshakeFailedError);
- QCOMPARE(sslErrorsSpy.count(), 0);
+ QCOMPARE(sslErrorsSpy.size(), 0);
}
#endif
@@ -7911,6 +8377,218 @@ void tst_QNetworkReply::httpAbort()
QCOMPARE(reply3->error(), QNetworkReply::NoError);
}
+void tst_QNetworkReply::closeClientSideConnectionEagerlyQtbug20726()
+{
+ QNetworkAccessManager manager; // function local instance
+ // Setup HTTP servers
+ MiniHttpServer server("HTTP/1.1 200 OK\r\nContent-Length: 0\r\nConnection: Keep-Alive\r\n\r\n", false);
+ server.doClose = false; // server should not disconnect.
+
+ MiniHttpServer serverNotEagerClientClose("HTTP/1.1 200 OK\r\nContent-Length: 0\r\nConnection: Keep-Alive\r\n\r\n", false);
+ serverNotEagerClientClose.doClose = false; // server should not disconnect.
+ QUrl urlNotEager(QLatin1String("http://localhost"));
+ urlNotEager.setPort(serverNotEagerClientClose.serverPort());
+ QNetworkRequest requestNotEager(urlNotEager);
+ QNetworkReplyPtr replyNotEager(manager.get(requestNotEager));
+ QCOMPARE(waitForFinish(replyNotEager), Success);
+ // The reply was finished, the connection should be hanging and waiting to be expired
+
+ // Another server not eager to close, the connection should be hanging and waiting to be expired
+ MiniHttpServer serverNotEagerClientClose2("HTTP/1.1 200 OK\r\nContent-Length: 0\r\nConnection: Keep-Alive\r\n\r\n", false);
+ serverNotEagerClientClose2.doClose = false; // server should not disconnect.
+ QUrl urlNotEager2(QLatin1String("http://localhost"));
+ urlNotEager2.setPort(serverNotEagerClientClose2.serverPort());
+ QNetworkRequest requestNotEager2(urlNotEager2);
+ QNetworkReplyPtr replyNotEager2(manager.get(requestNotEager2));
+ QCOMPARE(waitForFinish(replyNotEager2), Success);
+
+ // However for this one we want to eagerly close.
+ QUrl url(QLatin1String("http://localhost"));
+ url.setPort(server.serverPort());
+ QNetworkRequest request(url);
+ request.setAttribute(QNetworkRequest::ConnectionCacheExpiryTimeoutSecondsAttribute, 0);
+ QNetworkReplyPtr reply(manager.get(request));
+ // The server just uses a normal TCP socket and prints out this error when the client disconnects:
+ QTest::ignoreMessage(QtDebugMsg,
+ "slotError QAbstractSocket::RemoteHostClosedError "
+ "\"The remote host closed the connection\"");
+ QCOMPARE(waitForFinish(reply), Success);
+ QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
+ // Socket from server to QNAM still connected?
+ QVERIFY (!server.client.isNull());
+ QVERIFY (server.client->state() == QTcpSocket::ConnectedState);
+ // Wait a bit
+ QTest::qWait(1*1000);
+ // The QNAM should have disconnected the socket, so on our server it's disconnected now.
+ QVERIFY (!server.client.isNull());
+ QVERIFY (server.client->state() != QTcpSocket::ConnectedState);
+
+ // Now we check the not eager reply, it should still be connected.
+ QCOMPARE(replyNotEager->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
+ QCOMPARE(serverNotEagerClientClose.client->state(), QTcpSocket::ConnectedState);
+ QCOMPARE(replyNotEager2->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
+ QCOMPARE(serverNotEagerClientClose2.client->state(), QTcpSocket::ConnectedState);
+}
+
+void tst_QNetworkReply::varyingCacheExpiry_data()
+{
+ QTest::addColumn<int>("firstExpiry");
+ QTest::addColumn<int>("secondExpiry");
+ QTest::addColumn<int>("thirdExpiry");
+ QTest::addColumn<int>("fourthExpiry");
+
+ // The datatags signify the Keep-Alive time-outs of the successive requests:
+ QTest::newRow("1-2-3-4") << 1 << 2 << 3 << 4;
+ QTest::newRow("4-1-2-3") << 4 << 1 << 2 << 3;
+ QTest::newRow("3-4-1-2") << 3 << 4 << 1 << 2;
+ QTest::newRow("2-3-4-1") << 2 << 3 << 4 << 1;
+ QTest::newRow("1-2-2-1") << 1 << 2 << 2 << 1;
+}
+
+// Test creating a few requests with various expiry timeouts.
+// We do this because the internal QNetworkAccessCache inserts them in sorted
+// order, so make sure it gets it right.
+void tst_QNetworkReply::varyingCacheExpiry()
+{
+ // Local QNAM instance because there may be leftover entries from other
+ // tests. Which wouldn't be a big deal, it would just get in the way of our
+ // pattern
+ QNetworkAccessManager qnam;
+ QFETCH(int, firstExpiry);
+ QFETCH(int, secondExpiry);
+ QFETCH(int, thirdExpiry);
+ QFETCH(int, fourthExpiry);
+
+ int expiryTimes[4] = {
+ firstExpiry,
+ secondExpiry,
+ thirdExpiry,
+ fourthExpiry,
+ };
+
+ // We need multiple servers because we want to have multiple connections
+ // in the QNetworkAccessCache, not to just reuse one connection from the
+ // cache.
+ MiniHttpServer servers[4] = {
+ { httpEmpty200Response },
+ { httpEmpty200Response },
+ { httpEmpty200Response },
+ { httpEmpty200Response },
+ };
+ for (MiniHttpServer &server : servers)
+ server.doClose = false;
+
+ QUrl urls[4] = {
+ u"http://localhost"_s,
+ u"http://localhost"_s,
+ u"http://localhost"_s,
+ u"http://localhost"_s,
+ };
+ for (size_t i = 0; i < std::size(urls); ++i)
+ urls[i].setPort(servers[i].serverPort());
+
+ // After the initial request is completed the connection is kept alive
+ // (Keep-Alive). Internally they are added to a sorted linked-list based on
+ // expiry. So, set the requests to be torn down at varying timeouts.
+
+ QNetworkRequest requests[4] = {
+ QNetworkRequest(urls[0]),
+ QNetworkRequest(urls[1]),
+ QNetworkRequest(urls[2]),
+ QNetworkRequest(urls[3]),
+ };
+
+ for (int i = 0; i < 4; ++i) {
+ requests[i].setAttribute(QNetworkRequest::Http2AllowedAttribute,
+ QVariant::fromValue(false));
+ requests[i].setAttribute(QNetworkRequest::ConnectionCacheExpiryTimeoutSecondsAttribute,
+ expiryTimes[i]);
+ }
+
+ // The server just uses a normal TCP socket and prints out this error when the client
+ // disconnects:
+ for (int i = 0; i < 4; ++i) {
+ QTest::ignoreMessage(QtDebugMsg,
+ "slotError QAbstractSocket::RemoteHostClosedError "
+ "\"The remote host closed the connection\"");
+ }
+
+ // Start each request and wait for it to finish before starting the next
+ // one, we must do this because the connections are only added to the expiry
+ // list once finished
+ for (const auto &request : requests) {
+ QNetworkReplyPtr reply(qnam.get(request));
+ QCOMPARE(waitForFinish(reply), Success);
+ }
+
+ int lastExpiry = *std::max_element(std::begin(expiryTimes), std::end(expiryTimes));
+ auto allServersDisconnected = [&servers]() {
+ auto socketDisconnected = [](const MiniHttpServer &s) {
+ return s.client->state() == QAbstractSocket::UnconnectedState;
+ };
+ return std::all_of(std::begin(servers), std::end(servers), socketDisconnected);
+ };
+ // At least +5 seconds due to CI. Completes much faster locally
+ bool success = QTest::qWaitFor(allServersDisconnected, lastExpiry * 1000 + 5000);
+
+ QVERIFY(success);
+}
+
+class Qtbug25280Server : public MiniHttpServer
+{
+public:
+ Qtbug25280Server(QByteArray qba) : MiniHttpServer(qba, false) {}
+ QSet<QTcpSocket*> receivedSockets;
+ void reply() override
+ {
+ // Save sockets in a list
+ receivedSockets.insert((QTcpSocket*)sender());
+ qobject_cast<QTcpSocket*>(sender())->write(dataToTransmit);
+ //qDebug() << "count=" << receivedSockets.count();
+ }
+};
+
+void tst_QNetworkReply::amountOfHttp1ConnectionsQtbug25280_data()
+{
+ QTest::addColumn<int>("amount");
+ QTest::addRow("default") << 6;
+ QTest::addRow("minimize") << 1;
+ QTest::addRow("increase") << 12;
+}
+
+// Also kind of QTBUG-8468
+void tst_QNetworkReply::amountOfHttp1ConnectionsQtbug25280()
+{
+ QFETCH(const int, amount);
+ QNetworkAccessManager manager; // function local instance
+ Qtbug25280Server server(tst_QNetworkReply::httpEmpty200Response);
+ server.doClose = false;
+ server.multiple = true;
+ QUrl url(QLatin1String("http://127.0.0.1")); // not "localhost" to prevent "Happy Eyeballs"
+ // from skewing the counting
+ url.setPort(server.serverPort());
+ std::optional<QHttp1Configuration> http1Configuration;
+ if (amount != 6) // don't set if it's the default
+ http1Configuration.emplace().setNumberOfConnectionsPerHost(amount);
+ constexpr int NumRequests = 200; // send a lot more than we have sockets
+ int finished = 0;
+ std::array<std::unique_ptr<QNetworkReply>, NumRequests> replies;
+ for (auto &reply : replies) {
+ QNetworkRequest request(url);
+ if (http1Configuration)
+ request.setHttp1Configuration(*http1Configuration);
+ reply.reset(manager.get(request));
+ QObject::connect(reply.get(), &QNetworkReply::finished,
+ [&finished] { ++finished; });
+ }
+ QTRY_COMPARE_WITH_TIMEOUT(finished, NumRequests, 60'000);
+ for (const auto &reply : replies) {
+ QCOMPARE(reply->error(), QNetworkReply::NoError);
+ QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
+ }
+ QCOMPARE(server.receivedSockets.size(), amount);
+}
+
void tst_QNetworkReply::dontInsertPartialContentIntoTheCache()
{
QByteArray reply206 =
@@ -7939,7 +8617,7 @@ void tst_QNetworkReply::dontInsertPartialContentIntoTheCache()
QVERIFY(server.totalConnections > 0);
QCOMPARE(reply->readAll().constData(), "load");
- QCOMPARE(memoryCache->m_insertedUrls.count(), 0);
+ QCOMPARE(memoryCache->m_insertedUrls.size(), 0);
}
void tst_QNetworkReply::httpUserAgent()
@@ -7956,7 +8634,7 @@ void tst_QNetworkReply::httpUserAgent()
QVERIFY(reply->isFinished());
QCOMPARE(reply->error(), QNetworkReply::NoError);
- QVERIFY(server.receivedData.contains("\r\nUser-Agent: abcDEFghi\r\n"));
+ QVERIFY(server.receivedData.contains("\r\nuser-agent: abcDEFghi\r\n"));
}
void tst_QNetworkReply::synchronousAuthenticationCache()
@@ -7976,7 +8654,7 @@ void tst_QNetworkReply::synchronousAuthenticationCache()
"Content-Type: text/plain\r\n"
"\r\n"
"auth";
- QRegularExpression rx("Authorization: Basic ([^\r\n]*)\r\n");
+ QRegularExpression rx("authorization: Basic ([^\r\n]*)\r\n");
QRegularExpressionMatch match = rx.match(receivedData);
if (match.hasMatch()) {
if (QByteArray::fromBase64(match.captured(1).toLatin1()) == "login:password") {
@@ -7997,7 +8675,7 @@ void tst_QNetworkReply::synchronousAuthenticationCache()
// the server thread, because the client is never returning to the
// event loop
QScopedPointer<QThread, QThreadCleanup> serverThread(new QThread);
- QScopedPointer<MiniHttpServer, QDeleteLaterCleanup> server(new MiniAuthServer(serverThread.data()));
+ QScopedPointer<MiniHttpServer, QScopedPointerDeleteLater> server(new MiniAuthServer(serverThread.data()));
server->doClose = true;
//1) URL without credentials, we are not authenticated
@@ -8136,31 +8814,33 @@ void tst_QNetworkReply::ftpAuthentication()
void tst_QNetworkReply::emitErrorForAllReplies() // QTBUG-36890
{
// port 100 is not well-known and should be closed
- QList<QUrl> urls = QList<QUrl>() << QUrl("http://localhost:100/request1")
- << QUrl("http://localhost:100/request2")
- << QUrl("http://localhost:100/request3");
- QList<QNetworkReply *> replies;
- QList<QSignalSpy *> errorSpies;
- QList<QSignalSpy *> finishedSpies;
- for (int a = 0; a < urls.count(); ++a) {
- QNetworkRequest request(urls.at(a));
+ const QUrl urls[] = {
+ QUrl("http://localhost:100/request1"),
+ QUrl("http://localhost:100/request2"),
+ QUrl("http://localhost:100/request3"),
+ };
+ constexpr auto NUrls = std::size(urls);
+
+ std::unique_ptr<QNetworkReply, QScopedPointerDeleteLater> replies[NUrls];
+ std::optional<QSignalSpy> errorSpies[NUrls];
+ std::optional<QSignalSpy> finishedSpies[NUrls];
+
+ for (size_t i = 0; i < NUrls; ++i) {
+ QNetworkRequest request(urls[i]);
QNetworkReply *reply = manager.get(request);
- replies.append(reply);
- QSignalSpy *errorSpy = new QSignalSpy(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
- errorSpies.append(errorSpy);
- QSignalSpy *finishedSpy = new QSignalSpy(reply, SIGNAL(finished()));
- finishedSpies.append(finishedSpy);
+ replies[i].reset(reply);
+ errorSpies[i].emplace(reply, SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
+ finishedSpies[i].emplace(reply, SIGNAL(finished()));
QObject::connect(reply, SIGNAL(finished()), SLOT(emitErrorForAllRepliesSlot()));
}
+
QTestEventLoop::instance().enterLoop(10);
QVERIFY(!QTestEventLoop::instance().timeout());
- for (int a = 0; a < urls.count(); ++a) {
- QVERIFY(replies.at(a)->isFinished());
- QCOMPARE(errorSpies.at(a)->count(), 1);
- errorSpies.at(a)->deleteLater();
- QCOMPARE(finishedSpies.at(a)->count(), 1);
- finishedSpies.at(a)->deleteLater();
- replies.at(a)->deleteLater();
+
+ for (size_t i = 0; i < NUrls; ++i) {
+ QVERIFY(replies[i]->isFinished());
+ QCOMPARE(errorSpies[i]->size(), 1);
+ QCOMPARE(finishedSpies[i]->size(), 1);
}
}
@@ -8208,7 +8888,7 @@ public:
return ret;
}
virtual bool atEnd() const override { return buffer.atEnd(); }
- virtual qint64 size() const override { return data.length(); }
+ virtual qint64 size() const override { return data.size(); }
qint64 bytesAvailable() const override
{
return buffer.bytesAvailable() + QIODevice::bytesAvailable();
@@ -8229,9 +8909,9 @@ protected slots:
void tst_QNetworkReply::putWithRateLimiting()
{
QFile reference(testDataDir + "/rfc3252.txt");
- reference.open(QIODevice::ReadOnly);
+ QVERIFY(reference.open(QIODevice::ReadOnly));
QByteArray data = reference.readAll();
- QVERIFY(data.length() > 0);
+ QVERIFY(data.size() > 0);
QUrl url = QUrl::fromUserInput("http://" + QtNetworkSettings::httpServerName()+ "/qtest/cgi-bin/echo.cgi?");
@@ -8246,7 +8926,7 @@ void tst_QNetworkReply::putWithRateLimiting()
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QByteArray uploadedData = reply->readAll();
- QCOMPARE(uploadedData.length(), data.length());
+ QCOMPARE(uploadedData.size(), data.size());
QCOMPARE(uploadedData, data);
}
@@ -8278,8 +8958,8 @@ void tst_QNetworkReply::ioHttpSingleRedirect()
QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
// Redirected and finished should be emitted exactly once
- QCOMPARE(redSpy.count(), 1);
- QCOMPARE(finSpy.count(), 1);
+ QCOMPARE(redSpy.size(), 1);
+ QCOMPARE(finSpy.size(), 1);
// Original URL should not be changed after redirect
QCOMPARE(request.url(), localhost);
@@ -8325,8 +9005,8 @@ void tst_QNetworkReply::ioHttpChangeMaxRedirects()
QCOMPARE(waitForFinish(reply), int(Failure));
- QCOMPARE(redSpy.count(), request.maximumRedirectsAllowed());
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(redSpy.size(), request.maximumRedirectsAllowed());
+ QCOMPARE(spy.size(), 1);
QCOMPARE(reply->error(), QNetworkReply::TooManyRedirectsError);
// Increase max redirects to allow successful completion
@@ -8337,7 +9017,7 @@ void tst_QNetworkReply::ioHttpChangeMaxRedirects()
QVERIFY2(waitForFinish(reply2) == Success, msgWaitForFinished(reply2));
- QCOMPARE(redSpy2.count(), 2);
+ QCOMPARE(redSpy2.size(), 2);
QCOMPARE(reply2->url(), server3Url);
QCOMPARE(reply2->error(), QNetworkReply::NoError);
QVERIFY(validateRedirectedResponseHeaders(reply2));
@@ -8470,8 +9150,8 @@ void tst_QNetworkReply::ioHttpRedirectPolicy()
QSignalSpy redirectSpy(reply.data(), SIGNAL(redirected(QUrl)));
QSignalSpy finishedSpy(reply.data(), SIGNAL(finished()));
QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
- QCOMPARE(finishedSpy.count(), 1);
- QCOMPARE(redirectSpy.count(), redirectCount);
+ QCOMPARE(finishedSpy.size(), 1);
+ QCOMPARE(redirectSpy.size(), redirectCount);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), statusCode);
QVERIFY(validateRedirectedResponseHeaders(reply) || statusCode != 200);
}
@@ -8554,7 +9234,7 @@ void tst_QNetworkReply::ioHttpRedirectPolicyErrors()
QSignalSpy spy(reply.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
QCOMPARE(waitForFinish(reply), int(Failure));
- QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.size(), 1);
QCOMPARE(reply->error(), expectedError);
}
@@ -8604,7 +9284,7 @@ void tst_QNetworkReply::ioHttpUserVerifiedRedirect()
QSignalSpy finishedSpy(reply.data(), SIGNAL(finished()));
waitForFinish(reply);
- QCOMPARE(finishedSpy.count(), 1);
+ QCOMPARE(finishedSpy.size(), 1);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), statusCode);
QVERIFY(validateRedirectedResponseHeaders(reply) || statusCode != 200);
}
@@ -8616,7 +9296,7 @@ void tst_QNetworkReply::ioHttpCookiesDuringRedirect()
const QString cookieHeader = QStringLiteral("Set-Cookie: hello=world; Path=/;\r\n");
QString redirect = tempRedirectReplyStr();
// Insert 'cookieHeader' before the final \r\n
- redirect.insert(redirect.length() - 2, cookieHeader);
+ redirect.insert(redirect.size() - 2, cookieHeader);
QUrl url("http://localhost/");
url.setPort(target.serverPort());
@@ -8633,7 +9313,7 @@ void tst_QNetworkReply::ioHttpCookiesDuringRedirect()
manager.setRedirectPolicy(oldRedirectPolicy);
QVERIFY(waitForFinish(reply) == Success);
- QVERIFY(target.receivedData.contains("\r\nCookie: hello=world\r\n"));
+ QVERIFY(target.receivedData.contains("\r\ncookie: hello=world\r\n"));
QVERIFY(validateRedirectedResponseHeaders(reply));
}
@@ -8676,6 +9356,64 @@ void tst_QNetworkReply::ioHttpRedirect()
QVERIFY(validateRedirectedResponseHeaders(reply));
}
+/*
+ Test that, if we load a redirect from cache, we don't treat the request to
+ the destination of the redirect as a redirect.
+
+ If it was treated as a redirect the finished() signal was never emitted!
+*/
+void tst_QNetworkReply::ioHttpRedirectWithCache()
+{
+ // Disallow caching the result so that the second request must also send the request
+ QByteArray http200ResponseNoCache = "HTTP/1.1 200 OK\r\n"
+ "Content-Type: text/plain\r\n"
+ "Cache-Control: no-cache\r\n"
+ "\r\nHello";
+
+ MiniHttpServer target(http200ResponseNoCache, false);
+ QUrl targetUrl("http://localhost/");
+ targetUrl.setPort(target.serverPort());
+
+ // A cache-able redirect reply
+ QString redirectReply = QStringLiteral("HTTP/1.1 308\r\n"
+ "Content-Type: text/plain\r\n"
+ "location: %1\r\n"
+ "Cache-Control: max-age=3600\r\n"
+ "\r\nYou're being redirected").arg(targetUrl.toString());
+ MiniHttpServer redirectServer(redirectReply.toLatin1(), false);
+ QUrl url("http://localhost/");
+ url.setPort(redirectServer.serverPort());
+
+ QTemporaryDir tempDir(QDir::tempPath() + "/tmp_cache_28035");
+ QVERIFY2(tempDir.isValid(), qPrintable(tempDir.errorString()));
+ tempDir.setAutoRemove(true);
+
+ QNetworkDiskCache *diskCache = new QNetworkDiskCache();
+ diskCache->setCacheDirectory(tempDir.path());
+ // Manager takes ownership of the cache:
+ manager.setCache(diskCache);
+ QCOMPARE(diskCache->cacheSize(), 0);
+
+ // Send the first request, we end up caching the redirect reply
+ QNetworkRequest request(url);
+ QNetworkReplyPtr reply(manager.get(request));
+
+ QCOMPARE(waitForFinish(reply), int(Success));
+ QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
+ QVERIFY(validateRedirectedResponseHeaders(reply));
+
+ QVERIFY(diskCache->cacheSize() != 0);
+
+ // Now for the second request, we will use the cache, and we test that the finished()
+ // signal is still emitted.
+ request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
+ reply.reset(manager.get(request));
+
+ QCOMPARE(waitForFinish(reply), int(Success));
+ QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
+ QVERIFY(validateRedirectedResponseHeaders(reply));
+}
+
void tst_QNetworkReply::ioHttpRedirectFromLocalToRemote()
{
QUrl targetUrl("http://" + QtNetworkSettings::httpServerName() + "/qtest/rfc3252.txt");
@@ -8821,8 +9559,8 @@ void tst_QNetworkReply::ioHttpRedirectMultipartPost()
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); // 200 OK
- QVERIFY(multiPart->boundary().count() > 20); // check that there is randomness after the "boundary_.oOo._" string
- QVERIFY(multiPart->boundary().count() < 70);
+ QVERIFY(multiPart->boundary().size() > 20); // check that there is randomness after the "boundary_.oOo._" string
+ QVERIFY(multiPart->boundary().size() < 70);
QByteArray replyData = reply->readAll();
expectedReplyData.prepend("content type: multipart/" + contentType + "; boundary=\"" + multiPart->boundary() + "\"\n");
@@ -8943,7 +9681,7 @@ void tst_QNetworkReply::ioHttpRedirectWithUploadDevice()
}
}
-#ifndef QT_NO_SSL
+#if QT_CONFIG(ssl)
class PutWithServerClosingConnectionImmediatelyHandler: public QObject
{
@@ -8985,9 +9723,9 @@ public slots:
//qDebug() << m_receivedData.left(m_receivedData.indexOf("\r\n\r\n"));
m_receivedData = m_receivedData.mid(m_receivedData.indexOf("\r\n\r\n")+4); // check only actual data
}
- if (m_receivedData.length() > 0 && !m_expectedData.startsWith(m_receivedData)) {
+ if (m_receivedData.size() > 0 && !m_expectedData.startsWith(m_receivedData)) {
// We had received some data but it is corrupt!
- qDebug() << "CORRUPT" << m_receivedData.count();
+ qDebug() << "CORRUPT" << m_receivedData.size();
#if 0 // Use this to track down the pattern of the corruption and conclude the source
QFile a("/tmp/corrupt");
@@ -9109,8 +9847,12 @@ void tst_QNetworkReply::autoDeleteRepliesAttribute_data()
{
QTest::addColumn<QUrl>("destination");
- QTest::newRow("http") << QUrl("http://QInvalidDomain.qt/test");
- QTest::newRow("https") << QUrl("https://QInvalidDomain.qt/test");
+ QUrl webServerUrl = QtNetworkSettings::httpServerIp().toString();
+ webServerUrl.setPath("/notfound");
+ webServerUrl.setScheme("http");
+ QTest::newRow("http") << webServerUrl;
+ webServerUrl.setScheme("https");
+ QTest::newRow("https") << webServerUrl;
if (ftpSupported)
QTest::newRow("ftp") << QUrl("ftp://QInvalidDomain.qt/test");
QTest::newRow("file") << QUrl("file:///thisfolderdoesn'texist/probably.txt");
@@ -9133,7 +9875,7 @@ void tst_QNetworkReply::autoDeleteRepliesAttribute()
QSignalSpy finishedSpy(reply, &QNetworkReply::finished);
QSignalSpy destroyedSpy(reply, &QObject::destroyed);
QVERIFY(finishedSpy.wait());
- QCOMPARE(destroyedSpy.count(), 0);
+ QCOMPARE(destroyedSpy.size(), 0);
QVERIFY(destroyedSpy.wait());
}
{
@@ -9144,7 +9886,7 @@ void tst_QNetworkReply::autoDeleteRepliesAttribute()
QSignalSpy finishedSpy(reply, &QNetworkReply::finished);
QSignalSpy destroyedSpy(reply, &QObject::destroyed);
QVERIFY(finishedSpy.wait());
- QCOMPARE(destroyedSpy.count(), 0);
+ QCOMPARE(destroyedSpy.size(), 0);
QVERIFY(destroyedSpy.wait());
}
// Now repeated, but without the attribute to make sure it does not get deleted automatically.
@@ -9158,10 +9900,10 @@ void tst_QNetworkReply::autoDeleteRepliesAttribute()
QSignalSpy finishedSpy(reply.data(), &QNetworkReply::finished);
QSignalSpy destroyedSpy(reply.data(), &QObject::destroyed);
QVERIFY(finishedSpy.wait());
- QCOMPARE(destroyedSpy.count(), 0);
+ QCOMPARE(destroyedSpy.size(), 0);
QCoreApplication::processEvents();
QCoreApplication::processEvents();
- QCOMPARE(destroyedSpy.count(), 0);
+ QCOMPARE(destroyedSpy.size(), 0);
}
{
// Post
@@ -9170,10 +9912,10 @@ void tst_QNetworkReply::autoDeleteRepliesAttribute()
QSignalSpy finishedSpy(reply.data(), &QNetworkReply::finished);
QSignalSpy destroyedSpy(reply.data(), &QObject::destroyed);
QVERIFY(finishedSpy.wait());
- QCOMPARE(destroyedSpy.count(), 0);
+ QCOMPARE(destroyedSpy.size(), 0);
QCoreApplication::processEvents();
QCoreApplication::processEvents();
- QCOMPARE(destroyedSpy.count(), 0);
+ QCOMPARE(destroyedSpy.size(), 0);
}
}
@@ -9194,7 +9936,7 @@ void tst_QNetworkReply::autoDeleteReplies()
QSignalSpy finishedSpy(reply, &QNetworkReply::finished);
QSignalSpy destroyedSpy(reply, &QObject::destroyed);
QVERIFY(finishedSpy.wait());
- QCOMPARE(destroyedSpy.count(), 0);
+ QCOMPARE(destroyedSpy.size(), 0);
QVERIFY(destroyedSpy.wait());
}
{
@@ -9204,7 +9946,7 @@ void tst_QNetworkReply::autoDeleteReplies()
QSignalSpy finishedSpy(reply, &QNetworkReply::finished);
QSignalSpy destroyedSpy(reply, &QObject::destroyed);
QVERIFY(finishedSpy.wait());
- QCOMPARE(destroyedSpy.count(), 0);
+ QCOMPARE(destroyedSpy.size(), 0);
QVERIFY(destroyedSpy.wait());
}
// Here we repeat the test, but override the auto-deletion in the QNetworkRequest
@@ -9219,10 +9961,10 @@ void tst_QNetworkReply::autoDeleteReplies()
QSignalSpy finishedSpy(reply.data(), &QNetworkReply::finished);
QSignalSpy destroyedSpy(reply.data(), &QObject::destroyed);
QVERIFY(finishedSpy.wait());
- QCOMPARE(destroyedSpy.count(), 0);
+ QCOMPARE(destroyedSpy.size(), 0);
QCoreApplication::processEvents();
QCoreApplication::processEvents();
- QCOMPARE(destroyedSpy.count(), 0);
+ QCOMPARE(destroyedSpy.size(), 0);
}
{
// Post
@@ -9232,10 +9974,10 @@ void tst_QNetworkReply::autoDeleteReplies()
QSignalSpy finishedSpy(reply.data(), &QNetworkReply::finished);
QSignalSpy destroyedSpy(reply.data(), &QObject::destroyed);
QVERIFY(finishedSpy.wait());
- QCOMPARE(destroyedSpy.count(), 0);
+ QCOMPARE(destroyedSpy.size(), 0);
QCoreApplication::processEvents();
QCoreApplication::processEvents();
- QCOMPARE(destroyedSpy.count(), 0);
+ QCOMPARE(destroyedSpy.size(), 0);
}
// Now we repeat the test with autoDeleteReplies set to false
cleanup.dismiss();
@@ -9247,10 +9989,10 @@ void tst_QNetworkReply::autoDeleteReplies()
QSignalSpy finishedSpy(reply.data(), &QNetworkReply::finished);
QSignalSpy destroyedSpy(reply.data(), &QObject::destroyed);
QVERIFY(finishedSpy.wait());
- QCOMPARE(destroyedSpy.count(), 0);
+ QCOMPARE(destroyedSpy.size(), 0);
QCoreApplication::processEvents();
QCoreApplication::processEvents();
- QCOMPARE(destroyedSpy.count(), 0);
+ QCOMPARE(destroyedSpy.size(), 0);
}
{
// Post
@@ -9259,89 +10001,138 @@ void tst_QNetworkReply::autoDeleteReplies()
QSignalSpy finishedSpy(reply.data(), &QNetworkReply::finished);
QSignalSpy destroyedSpy(reply.data(), &QObject::destroyed);
QVERIFY(finishedSpy.wait());
- QCOMPARE(destroyedSpy.count(), 0);
+ QCOMPARE(destroyedSpy.size(), 0);
QCoreApplication::processEvents();
QCoreApplication::processEvents();
- QCOMPARE(destroyedSpy.count(), 0);
+ QCOMPARE(destroyedSpy.size(), 0);
}
}
-void tst_QNetworkReply::getWithTimeout()
+void tst_QNetworkReply::requestWithTimeout_data()
{
- MiniHttpServer server(tst_QNetworkReply::httpEmpty200Response, false);
-
- QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
- QNetworkReplyPtr reply(manager.get(request));
- QSignalSpy spy(reply.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
-
- QCOMPARE(waitForFinish(reply), int(Success));
+ using Operation = QNetworkAccessManager::Operation;
+ QTest::addColumn<Operation>("method");
+ QTest::addColumn<int>("reqInt");
+ QTest::addColumn<std::chrono::milliseconds>("reqChrono");
+ QTest::addColumn<int>("mgrInt");
+ QTest::addColumn<std::chrono::milliseconds>("mgrChrono");
- QCOMPARE(spy.count(), 0);
- QVERIFY(reply->error() == QNetworkReply::NoError);
-
- request.setTransferTimeout(1000);
- server.stopTransfer = true;
-
- QNetworkReplyPtr reply2(manager.get(request));
- QSignalSpy spy2(reply2.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
+ QTest::addRow("get_req_int") << Operation::GetOperation << 500 << 0ms << 0 << 0ms;
+ QTest::addRow("get_req_chrono") << Operation::GetOperation << 0 << 500ms << 0 << 0ms;
+ QTest::addRow("get_mgr_int") << Operation::GetOperation << 0 << 0ms << 500 << 0ms;
+ QTest::addRow("get_mgr_chrono") << Operation::GetOperation << 0 << 0ms << 0 << 500ms;
- QCOMPARE(waitForFinish(reply2), int(Failure));
-
- QCOMPARE(spy2.count(), 1);
- QVERIFY(reply2->error() == QNetworkReply::OperationCanceledError);
-
- request.setTransferTimeout(0);
- manager.setTransferTimeout(1000);
-
- QNetworkReplyPtr reply3(manager.get(request));
- QSignalSpy spy3(reply3.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
-
- QCOMPARE(waitForFinish(reply3), int(Failure));
-
- QCOMPARE(spy3.count(), 1);
- QVERIFY(reply3->error() == QNetworkReply::OperationCanceledError);
-
- manager.setTransferTimeout(0);
+ QTest::addRow("post_req_int") << Operation::PostOperation << 500 << 0ms << 0 << 0ms;
+ QTest::addRow("post_req_chrono") << Operation::PostOperation << 0 << 500ms << 0 << 0ms;
+ QTest::addRow("post_mgr_int") << Operation::PostOperation << 0 << 0ms << 500 << 0ms;
+ QTest::addRow("post_mgr_chrono") << Operation::PostOperation << 0 << 0ms << 0 << 500ms;
}
-void tst_QNetworkReply::postWithTimeout()
+void tst_QNetworkReply::requestWithTimeout()
{
+ QFETCH(QNetworkAccessManager::Operation, method);
+ QFETCH(int, reqInt);
+ QFETCH(int, mgrInt);
+ QFETCH(std::chrono::milliseconds, reqChrono);
+ QFETCH(std::chrono::milliseconds, mgrChrono);
+ const auto data = "some data"_ba;
+ // Manager instance remains between case runs => always reset it's transferTimeout to
+ // ensure setting its transferTimeout in this case has effect
+ manager.setTransferTimeout(0ms);
+ auto cleanup = qScopeGuard([this] { manager.setTransferTimeout(0ms); });
+
MiniHttpServer server(tst_QNetworkReply::httpEmpty200Response, false);
+ server.stopTransfer = true;
QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
request.setRawHeader("Content-Type", "application/octet-stream");
- QByteArray postData("Just some nonsense");
- QNetworkReplyPtr reply(manager.post(request, postData));
- QSignalSpy spy(reply.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
-
- QCOMPARE(waitForFinish(reply), int(Success));
-
- QCOMPARE(spy.count(), 0);
- QVERIFY(reply->error() == QNetworkReply::NoError);
-
- request.setTransferTimeout(1000);
- server.stopTransfer = true;
-
- QNetworkReplyPtr reply2(manager.post(request, postData));
- QSignalSpy spy2(reply2.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
-
- QCOMPARE(waitForFinish(reply2), int(Failure));
-
- QCOMPARE(spy2.count(), 1);
- QVERIFY(reply2->error() == QNetworkReply::OperationCanceledError);
+ if (reqInt > 0)
+ request.setTransferTimeout(reqInt);
+ if (reqChrono > 0ms)
+ request.setTransferTimeout(reqChrono);
+ if (mgrInt > 0)
+ manager.setTransferTimeout(mgrInt);
+ if (mgrChrono > 0ms)
+ manager.setTransferTimeout(mgrChrono);
- request.setTransferTimeout(0);
- manager.setTransferTimeout(1000);
-
- QNetworkReplyPtr reply3(manager.post(request, postData));
- QSignalSpy spy3(reply3.data(), SIGNAL(errorOccurred(QNetworkReply::NetworkError)));
+ QNetworkReplyPtr reply;
+ if (method == QNetworkAccessManager::GetOperation)
+ reply.reset(manager.get(request));
+ else if (method == QNetworkAccessManager::PostOperation)
+ reply.reset(manager.post(request, data));
+ QVERIFY(reply);
- QCOMPARE(waitForFinish(reply3), int(Failure));
+ QSignalSpy spy(reply.data(), &QNetworkReply::errorOccurred);
+ QCOMPARE(waitForFinish(reply), int(Failure));
+ QCOMPARE(spy.size(), 1);
+ QCOMPARE(reply->error(), QNetworkReply::OperationCanceledError);
+}
- QCOMPARE(spy3.count(), 1);
- QVERIFY(reply3->error() == QNetworkReply::OperationCanceledError);
+void tst_QNetworkReply::moreActivitySignals_data()
+{
+ QTest::addColumn<QUrl>("url");
+ QTest::addColumn<bool>("useipv6");
+ QTest::addColumn<bool>("postWithData");
+ QTest::addRow("local4") << QUrl("http://127.0.0.1") << false << false;
+ QTest::addRow("local6") << QUrl("http://[::1]") << true << false;
+ if (qEnvironmentVariable("QTEST_ENVIRONMENT").split(' ').contains("ci")) {
+ // On CI server
+ QTest::addRow("localDns") << QUrl("http://localhost") << false << false; // will find v6
+ } else {
+ // For manual testing
+ QTest::addRow("localDns4") << QUrl("http://localhost") << true << false; // will find both v4 and v6
+ QTest::addRow("localDns6") << QUrl("http://localhost") << false << false; // will find both v4 and v6
+ }
+ QTest::addRow("post-with-data") << QUrl("http://[::1]") << true << true;
+}
- manager.setTransferTimeout(0);
+void tst_QNetworkReply::moreActivitySignals()
+{
+ QFETCH(QUrl, url);
+ QFETCH(bool, useipv6);
+ QFETCH(bool, postWithData);
+ MiniHttpServer server(tst_QNetworkReply::httpEmpty200Response, false, nullptr/*thread*/, useipv6);
+ server.doClose = false;
+ url.setPort(server.serverPort());
+ QNetworkRequest request(url);
+ QNetworkReplyPtr reply;
+ if (postWithData) {
+ request.setRawHeader("Content-Type", "text/plain");
+ reply.reset(manager.post(request, "Hello, world!"));
+ } else {
+ reply.reset(manager.get(request));
+ }
+ QSignalSpy spy1(reply.data(), SIGNAL(socketStartedConnecting()));
+ QSignalSpy spy2(reply.data(), SIGNAL(requestSent()));
+ QSignalSpy spy3(reply.data(), SIGNAL(metaDataChanged()));
+ QSignalSpy spy4(reply.data(), SIGNAL(finished()));
+ spy1.wait();
+ QCOMPARE(spy1.size(), 1);
+ spy2.wait();
+ QCOMPARE(spy2.size(), 1);
+ spy3.wait();
+ QCOMPARE(spy3.size(), 1);
+ spy4.wait();
+ QCOMPARE(spy4.size(), 1);
+ QVERIFY(reply->error() == QNetworkReply::NoError);
+ // Second request will not send socketStartedConnecting because of keep-alive, so don't check it.
+ QNetworkReplyPtr secondreply;
+ if (postWithData) {
+ request.setRawHeader("Content-Type", "text/plain");
+ secondreply.reset(manager.post(request, "Hello, world!"));
+ } else {
+ secondreply.reset(manager.get(request));
+ }
+ QSignalSpy secondspy2(secondreply.data(), SIGNAL(requestSent()));
+ QSignalSpy secondspy3(secondreply.data(), SIGNAL(metaDataChanged()));
+ QSignalSpy secondspy4(secondreply.data(), SIGNAL(finished()));
+ secondspy2.wait();
+ QCOMPARE(secondspy2.size(), 1);
+ secondspy3.wait();
+ QCOMPARE(secondspy3.size(), 1);
+ secondspy4.wait();
+ QCOMPARE(secondspy4.size(), 1);
+ QVERIFY(secondreply->error() == QNetworkReply::NoError);
}
void tst_QNetworkReply::contentEncoding_data()
@@ -9349,25 +10140,53 @@ void tst_QNetworkReply::contentEncoding_data()
QTest::addColumn<QByteArray>("encoding");
QTest::addColumn<QByteArray>("body");
QTest::addColumn<QByteArray>("expected");
+ QTest::addColumn<bool>("decompress");
+
+ const QByteArray helloWorld = "hello world";
+ const QByteArray gzipBody = QByteArray::fromBase64("H4sIAAAAAAAAA8tIzcnJVyjPL8pJAQCFEUoNCwAAAA==");
QTest::newRow("gzip-hello-world")
<< QByteArray("gzip")
- << QByteArray::fromBase64("H4sIAAAAAAAAA8tIzcnJVyjPL8pJAQCFEUoNCwAAAA==")
- << QByteArray("hello world");
+ << gzipBody
+ << helloWorld
+ << true;
+ QTest::newRow("gzip-hello-world-no-decompress")
+ << QByteArray("gzip")
+ << gzipBody
+ << helloWorld
+ << false;
+ const QByteArray deflateBody = QByteArray::fromBase64("eJzLSM3JyVcozy/KSQEAGgsEXQ==");
QTest::newRow("deflate-hello-world")
- << QByteArray("deflate") << QByteArray::fromBase64("eJzLSM3JyVcozy/KSQEAGgsEXQ==")
- << QByteArray("hello world");
+ << QByteArray("deflate") << deflateBody
+ << helloWorld
+ << true;
+ QTest::newRow("deflate-hello-world-no-decompress")
+ << QByteArray("deflate") << deflateBody
+ << helloWorld
+ << false;
#if QT_CONFIG(brotli)
+ const QByteArray brotliBody = QByteArray::fromBase64("DwWAaGVsbG8gd29ybGQD");
QTest::newRow("brotli-hello-world")
- << QByteArray("br") << QByteArray::fromBase64("DwWAaGVsbG8gd29ybGQD")
- << QByteArray("hello world");
+ << QByteArray("br") << brotliBody
+ << helloWorld
+ << true;
+ QTest::newRow("brotli-hello-world-no-decompress")
+ << QByteArray("br") << brotliBody
+ << helloWorld
+ << false;
#endif
#if defined(QT_BUILD_INTERNAL) && QT_CONFIG(zstd)
+ const QByteArray zstdBody = QByteArray::fromBase64("KLUv/QRYWQAAaGVsbG8gd29ybGRoaR6y");
QTest::newRow("zstandard-hello-world")
- << QByteArray("zstd") << QByteArray::fromBase64("KLUv/QRYWQAAaGVsbG8gd29ybGRoaR6y")
- << QByteArray("hello world");
+ << QByteArray("zstd") << zstdBody
+ << helloWorld
+ << true;
+ QTest::newRow("zstandard-hello-world-no-decompress")
+ << QByteArray("zstd") << zstdBody
+ << helloWorld
+ << false;
#else
qDebug("Note: ZStandard testdata is only available for developer builds.");
#endif
@@ -9377,12 +10196,19 @@ void tst_QNetworkReply::contentEncoding()
{
QFETCH(QByteArray, encoding);
QFETCH(QByteArray, body);
+ QFETCH(bool, decompress);
QString header("HTTP/1.0 200 OK\r\nContent-Encoding: %1\r\nContent-Length: %2\r\n\r\n");
header = header.arg(encoding, QString::number(body.size()));
MiniHttpServer server(header.toLatin1() + body);
QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
+ if (!decompress) {
+ // This disables decompression of the received content:
+ request.setRawHeader("accept-encoding", QLatin1String("%1").arg(encoding).toLatin1());
+ // This disables the zerocopy optimization
+ request.setAttribute(QNetworkRequest::MaximumDownloadBufferSizeAttribute, 0);
+ }
QNetworkReplyPtr reply(manager.get(request));
QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
@@ -9391,7 +10217,7 @@ void tst_QNetworkReply::contentEncoding()
{
// Check that we included the content encoding method in our Accept-Encoding header
const QByteArray &receivedData = server.receivedData;
- int start = receivedData.indexOf("Accept-Encoding");
+ int start = receivedData.indexOf("accept-encoding");
QVERIFY(start != -1);
int end = receivedData.indexOf("\r\n", start);
QVERIFY(end != -1);
@@ -9403,12 +10229,338 @@ void tst_QNetworkReply::contentEncoding()
QVERIFY2(list.contains(encoding), acceptedEncoding.data());
}
+ if (decompress) {
+ QFETCH(QByteArray, expected);
+ QCOMPARE(reply->bytesAvailable(), expected.size());
+ QCOMPARE(reply->readAll(), expected);
+ } else {
+ QCOMPARE(reply->bytesAvailable(), body.size());
+ QCOMPARE(reply->readAll(), body);
+ }
+}
+
+void tst_QNetworkReply::contentEncodingBigPayload_data()
+{
+ QTest::addColumn<QByteArray>("encoding");
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<qint64>("expectedSize");
+
+ qint64 fourGiB = 4ll * 1024ll * 1024ll * 1024ll;
+
+ QTest::addRow("gzip-4GB") << QByteArray("gzip") << (":/4G.gz") << fourGiB;
+
+#if QT_CONFIG(brotli)
+ QTest::addRow("brotli-4GB") << QByteArray("br") << (testDataDir + "/4G.br") << fourGiB;
+#endif
+#if defined(QT_BUILD_INTERNAL) && QT_CONFIG(zstd)
+ QTest::addRow("zstd-4GB") << QByteArray("zstd") << (":/4G.zst") << fourGiB;
+#else
+ qDebug("Note: ZStandard testdata is only available for developer builds.");
+#endif
+}
+
+void tst_QNetworkReply::contentEncodingBigPayload()
+{
+ QFETCH(QString, path);
+ QFile compressedFile(path);
+ QVERIFY(compressedFile.open(QIODevice::ReadOnly));
+ QByteArray body = compressedFile.readAll();
+
+ QFETCH(QByteArray, encoding);
+ QString header("HTTP/1.0 200 OK\r\nContent-Encoding: %1\r\nContent-Length: %2\r\n\r\n");
+ header = header.arg(encoding, QString::number(body.size()));
+
+ MiniHttpServer server(header.toLatin1() + body);
+
+ QNetworkRequest request(QUrl("http://localhost:" + QString::number(server.serverPort())));
+ // QDecompressHelper will abort the download if the compressed to decompressed size ratio
+ // differs too much, so we override it
+ request.setDecompressedSafetyCheckThreshold(-1);
+ QNetworkReplyPtr reply(manager.get(request));
+
+ QTRY_VERIFY2_WITH_TIMEOUT(reply->isFinished(), qPrintable(reply->errorString()), 15000);
+ QCOMPARE(reply->error(), QNetworkReply::NoError);
+
+ QFETCH(qint64, expectedSize);
+
+ QCOMPARE(reply->bytesAvailable(), expectedSize);
+ QByteArray output(512 * 1024 * 1024, Qt::Uninitialized);
+ qint64 total = 0;
+ while (reply->bytesAvailable()) {
+ qint64 read = reply->read(output.data(), output.size());
+ QVERIFY(read != -1);
+ total += read;
+
+ static const auto isZero = [](char c) { return c == '\0'; };
+ bool allZero = std::all_of(output.cbegin(), output.cbegin() + read, isZero);
+ QVERIFY(allZero);
+ }
+ QCOMPARE(total, expectedSize);
+}
+
+void tst_QNetworkReply::cacheWithContentEncoding_data()
+{
+ contentEncoding_data();
+}
+
+void tst_QNetworkReply::cacheWithContentEncoding()
+{
+ QFETCH(QByteArray, encoding);
+ QFETCH(QByteArray, body);
QFETCH(QByteArray, expected);
+ QString header("HTTP/1.0 200 OK\r\nContent-Encoding: %1\r\nContent-Length: %2\r\n\r\n");
+ header = header.arg(encoding, QString::number(body.size()));
+
+ MiniHttpServer server(header.toLatin1() + body);
- QCOMPARE(reply->bytesAvailable(), expected.size());
+ MySpyMemoryCache *cache = new MySpyMemoryCache(this);
+ manager.setCache(cache);
+ auto unsetCache = qScopeGuard([this](){ manager.setCache(nullptr); });
+
+ QUrl url("http://localhost:" + QString::number(server.serverPort()));
+ QNetworkRequest request(url);
+ QNetworkReplyPtr reply(manager.get(request));
+
+ QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
+ QCOMPARE(reply->error(), QNetworkReply::NoError);
+
+ QByteArray output = reply->readAll();
+ QIODevice *device = cache->data(url);
+ QVERIFY(device);
+ QByteArray fromCache = device->readAll();
+ QCOMPARE(output, expected);
+ QCOMPARE(fromCache, expected);
+}
+
+void tst_QNetworkReply::downloadProgressWithContentEncoding_data()
+{
+ contentEncoding_data();
+}
+
+void tst_QNetworkReply::downloadProgressWithContentEncoding()
+{
+ QFETCH(QByteArray, encoding);
+ QFETCH(QByteArray, body);
+ QFETCH(QByteArray, expected);
+ QString header("HTTP/1.0 200 OK\r\nContent-Encoding: %1\r\nContent-Length: %2\r\n\r\n");
+ header = header.arg(encoding, QString::number(body.size()));
+
+ MiniHttpServer server(header.toLatin1() + body);
+
+ QUrl url("http://localhost:" + QString::number(server.serverPort()));
+ QNetworkRequest request(url);
+ QNetworkReplyPtr reply(manager.get(request));
+ // Limit the amount of bytes we read so we get more than one downloadProgress emission
+ reply->setReadBufferSize(5);
+
+ qint64 bytesReceived = -1;
+ connect(reply.data(), &QNetworkReply::downloadProgress, this,
+ [reply = reply.data(), &expected, &bytesReceived](qint64 recv, qint64 /*total*/) {
+ qint64 previous = bytesReceived;
+ bytesReceived = recv;
+ if (bytesReceived > expected.size()) {
+ qWarning("bytesReceived greater than expected size!");
+ reply->abort();
+ }
+ if (bytesReceived < previous) {
+ qWarning("bytesReceived shrank!");
+ reply->abort();
+ }
+ });
+
+ SlowReader reader(reply.data());
+
+ QVERIFY2(waitForFinish(reply) == Success, msgWaitForFinished(reply));
+ QCOMPARE(reply->error(), QNetworkReply::NoError);
+ QCOMPARE(bytesReceived, expected.size());
+}
+
+void tst_QNetworkReply::contentEncodingError_data()
+{
+ QTest::addColumn<QByteArray>("encoding");
+ QTest::addColumn<QString>("path");
+ QTest::addColumn<QNetworkReply::NetworkError>("expectedError");
+
+ QTest::addRow("archive-bomb") << QByteArray("gzip") << (":/4G.gz")
+ << QNetworkReply::UnknownContentError;
+}
+
+void tst_QNetworkReply::contentEncodingError()
+{
+ QFETCH(QString, path);
+ QFile compressedFile(path);
+ QVERIFY(compressedFile.open(QIODevice::ReadOnly));
+ QByteArray body = compressedFile.readAll();
+
+ QFETCH(QByteArray, encoding);
+ QString header("HTTP/1.0 200 OK\r\nContent-Encoding: %1\r\nContent-Length: %2\r\n\r\n");
+ header = header.arg(encoding, QString::number(body.size()));
+
+ MiniHttpServer server(header.toLatin1() + body);
+
+ QNetworkRequest request(
+ QUrl(QLatin1String("http://localhost:%1").arg(QString::number(server.serverPort()))));
+ QNetworkReplyPtr reply(manager.get(request));
+
+ QTRY_VERIFY2_WITH_TIMEOUT(reply->isFinished(), qPrintable(reply->errorString()), 15000);
+ QTEST(reply->error(), "expectedError");
+}
+
+// When this test is failing it will appear flaky because it relies on the
+// timing of delivery from one socket to another in the OS.
+// + we have to send all the data at once, so the readyRead emissions are
+// compressed into a single emission, so we cannot artificially time it with
+// waits and sleeps.
+void tst_QNetworkReply::compressedReadyRead()
+{
+ // There were historically an issue where a mix of signal compression and
+ // data decompression made it so we accidentally didn't emit the final
+ // readyRead signal before emitting finished(). Test this here to make sure
+ // it happens:
+ const QByteArray gzipPayload =
+ QByteArray::fromBase64("H4sIAAAAAAAAA8tIzcnJVyjPL8pJAQCFEUoNCwAAAA==");
+ const QByteArray expected = "hello world";
+
+ QString header("HTTP/1.0 200 OK\r\nContent-Encoding: gzip\r\nContent-Length: %1\r\n\r\n");
+ header = header.arg(gzipPayload.size());
+ MiniHttpServer server(header.toLatin1()); // only send header automatically
+ server.doClose = false; // don't close and delete client socket right away
+
+ QNetworkRequest request(
+ QUrl(QLatin1String("http://localhost:%1").arg(QString::number(server.serverPort()))));
+ QNetworkReplyPtr reply(manager.get(request));
+
+ QObject::connect(reply.get(), &QNetworkReply::metaDataChanged, reply.get(),
+ [&server, &gzipPayload]() {
+ // Client received headers, now send data:
+ // We do this awkward write,flush,write dance to try to
+ // make sure the data does not all arrive at the same
+ // time. By design we send the final "=" byte by itself
+ qsizetype boundary = gzipPayload.size() - 1;
+ server.client->write(gzipPayload.sliced(0, boundary));
+ server.client->flush();
+ // Let the server take care of deleting the client once
+ // the rest of the data is written:
+ server.doClose = true;
+ server.client->write(gzipPayload.sliced(boundary));
+ });
+
+ QByteArray received;
+ QObject::connect(reply.get(), &QNetworkReply::readyRead, reply.get(),
+ [reply = reply.get(), &received]() {
+ received += reply->readAll();
+ });
+ QTRY_VERIFY(reply->isFinished());
+ QCOMPARE(received, expected);
+}
+
+void tst_QNetworkReply::notFoundWithCompression_data()
+{
+ contentEncoding_data();
+}
+
+void tst_QNetworkReply::notFoundWithCompression()
+{
+ QFETCH(QByteArray, encoding);
+ QFETCH(QByteArray, body);
+ QString header("HTTP/1.0 404 OK\r\nContent-Encoding: %1\r\nContent-Length: %2\r\n\r\n");
+ header = header.arg(encoding, QString::number(body.size()));
+
+ MiniHttpServer server(header.toLatin1() + body);
+
+ QNetworkRequest request(
+ QUrl(QLatin1String("http://localhost:%1").arg(QString::number(server.serverPort()))));
+ QNetworkReplyPtr reply(manager.get(request));
+
+ QTRY_VERIFY2_WITH_TIMEOUT(reply->isFinished(), qPrintable(reply->errorString()), 15000);
+ QCOMPARE(reply->error(), QNetworkReply::ContentNotFoundError);
+
+ QFETCH(QByteArray, expected);
QCOMPARE(reply->readAll(), expected);
}
+void tst_QNetworkReply::qtbug68821proxyError_data()
+{
+ QTest::addColumn<QString>("proxyHost");
+ QTest::addColumn<QString>("scheme");
+ QTest::addColumn<QNetworkReply::NetworkError>("error");
+
+ QTest::newRow("invalidhost+http") << "this-host-will-never-exist.qt-project.org"
+ << "http" << QNetworkReply::ProxyNotFoundError;
+ QTest::newRow("localhost+http") << "localhost"
+ << "http" << QNetworkReply::ProxyConnectionRefusedError;
+#ifndef QT_NO_SSL
+ QTest::newRow("invalidhost+https") << "this-host-will-never-exist.qt-project.org"
+ << "https" << QNetworkReply::ProxyNotFoundError;
+ QTest::newRow("localhost+https") << "localhost"
+ << "https" << QNetworkReply::ProxyConnectionRefusedError;
+#endif
+}
+
+void tst_QNetworkReply::qtbug68821proxyError()
+{
+ auto getUnusedPort = []() -> std::optional<quint16> {
+ QTcpServer probeServer;
+ if (!probeServer.listen())
+ return std::nullopt;
+ // If we can listen on it, it was unused, and hopefully is also
+ // still unused after we stop listening.
+ return probeServer.serverPort();
+ };
+
+ auto proxyPort = getUnusedPort();
+ QVERIFY(proxyPort);
+
+ QFETCH(QString, proxyHost);
+ QNetworkProxy proxy(QNetworkProxy::HttpProxy, proxyHost, proxyPort.value());
+
+ manager.setProxy(proxy);
+
+ QFETCH(QString, scheme);
+ QNetworkReply *reply = manager.get(QNetworkRequest(QUrl(scheme + "://example.com")));
+ QSignalSpy spy(reply, &QNetworkReply::errorOccurred);
+ QVERIFY(spy.isValid());
+
+ QVERIFY(spy.wait(15000));
+
+ QFETCH(QNetworkReply::NetworkError, error);
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spy.at(0).at(0), error);
+}
+
+void tst_QNetworkReply::abortAndError()
+{
+ const QByteArray response =
+ R"(HTTP/1.0 500 Internal Server Error
+Content-Length: 12
+Content-Type: text/plain
+
+Hello World!)"_ba;
+
+ MiniHttpServer server(response);
+
+ QNetworkAccessManager manager;
+ QNetworkRequest req(QUrl("http://127.0.0.1:" + QString::number(server.serverPort())));
+ std::unique_ptr<QNetworkReply> reply(manager.post(req, "my data goes here"_ba));
+ QSignalSpy errorSignal(reply.get(), &QNetworkReply::errorOccurred);
+ QSignalSpy finishedSignal(reply.get(), &QNetworkReply::finished);
+
+ reply->abort();
+
+ // We don't want to print this warning in this case because it is impossible
+ // for users to avoid it.
+ QTest::failOnWarning("QNetworkReplyImplPrivate::error: Internal problem, this method must only "
+ "be called once.");
+ // Process any signals from the http thread:
+ QTest::qWait(1s);
+ if (QTest::currentTestFailed())
+ return;
+
+ QCOMPARE(finishedSignal.count(), 1);
+ QCOMPARE(errorSignal.count(), 1);
+ QCOMPARE(reply->error(), QNetworkReply::OperationCanceledError);
+}
+
// NOTE: This test must be last testcase in tst_qnetworkreply!
void tst_QNetworkReply::parentingRepliesToTheApp()
{
diff --git a/tests/auto/network/access/qnetworkrequest/CMakeLists.txt b/tests/auto/network/access/qnetworkrequest/CMakeLists.txt
index 1a98449549..2c4a7dd7ca 100644
--- a/tests/auto/network/access/qnetworkrequest/CMakeLists.txt
+++ b/tests/auto/network/access/qnetworkrequest/CMakeLists.txt
@@ -1,12 +1,19 @@
-# Generated from qnetworkrequest.pro.
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qnetworkrequest Test:
#####################################################################
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qnetworkrequest LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
qt_internal_add_test(tst_qnetworkrequest
SOURCES
tst_qnetworkrequest.cpp
- PUBLIC_LIBRARIES
+ LIBRARIES
Qt::Network
)
diff --git a/tests/auto/network/access/qnetworkrequest/tst_qnetworkrequest.cpp b/tests/auto/network/access/qnetworkrequest/tst_qnetworkrequest.cpp
index d90289ea08..bdef1115dd 100644
--- a/tests/auto/network/access/qnetworkrequest/tst_qnetworkrequest.cpp
+++ b/tests/auto/network/access/qnetworkrequest/tst_qnetworkrequest.cpp
@@ -1,37 +1,15 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the test suite of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QTest>
-#include <QtCore/QUrl>
+
#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
@@ -188,11 +166,11 @@ void tst_QNetworkRequest::rawHeaderList_data()
void tst_QNetworkRequest::rawHeaderList()
{
- QFETCH(QList<QByteArray>, set);
+ QFETCH(const QList<QByteArray>, set);
QFETCH(QList<QByteArray>, expected);
QNetworkRequest request;
- foreach (QByteArray header, set)
+ for (const QByteArray &header : set)
request.setRawHeader(header, "a value");
QList<QByteArray> got = request.rawHeaderList();
@@ -236,12 +214,30 @@ void tst_QNetworkRequest::setHeader_data()
<< QVariant(QDate(2007, 11, 01))
<< true << "Last-Modified"
<< "Thu, 01 Nov 2007 00:00:00 GMT";
- QTest::newRow("Last-Modified-DateTime") << QNetworkRequest::LastModifiedHeader
- << QVariant(QDateTime(QDate(2007, 11, 01),
- QTime(18, 8, 30),
- Qt::UTC))
- << true << "Last-Modified"
- << "Thu, 01 Nov 2007 18:08:30 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))
@@ -250,7 +246,7 @@ void tst_QNetworkRequest::setHeader_data()
QTest::newRow("If-Modified-Since-DateTime") << QNetworkRequest::IfModifiedSinceHeader
<< QVariant(QDateTime(QDate(2017, 7, 01),
QTime(3, 14, 15),
- Qt::UTC))
+ QTimeZone::UTC))
<< true << "If-Modified-Since"
<< "Sat, 01 Jul 2017 03:14:15 GMT";
@@ -358,38 +354,38 @@ void tst_QNetworkRequest::rawHeaderParsing_data()
QTest::newRow("Last-Modified-RFC1123") << QNetworkRequest::LastModifiedHeader
<< QVariant(QDateTime(QDate(1994, 11, 06),
QTime(8, 49, 37),
- Qt::UTC))
+ 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),
- Qt::UTC))
+ 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),
- Qt::UTC))
+ 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),
- Qt::UTC))
+ 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),
- Qt::UTC))
+ 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),
- Qt::UTC))
+ QTimeZone::UTC))
<< true << "If-Modified-Since"
<< "Sun Aug 6 08:49:37 1994";
diff --git a/tests/auto/network/access/qnetworkrequestfactory/CMakeLists.txt b/tests/auto/network/access/qnetworkrequestfactory/CMakeLists.txt
new file mode 100644
index 0000000000..d2112de58f
--- /dev/null
+++ b/tests/auto/network/access/qnetworkrequestfactory/CMakeLists.txt
@@ -0,0 +1,17 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qnetworkrequestfactory LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qnetworkrequestfactory
+ SOURCES
+ tst_qnetworkrequestfactory.cpp
+ LIBRARIES
+ Qt::Core
+ Qt::Test
+ Qt::Network
+)
diff --git a/tests/auto/network/access/qnetworkrequestfactory/tst_qnetworkrequestfactory.cpp b/tests/auto/network/access/qnetworkrequestfactory/tst_qnetworkrequestfactory.cpp
new file mode 100644
index 0000000000..d04a7ff3ec
--- /dev/null
+++ b/tests/auto/network/access/qnetworkrequestfactory/tst_qnetworkrequestfactory.cpp
@@ -0,0 +1,423 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest/qtest.h>
+#include <QtNetwork/qnetworkrequestfactory.h>
+#ifndef QT_NO_SSL
+#include <QtNetwork/qsslconfiguration.h>
+#endif
+#include <QtCore/qurlquery.h>
+#include <QtCore/qurl.h>
+
+using namespace Qt::StringLiterals;
+using namespace std::chrono_literals;
+
+class tst_QNetworkRequestFactory : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void urlAndPath_data();
+ void urlAndPath();
+ void queryParameters();
+ void sslConfiguration();
+ void headers();
+ void bearerToken();
+ void operators();
+ void timeout();
+ void userInfo();
+ void priority();
+ void attributes();
+
+private:
+ const QUrl url1{u"http://foo.io"_s};
+ const QUrl url2{u"http://bar.io"_s};
+ const QByteArray bearerToken1{"bearertoken1"};
+ const QByteArray bearerToken2{"bearertoken2"};
+};
+
+void tst_QNetworkRequestFactory::urlAndPath_data()
+{
+ QTest::addColumn<QUrl>("baseUrl");
+ QTest::addColumn<QString>("requestPath");
+ QTest::addColumn<QUrl>("expectedRequestUrl");
+
+ QUrl base{"http://xyz.io"};
+ QUrl result{"http://xyz.io/path/to"};
+ QTest::newRow("baseUrl_nopath_noslash_1") << base << u""_s << base;
+ QTest::newRow("baseUrl_nopath_noslash_2") << base << u"/path/to"_s << result;
+ QTest::newRow("baseUrl_nopath_noslash_3") << base << u"path/to"_s << result;
+
+ base.setUrl("http://xyz.io/");
+ result.setUrl("http://xyz.io/path/to");
+ QTest::newRow("baseUrl_nopath_withslash_1") << base << u""_s << base;
+ QTest::newRow("baseUrl_nopath_withslash_2") << base << u"/path/to"_s << result;
+ QTest::newRow("baseUrl_nopath_withslash_3") << base << u"path/to"_s << result;
+
+ base.setUrl("http://xyz.io/v1");
+ result.setUrl("http://xyz.io/v1/path/to");
+ QTest::newRow("baseUrl_withpath_noslash_1") << base << u""_s << base;
+ QTest::newRow("baseUrl_withpath_noslash_2") << base << u"/path/to"_s << result;
+ QTest::newRow("baseUrl_withpath_noslash_3") << base << u"path/to"_s << result;
+
+ base.setUrl("http://xyz.io/v1/");
+ QTest::newRow("baseUrl_withpath_withslash_1") << base << u""_s << base;
+ QTest::newRow("baseUrl_withpath_withslash_2") << base << u"/path/to"_s << result;
+ QTest::newRow("baseUrl_withpath_withslash_3") << base << u"path/to"_s << result;
+
+ // Currently we keep any double '//', but not sure if there is a use case for it, or could
+ // it be corrected to a single '/'
+ base.setUrl("http://xyz.io/v1//");
+ result.setUrl("http://xyz.io/v1//path/to");
+ QTest::newRow("baseUrl_withpath_doubleslash_1") << base << u""_s << base;
+ QTest::newRow("baseUrl_withpath_doubleslash_2") << base << u"/path/to"_s << result;
+ QTest::newRow("baseUrl_withpath_doubleslash_3") << base << u"path/to"_s << result;
+}
+
+void tst_QNetworkRequestFactory::urlAndPath()
+{
+ QFETCH(QUrl, baseUrl);
+ QFETCH(QString, requestPath);
+ QFETCH(QUrl, expectedRequestUrl);
+
+ // Set with constructor
+ QNetworkRequestFactory factory1{baseUrl};
+ QCOMPARE(factory1.baseUrl(), baseUrl);
+
+ // Set with setter calls
+ QNetworkRequestFactory factory2{};
+ factory2.setBaseUrl(baseUrl);
+ QCOMPARE(factory2.baseUrl(), baseUrl);
+
+ // Request path
+ QNetworkRequest request = factory1.createRequest();
+ QCOMPARE(request.url(), baseUrl); // No path was provided for createRequest(), expect baseUrl
+ request = factory1.createRequest(requestPath);
+ QCOMPARE(request.url(), expectedRequestUrl);
+
+ // Check the request path didn't change base url
+ QCOMPARE(factory1.baseUrl(), baseUrl);
+}
+
+void tst_QNetworkRequestFactory::queryParameters()
+{
+ QNetworkRequestFactory factory({"http://example.com"});
+ const QUrlQuery query1{{"q1k", "q1v"}};
+ const QUrlQuery query2{{"q2k", "q2v"}};
+
+ // Set query parameters in createRequest() call
+ QCOMPARE(factory.createRequest(query1).url(), QUrl{"http://example.com?q1k=q1v"});
+ QCOMPARE(factory.createRequest(query2).url(), QUrl{"http://example.com?q2k=q2v"});
+
+ // Set query parameters into the factory
+ factory.setQueryParameters(query1);
+ QUrlQuery resultQuery = factory.queryParameters();
+ for (const auto &item: query1.queryItems()) {
+ QVERIFY(resultQuery.hasQueryItem(item.first));
+ QCOMPARE(resultQuery.queryItemValue(item.first), item.second);
+ }
+ QCOMPARE(factory.createRequest().url(), QUrl{"http://example.com?q1k=q1v"});
+
+ // Set query parameters into both createRequest() and factory
+ QCOMPARE(factory.createRequest(query2).url(), QUrl{"http://example.com?q2k=q2v&q1k=q1v"});
+
+ // Clear query parameters
+ factory.clearQueryParameters();
+ QVERIFY(factory.queryParameters().isEmpty());
+ QCOMPARE(factory.createRequest().url(), QUrl{"http://example.com"});
+
+ const QString pathWithQuery{"content?raw=1"};
+ // Set query parameters in per-request path
+ QCOMPARE(factory.createRequest(pathWithQuery).url(),
+ QUrl{"http://example.com/content?raw=1"});
+ // Set query parameters in per-request path and the query parameter
+ QCOMPARE(factory.createRequest(pathWithQuery, query1).url(),
+ QUrl{"http://example.com/content?q1k=q1v&raw=1"});
+ // Set query parameter in per-request path and into the factory
+ factory.setQueryParameters(query2);
+ QCOMPARE(factory.createRequest(pathWithQuery).url(),
+ QUrl{"http://example.com/content?raw=1&q2k=q2v"});
+ // Set query parameters in per-request, as additional parameters, and into the factory
+ QCOMPARE(factory.createRequest(pathWithQuery, query1).url(),
+ QUrl{"http://example.com/content?q1k=q1v&raw=1&q2k=q2v"});
+
+ // Test that other than path and query items as part of path are ignored
+ factory.setQueryParameters(query1);
+ QRegularExpression re("The provided path*");
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, re);
+ QCOMPARE(factory.createRequest("https://example2.com").url(), QUrl{"http://example.com?q1k=q1v"});
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, re);
+ QCOMPARE(factory.createRequest("https://example2.com?q3k=q3v").url(),
+ QUrl{"http://example.com?q3k=q3v&q1k=q1v"});
+}
+
+void tst_QNetworkRequestFactory::sslConfiguration()
+{
+#ifdef QT_NO_SSL
+ QSKIP("Skipping SSL tests, not supported by build");
+#else
+ // Two initially equal factories
+ QNetworkRequestFactory factory1{url1};
+ QNetworkRequestFactory factory2{url1};
+
+ // Make two differing SSL configurations (for this test it's irrelevant how they differ)
+ QSslConfiguration config1;
+ config1.setProtocol(QSsl::TlsV1_2);
+ QSslConfiguration config2;
+ config2.setProtocol(QSsl::DtlsV1_2);
+
+ // Set configuration and verify that the same config is returned
+ factory1.setSslConfiguration(config1);
+ QCOMPARE(factory1.sslConfiguration(), config1);
+ factory2.setSslConfiguration(config2);
+ QCOMPARE(factory2.sslConfiguration(), config2);
+
+ // Verify requests are set with appropriate SSL configs
+ QNetworkRequest request1 = factory1.createRequest();
+ QCOMPARE(request1.sslConfiguration(), config1);
+ QNetworkRequest request2 = factory2.createRequest();
+ QCOMPARE(request2.sslConfiguration(), config2);
+#endif
+}
+
+void tst_QNetworkRequestFactory::headers()
+{
+ const QByteArray name1{"headername1"};
+ const QByteArray name2{"headername2"};
+ const QByteArray value1{"headervalue1"};
+ const QByteArray value2{"headervalue2"};
+ const QByteArray value3{"headervalue3"};
+
+ QNetworkRequestFactory factory{url1};
+ // Initial state when no headers are set
+ QVERIFY(factory.commonHeaders().isEmpty());
+ QVERIFY(factory.commonHeaders().values(name1).isEmpty());
+ QVERIFY(!factory.commonHeaders().contains(name1));
+
+ // Set headers
+ QHttpHeaders h1;
+ h1.append(name1, value1);
+ factory.setCommonHeaders(h1);
+ QVERIFY(factory.commonHeaders().contains(name1));
+ QCOMPARE(factory.commonHeaders().combinedValue(name1), value1);
+ QCOMPARE(factory.commonHeaders().size(), 1);
+ QVERIFY(factory.commonHeaders().values("nonexistent").isEmpty());
+ QNetworkRequest request = factory.createRequest();
+ QVERIFY(request.hasRawHeader(name1));
+ QCOMPARE(request.rawHeader(name1), value1);
+
+ // Check that empty header does not match
+ QVERIFY(!factory.commonHeaders().contains(""_ba));
+ QVERIFY(factory.commonHeaders().values(""_ba).isEmpty());
+
+ // Clear headers
+ factory.clearCommonHeaders();
+ QVERIFY(factory.commonHeaders().isEmpty());
+ request = factory.createRequest();
+ QVERIFY(!request.hasRawHeader(name1));
+
+ // Set headers with more entries
+ h1.clear();
+ h1.append(name1, value1);
+ h1.append(name2, value2);
+ factory.setCommonHeaders(h1);
+ QVERIFY(factory.commonHeaders().contains(name1));
+ QVERIFY(factory.commonHeaders().contains(name2));
+ QCOMPARE(factory.commonHeaders().combinedValue(name1), value1);
+ QCOMPARE(factory.commonHeaders().combinedValue(name2), value2);
+ QCOMPARE(factory.commonHeaders().size(), 2);
+ request = factory.createRequest();
+ QVERIFY(request.hasRawHeader(name1));
+ QVERIFY(request.hasRawHeader(name2));
+ QCOMPARE(request.rawHeader(name1), value1);
+ QCOMPARE(request.rawHeader(name2), value2);
+ // Append more values to pre-existing header name2
+ h1.clear();
+ h1.append(name1, value1);
+ h1.append(name1, value2);
+ h1.append(name1, value3);
+ factory.setCommonHeaders(h1);
+ QVERIFY(factory.commonHeaders().contains(name1));
+ QCOMPARE(factory.commonHeaders().combinedValue(name1), value1 + ", " + value2 + ", " + value3);
+ request = factory.createRequest();
+ QVERIFY(request.hasRawHeader(name1));
+ QCOMPARE(request.rawHeader(name1), value1 + ", " + value2 + ", " + value3);
+}
+
+void tst_QNetworkRequestFactory::bearerToken()
+{
+ const auto authHeader = "Authorization"_ba;
+ QNetworkRequestFactory factory{url1};
+ QVERIFY(factory.bearerToken().isEmpty());
+
+ factory.setBearerToken(bearerToken1);
+ QCOMPARE(factory.bearerToken(), bearerToken1);
+ QNetworkRequest request = factory.createRequest();
+ QVERIFY(request.hasRawHeader(authHeader));
+ QCOMPARE(request.rawHeader(authHeader), "Bearer "_ba + bearerToken1);
+
+ // Verify that bearerToken is not in debug output
+ QString debugOutput;
+ QDebug debug(&debugOutput);
+ debug << factory;
+ QVERIFY(debugOutput.contains("bearerToken = (is set)"));
+ QVERIFY(!debugOutput.contains(bearerToken1));
+
+ factory.setBearerToken(bearerToken2);
+ QCOMPARE(factory.bearerToken(), bearerToken2);
+ request = factory.createRequest();
+ QVERIFY(request.hasRawHeader(authHeader));
+ QCOMPARE(request.rawHeader(authHeader), "Bearer "_ba + bearerToken2);
+
+ // Set authorization header manually
+ const auto value = "headervalue"_ba;
+ QHttpHeaders h1;
+ h1.append(authHeader, value);
+ factory.setCommonHeaders(h1);
+ request = factory.createRequest();
+ QVERIFY(request.hasRawHeader(authHeader));
+ // bearerToken has precedence over manually set header
+ QCOMPARE(request.rawHeader(authHeader), "Bearer "_ba + bearerToken2);
+ // clear bearer token, the manually set header is now used
+ factory.clearBearerToken();
+ request = factory.createRequest();
+ QVERIFY(request.hasRawHeader(authHeader));
+ QCOMPARE(request.rawHeader(authHeader), value);
+}
+
+void tst_QNetworkRequestFactory::operators()
+{
+ QNetworkRequestFactory factory1(url1);
+
+ // Copy ctor
+ QNetworkRequestFactory factory2(factory1);
+ QCOMPARE(factory2.baseUrl(), factory1.baseUrl());
+
+ // Copy assignment
+ QNetworkRequestFactory factory3;
+ factory3 = factory2;
+ QCOMPARE(factory3.baseUrl(), factory2.baseUrl());
+
+ // Move assignment
+ QNetworkRequestFactory factory4;
+ factory4 = std::move(factory3);
+ QCOMPARE(factory4.baseUrl(), factory2.baseUrl());
+
+ // Verify implicit sharing
+ factory1.setBaseUrl(url2);
+ QCOMPARE(factory1.baseUrl(), url2); // changed
+ QCOMPARE(factory2.baseUrl(), url1); // remains
+
+ // Move ctor
+ QNetworkRequestFactory factory5{std::move(factory4)};
+ QCOMPARE(factory5.baseUrl(), factory2.baseUrl()); // the moved factory4 originates from factory2
+ QCOMPARE(factory5.baseUrl(), url1);
+}
+
+void tst_QNetworkRequestFactory::timeout()
+{
+ constexpr auto defaultTimeout = 0ms;
+ constexpr auto timeout = 150ms;
+
+ QNetworkRequestFactory factory;
+ QNetworkRequest request = factory.createRequest();
+ QCOMPARE(factory.transferTimeout(), defaultTimeout);
+ QCOMPARE(request.transferTimeoutAsDuration(), defaultTimeout);
+
+ factory.setTransferTimeout(timeout);
+ request = factory.createRequest();
+ QCOMPARE(factory.transferTimeout(), timeout);
+ QCOMPARE(request.transferTimeoutAsDuration(), timeout);
+}
+
+void tst_QNetworkRequestFactory::userInfo()
+{
+ QNetworkRequestFactory factory;
+ QVERIFY(factory.userName().isEmpty());
+ QVERIFY(factory.password().isEmpty());
+
+ const auto uname = u"a_username"_s;
+ const auto password = u"a_password"_s;
+ factory.setUserName(uname);
+ QCOMPARE(factory.userName(), uname);
+ factory.setPassword(password);
+ QCOMPARE(factory.password(), password);
+
+ // Verify that debug output does not contain password
+ QString debugOutput;
+ QDebug debug(&debugOutput);
+ debug << factory;
+ QVERIFY(debugOutput.contains("password = (is set)"));
+ QVERIFY(!debugOutput.contains(password));
+
+ factory.clearUserName();
+ factory.clearPassword();
+ QVERIFY(factory.userName().isEmpty());
+ QVERIFY(factory.password().isEmpty());
+}
+
+void tst_QNetworkRequestFactory::priority()
+{
+ QNetworkRequestFactory factory(u"http://example.com"_s);
+ QCOMPARE(factory.priority(), QNetworkRequest::NormalPriority);
+ auto request = factory.createRequest("/index.html");
+ QCOMPARE(request.priority(), QNetworkRequest::NormalPriority);
+
+ factory.setPriority(QNetworkRequest::HighPriority);
+ QCOMPARE(factory.priority(), QNetworkRequest::HighPriority);
+ request = factory.createRequest("/index.html");
+ QCOMPARE(request.priority(), QNetworkRequest::HighPriority);
+}
+
+void tst_QNetworkRequestFactory::attributes()
+{
+ const auto attribute1 = QNetworkRequest::Attribute::BackgroundRequestAttribute;
+ const auto attribute2 = QNetworkRequest::User;
+ QNetworkRequestFactory factory;
+ QNetworkRequest request;
+
+ // Empty factory
+ QVERIFY(!factory.attribute(attribute1).isValid());
+ request = factory.createRequest();
+ QVERIFY(!request.attribute(attribute1).isValid());
+
+ // (Re-)set and clear individual attribute
+ factory.setAttribute(attribute1, true);
+ QVERIFY(factory.attribute(attribute1).isValid());
+ QCOMPARE(factory.attribute(attribute1).toBool(), true);
+ request = factory.createRequest();
+ QVERIFY(request.attribute(attribute1).isValid());
+ QCOMPARE(request.attribute(attribute1).toBool(), true);
+ // Replace previous value
+ factory.setAttribute(attribute1, false);
+ QVERIFY(factory.attribute(attribute1).isValid());
+ QCOMPARE(factory.attribute(attribute1).toBool(), false);
+ request = factory.createRequest();
+ QVERIFY(request.attribute(attribute1).isValid());
+ QCOMPARE(request.attribute(attribute1).toBool(), false);
+ // Clear individual attribute
+ factory.clearAttribute(attribute1);
+ QVERIFY(!factory.attribute(attribute1).isValid());
+
+ // Getter default value
+ QCOMPARE(factory.attribute(attribute2, 111).toInt(), 111); // default value returned
+ factory.setAttribute(attribute2, 222);
+ QCOMPARE(factory.attribute(attribute2, 111).toInt(), 222); // actual value returned
+ factory.clearAttribute(attribute2);
+ QCOMPARE(factory.attribute(attribute2, 111).toInt(), 111); // default value returned
+
+ // Clear attributes
+ factory.setAttribute(attribute1, true);
+ factory.setAttribute(attribute2, 333);
+ QVERIFY(factory.attribute(attribute1).isValid());
+ QVERIFY(factory.attribute(attribute2).isValid());
+ factory.clearAttributes();
+ QVERIFY(!factory.attribute(attribute1).isValid());
+ QVERIFY(!factory.attribute(attribute2).isValid());
+ request = factory.createRequest();
+ QVERIFY(!request.attribute(attribute1).isValid());
+ QVERIFY(!request.attribute(attribute2).isValid());
+}
+
+QTEST_MAIN(tst_QNetworkRequestFactory)
+#include "tst_qnetworkrequestfactory.moc"
diff --git a/tests/auto/network/access/qrestaccessmanager/CMakeLists.txt b/tests/auto/network/access/qrestaccessmanager/CMakeLists.txt
new file mode 100644
index 0000000000..614248be28
--- /dev/null
+++ b/tests/auto/network/access/qrestaccessmanager/CMakeLists.txt
@@ -0,0 +1,17 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
+ cmake_minimum_required(VERSION 3.16)
+ project(tst_qrestaccessmanager LANGUAGES CXX)
+ find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
+endif()
+
+qt_internal_add_test(tst_qrestaccessmanager
+ SOURCES
+ tst_qrestaccessmanager.cpp
+ httptestserver.cpp httptestserver_p.h
+ LIBRARIES
+ Qt::Network
+ Qt::CorePrivate
+)
diff --git a/tests/auto/network/access/qrestaccessmanager/httptestserver.cpp b/tests/auto/network/access/qrestaccessmanager/httptestserver.cpp
new file mode 100644
index 0000000000..25869eb46b
--- /dev/null
+++ b/tests/auto/network/access/qrestaccessmanager/httptestserver.cpp
@@ -0,0 +1,268 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "httptestserver_p.h"
+
+#include <QtNetwork/qtcpsocket.h>
+
+#include <QtCore/qcoreapplication.h>
+
+#include <private/qlocale_p.h>
+
+using namespace Qt::StringLiterals;
+
+static constexpr char CRLF[] = "\r\n";
+
+HttpTestServer::HttpTestServer(QObject *parent) : QTcpServer(parent)
+{
+ QObject::connect(this, &QTcpServer::newConnection, this, &HttpTestServer::handleConnected);
+ const auto ok = listen(QHostAddress::LocalHost);
+ Q_ASSERT(ok);
+};
+
+HttpTestServer::~HttpTestServer()
+{
+ if (isListening())
+ close();
+}
+
+QUrl HttpTestServer::url()
+{
+ return QUrl(u"http://127.0.0.1:%1"_s.arg(serverPort()));
+}
+
+void HttpTestServer::handleConnected()
+{
+ Q_ASSERT(!m_socket); // No socket must exist previously, this is a single-connection server
+ m_socket = nextPendingConnection();
+ Q_ASSERT(m_socket);
+ QObject::connect(m_socket, &QTcpSocket::readyRead,
+ this, &HttpTestServer::handleDataAvailable);
+}
+
+void HttpTestServer::handleDataAvailable()
+{
+ Q_ASSERT(m_socket);
+ bool ok = true;
+
+ // Parse the incoming request data into the HttpData object
+ while (m_socket->bytesAvailable()) {
+ if (state == State::ReadingMethod && !(ok = readMethod(m_socket)))
+ qWarning("Invalid Method");
+ if (ok && state == State::ReadingUrl && !(ok = readUrl(m_socket)))
+ qWarning("Invalid URL");
+ if (ok && state == State::ReadingStatus && !(ok = readStatus(m_socket)))
+ qWarning("Invalid Status");
+ if (ok && state == State::ReadingHeader && !(ok = readHeaders(m_socket)))
+ qWarning("Invalid Header");
+ if (ok && state == State::ReadingBody && !(ok = readBody(m_socket)))
+ qWarning("Invalid Body");
+ } // while bytes available
+
+ Q_ASSERT(ok);
+ Q_ASSERT(m_handler);
+ Q_ASSERT(state == State::AllDone);
+
+ if (auto values = m_request.headers.values(
+ QHttpHeaders::WellKnownHeader::Host); !values.empty()) {
+ const auto parts = values.first().split(':');
+ m_request.url.setHost(parts.at(0));
+ if (parts.size() == 2)
+ m_request.url.setPort(parts.at(1).toUInt());
+ }
+ HttpData response;
+ ResponseControl control;
+ // Inform the testcase about request and ask for response data
+ m_handler(m_request, response, control);
+
+ QByteArray responseMessage;
+ responseMessage += "HTTP/1.1 ";
+ responseMessage += QByteArray::number(response.status);
+ responseMessage += CRLF;
+ // Insert headers if any
+ for (const auto &[name,value] : response.headers.toListOfPairs()) {
+ responseMessage += name;
+ responseMessage += ": ";
+ responseMessage += value;
+ responseMessage += CRLF;
+ }
+ responseMessage += CRLF;
+ /*
+ qDebug() << "HTTPTestServer received request"
+ << "\nMethod:" << m_request.method
+ << "\nHeaders:" << m_request.headers
+ << "\nBody:" << m_request.body;
+ */
+ if (control.respond) {
+ if (control.responseChunkSize <= 0) {
+ responseMessage += response.body;
+ // qDebug() << "HTTPTestServer response:" << responseMessage;
+ m_socket->write(responseMessage);
+ } else {
+ // Respond in chunks, first write the headers
+ // qDebug() << "HTTPTestServer response:" << responseMessage;
+ m_socket->write(responseMessage);
+ // Then write bodydata in chunks, while allowing the testcase to process as well
+ QByteArray chunk;
+ while (!response.body.isEmpty()) {
+ chunk = response.body.left(control.responseChunkSize);
+ response.body.remove(0, control.responseChunkSize);
+ // qDebug() << "SERVER writing chunk" << chunk;
+ m_socket->write(chunk);
+ m_socket->flush();
+ m_socket->waitForBytesWritten();
+ // Process events until testcase indicates it's ready for next chunk.
+ // This way we can control the bytes the testcase gets in each chunk
+ control.readyForNextChunk = false;
+ while (!control.readyForNextChunk)
+ QCoreApplication::processEvents();
+ }
+ }
+ }
+ m_socket->disconnectFromHost();
+ m_request = {};
+ m_socket = nullptr; // deleted by QTcpServer during destruction
+ state = State::ReadingMethod;
+ fragment.clear();
+}
+
+bool HttpTestServer::readMethod(QTcpSocket *socket)
+{
+ bool finished = false;
+ while (socket->bytesAvailable() && !finished) {
+ const auto c = socket->read(1).at(0);
+ if (ascii_isspace(c))
+ finished = true;
+ else if (std::isupper(c) && fragment.size() < 8)
+ fragment += c;
+ else
+ return false;
+ }
+ if (finished) {
+ if (fragment == "HEAD")
+ method = Method::Head;
+ else if (fragment == "GET")
+ method = Method::Get;
+ else if (fragment == "PUT")
+ method = Method::Put;
+ else if (fragment == "PATCH")
+ method = Method::Patch;
+ else if (fragment == "POST")
+ method = Method::Post;
+ else if (fragment == "DELETE")
+ method = Method::Delete;
+ else if (fragment == "FOOBAR") // used by custom verb/method tests
+ method = Method::Custom;
+ else
+ qWarning("Invalid operation %s", fragment.data());
+
+ state = State::ReadingUrl;
+ m_request.method = fragment;
+ fragment.clear();
+
+ return method != Method::Unknown;
+ }
+ return true;
+}
+
+bool HttpTestServer::readUrl(QTcpSocket *socket)
+{
+ bool finished = false;
+ while (socket->bytesAvailable() && !finished) {
+ const auto c = socket->read(1).at(0);
+ if (std::isspace(c))
+ finished = true;
+ else
+ fragment += c;
+ }
+ if (finished) {
+ if (!fragment.startsWith('/')) {
+ qWarning("Invalid URL path %s", fragment.constData());
+ return false;
+ }
+ m_request.url = QStringLiteral("http://127.0.0.1:") + QString::number(m_request.port) +
+ QString::fromUtf8(fragment);
+ state = State::ReadingStatus;
+ if (!m_request.url.isValid()) {
+ qWarning("Invalid URL %s", fragment.constData());
+ return false;
+ }
+ fragment.clear();
+ }
+ return true;
+}
+
+bool HttpTestServer::readStatus(QTcpSocket *socket)
+{
+ bool finished = false;
+ while (socket->bytesAvailable() && !finished) {
+ fragment += socket->read(1);
+ if (fragment.endsWith(CRLF)) {
+ finished = true;
+ fragment.resize(fragment.size() - 2);
+ }
+ }
+ if (finished) {
+ if (!std::isdigit(fragment.at(fragment.size() - 3)) ||
+ fragment.at(fragment.size() - 2) != '.' ||
+ !std::isdigit(fragment.at(fragment.size() - 1))) {
+ qWarning("Invalid version");
+ return false;
+ }
+ m_request.version = std::pair(fragment.at(fragment.size() - 3) - '0',
+ fragment.at(fragment.size() - 1) - '0');
+ state = State::ReadingHeader;
+ fragment.clear();
+ }
+ return true;
+}
+
+bool HttpTestServer::readHeaders(QTcpSocket *socket)
+{
+ while (socket->bytesAvailable()) {
+ fragment += socket->read(1);
+ if (fragment.endsWith(CRLF)) {
+ if (fragment == CRLF) {
+ state = State::ReadingBody;
+ fragment.clear();
+ return true;
+ } else {
+ fragment.chop(2);
+ const int index = fragment.indexOf(':');
+ if (index == -1)
+ return false;
+
+ QByteArray key = fragment.sliced(0, index).trimmed();
+ QByteArray value = fragment.sliced(index + 1).trimmed();
+ m_request.headers.append(key, value);
+ fragment.clear();
+ }
+ }
+ }
+ return true;
+}
+
+bool HttpTestServer::readBody(QTcpSocket *socket)
+{
+ qint64 bytesLeft = 0;
+ if (auto values = m_request.headers.values(
+ QHttpHeaders::WellKnownHeader::ContentLength); !values.empty()) {
+ bool conversionResult;
+ bytesLeft = values.first().toInt(&conversionResult);
+ if (!conversionResult)
+ return false;
+ fragment.resize(bytesLeft);
+ }
+ while (bytesLeft) {
+ qint64 got = socket->read(&fragment.data()[fragment.size() - bytesLeft], bytesLeft);
+ if (got < 0)
+ return false; // error
+ bytesLeft -= got;
+ if (bytesLeft)
+ qApp->processEvents();
+ }
+ fragment.swap(m_request.body);
+ state = State::AllDone;
+ return true;
+}
+
diff --git a/tests/auto/network/access/qrestaccessmanager/httptestserver_p.h b/tests/auto/network/access/qrestaccessmanager/httptestserver_p.h
new file mode 100644
index 0000000000..0a94b2c8a6
--- /dev/null
+++ b/tests/auto/network/access/qrestaccessmanager/httptestserver_p.h
@@ -0,0 +1,90 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef QRESTACCESSSMANAGER_HTTPTESTSERVER_P_H
+#define QRESTACCESSSMANAGER_HTTPTESTSERVER_P_H
+
+#include <QtNetwork/qtcpserver.h>
+#include <QtNetwork/qhttpheaders.h>
+
+#include <QtCore/qmap.h>
+#include <QtCore/qurl.h>
+
+#include <functional>
+
+// This struct is used for parsing the incoming network request data into, as well
+// as getting the response data from the testcase
+struct HttpData {
+ QUrl url;
+ int status = 0;
+ QByteArray body;
+ QByteArray method;
+ quint16 port = 0;
+ QPair<quint8, quint8> version;
+ QHttpHeaders headers;
+};
+
+struct ResponseControl
+{
+ bool respond = true;
+ qsizetype responseChunkSize = -1;
+ bool readyForNextChunk = true;
+};
+
+// Simple HTTP server. Currently supports only one concurrent connection
+class HttpTestServer : public QTcpServer
+{
+ Q_OBJECT
+
+public:
+ explicit HttpTestServer(QObject *parent = nullptr);
+ ~HttpTestServer() override;
+
+ // Returns this server's URL for the testcase to send requests to
+ QUrl url();
+
+ enum class State {
+ ReadingMethod,
+ ReadingUrl,
+ ReadingStatus,
+ ReadingHeader,
+ ReadingBody,
+ AllDone
+ } state = State::ReadingMethod;
+
+ enum class Method {
+ Unknown,
+ Head,
+ Get,
+ Put,
+ Patch,
+ Post,
+ Delete,
+ Custom,
+ } method = Method::Unknown;
+
+ // Parsing helpers for incoming data => HttpData
+ bool readMethod(QTcpSocket *socket);
+ bool readUrl(QTcpSocket *socket);
+ bool readStatus(QTcpSocket *socket);
+ bool readHeaders(QTcpSocket *socket);
+ bool readBody(QTcpSocket *socket);
+ // Parsing-time buffer in case data is received a small chunk at a time (readyRead())
+ QByteArray fragment;
+
+ // Settable callback for testcase. Gives the received request data, and takes in response data
+ using Handler = std::function<void(const HttpData &request, HttpData &response,
+ ResponseControl &control)>;
+ void setHandler(Handler handler) { m_handler = std::move(handler); }
+
+private slots:
+ void handleConnected();
+ void handleDataAvailable();
+
+private:
+ QTcpSocket *m_socket = nullptr;
+ HttpData m_request;
+ Handler m_handler = nullptr;
+};
+
+#endif // QRESTACCESSSMANAGER_HTTPTESTSERVER_P_H
diff --git a/tests/auto/network/access/qrestaccessmanager/tst_qrestaccessmanager.cpp b/tests/auto/network/access/qrestaccessmanager/tst_qrestaccessmanager.cpp
new file mode 100644
index 0000000000..814a9b27f4
--- /dev/null
+++ b/tests/auto/network/access/qrestaccessmanager/tst_qrestaccessmanager.cpp
@@ -0,0 +1,870 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "httptestserver_p.h"
+
+#include <QtNetwork/qhttpmultipart.h>
+#include <QtNetwork/qrestaccessmanager.h>
+#include <QtNetwork/qauthenticator.h>
+#include <QtNetwork/qnetworkreply.h>
+#include <QtNetwork/qnetworkrequestfactory.h>
+#include <QtNetwork/qrestreply.h>
+
+#include <QTest>
+#include <QtTest/qsignalspy.h>
+
+#include <QtCore/private/qglobal_p.h> // for access to Qt's feature system
+#include <QtCore/qbuffer.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonarray.h>
+#include <QtCore/qstringconverter.h>
+
+using namespace Qt::StringLiterals;
+using namespace std::chrono_literals;
+
+using Header = QHttpHeaders::WellKnownHeader;
+
+class tst_QRestAccessManager : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void initialization();
+ void destruction();
+ void callbacks();
+ void requests();
+ void reply();
+ void errors();
+ void body();
+ void json();
+ void text();
+ void textStreaming();
+
+private:
+ void memberHandler(QRestReply &reply);
+
+ friend class Transient;
+ QList<QNetworkReply*> m_expectedReplies;
+ QList<QNetworkReply*> m_actualReplies;
+};
+
+void tst_QRestAccessManager::initialization()
+{
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, "QRestAccessManager: QNetworkAccesManager is nullptr");
+ QRestAccessManager manager1(nullptr);
+ QVERIFY(!manager1.networkAccessManager());
+
+ QNetworkAccessManager qnam;
+ QRestAccessManager manager2(&qnam);
+ QVERIFY(manager2.networkAccessManager());
+}
+
+void tst_QRestAccessManager::reply()
+{
+ QNetworkAccessManager qnam;
+
+ QNetworkReply *nr = qnam.get(QNetworkRequest(QUrl{"someurl"}));
+ QRestReply rr1(nr);
+ QCOMPARE(rr1.networkReply(), nr);
+
+ // Move-construct
+ QRestReply rr2(std::move(rr1));
+ QCOMPARE(rr2.networkReply(), nr);
+
+ // Move-assign
+ QTest::ignoreMessage(QtMsgType::QtWarningMsg, "QRestReply: QNetworkReply is nullptr");
+ QRestReply rr3(nullptr);
+ rr3 = std::move(rr2);
+ QCOMPARE(rr3.networkReply(), nr);
+}
+
+#define VERIFY_REPLY_OK(METHOD) \
+{ \
+ QTRY_VERIFY(networkReply); \
+ QRestReply restReply(networkReply); \
+ QCOMPARE(serverSideRequest.method, METHOD); \
+ QVERIFY(restReply.isSuccess()); \
+ QVERIFY(!restReply.hasError()); \
+ networkReply->deleteLater(); \
+ networkReply = nullptr; \
+}
+
+void tst_QRestAccessManager::requests()
+{
+ // A basic test for each HTTP method against the local testserver.
+ QNetworkAccessManager qnam;
+ QRestAccessManager manager(&qnam);
+ HttpTestServer server;
+ QTRY_VERIFY(server.isListening());
+ QNetworkRequest request(server.url());
+ request.setRawHeader("Content-Type"_ba, "text/plain"); // To silence missing content-type warn
+ QNetworkReply *networkReply = nullptr;
+ std::unique_ptr<QHttpMultiPart> multiPart;
+ QHttpPart part;
+ part.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"text\""));
+ part.setBody("multipart_text");
+ QByteArray ioDeviceData{"io_device_data"_ba};
+ QBuffer bufferIoDevice(&ioDeviceData);
+
+ HttpData serverSideRequest; // The request data the server received
+ HttpData serverSideResponse; // The response data the server responds with
+ serverSideResponse.status = 200;
+ server.setHandler([&](const HttpData &request, HttpData &response, ResponseControl&) {
+ serverSideRequest = request;
+ response = serverSideResponse;
+
+ });
+ auto callback = [&](QRestReply &reply) { networkReply = reply.networkReply(); };
+ const QByteArray byteArrayData{"some_data"_ba};
+ const QJsonObject jsonObjectData{{"key1", "value1"}, {"key2", "value2"}};
+ const QJsonArray jsonArrayData{{"arrvalue1", "arrvalue2", QJsonObject{{"key1", "value1"}}}};
+ const QVariantMap variantMapData{{"key1", "value1"}, {"key2", "value2"}};
+ const QByteArray methodDELETE{"DELETE"_ba};
+ const QByteArray methodHEAD{"HEAD"_ba};
+ const QByteArray methodPOST{"POST"_ba};
+ const QByteArray methodGET{"GET"_ba};
+ const QByteArray methodPUT{"PUT"_ba};
+ const QByteArray methodPATCH{"PATCH"_ba};
+ const QByteArray methodCUSTOM{"FOOBAR"_ba};
+
+ // DELETE
+ manager.deleteResource(request, this, callback);
+ VERIFY_REPLY_OK(methodDELETE);
+ QCOMPARE(serverSideRequest.body, ""_ba);
+
+ // HEAD
+ manager.head(request, this, callback);
+ VERIFY_REPLY_OK(methodHEAD);
+ QCOMPARE(serverSideRequest.body, ""_ba);
+
+ // GET
+ manager.get(request, this, callback);
+ VERIFY_REPLY_OK(methodGET);
+ QCOMPARE(serverSideRequest.body, ""_ba);
+
+ manager.get(request, byteArrayData, this, callback);
+ VERIFY_REPLY_OK(methodGET);
+ QCOMPARE(serverSideRequest.body, byteArrayData);
+
+ manager.get(request, QJsonDocument{jsonObjectData}, this, callback);
+ VERIFY_REPLY_OK(methodGET);
+ QCOMPARE(QJsonDocument::fromJson(serverSideRequest.body).object(), jsonObjectData);
+
+ manager.get(request, &bufferIoDevice, this, callback);
+ VERIFY_REPLY_OK(methodGET);
+ QCOMPARE(serverSideRequest.body, ioDeviceData);
+
+ // CUSTOM
+ manager.sendCustomRequest(request, methodCUSTOM, byteArrayData, this, callback);
+ VERIFY_REPLY_OK(methodCUSTOM);
+ QCOMPARE(serverSideRequest.body, byteArrayData);
+
+ manager.sendCustomRequest(request, methodCUSTOM, &bufferIoDevice, this, callback);
+ VERIFY_REPLY_OK(methodCUSTOM);
+ QCOMPARE(serverSideRequest.body, ioDeviceData);
+
+ multiPart.reset(new QHttpMultiPart(QHttpMultiPart::FormDataType));
+ multiPart->append(part);
+ manager.sendCustomRequest(request, methodCUSTOM, multiPart.get(), this, callback);
+ VERIFY_REPLY_OK(methodCUSTOM);
+ QVERIFY(serverSideRequest.body.contains("--boundary"_ba));
+ QVERIFY(serverSideRequest.body.contains("multipart_text"_ba));
+
+ // POST
+ manager.post(request, byteArrayData, this, callback);
+ VERIFY_REPLY_OK(methodPOST);
+ QCOMPARE(serverSideRequest.body, byteArrayData);
+
+ manager.post(request, QJsonDocument{jsonObjectData}, this, callback);
+ VERIFY_REPLY_OK(methodPOST);
+ QCOMPARE(QJsonDocument::fromJson(serverSideRequest.body).object(), jsonObjectData);
+
+ manager.post(request, QJsonDocument{jsonArrayData}, this, callback);
+ VERIFY_REPLY_OK(methodPOST);
+ QCOMPARE(QJsonDocument::fromJson(serverSideRequest.body).array(), jsonArrayData);
+
+ manager.post(request, variantMapData, this, callback);
+ VERIFY_REPLY_OK(methodPOST);
+ QCOMPARE(QJsonDocument::fromJson(serverSideRequest.body).object(), jsonObjectData);
+
+ multiPart = std::make_unique<QHttpMultiPart>(QHttpMultiPart::FormDataType);
+ multiPart->append(part);
+ manager.post(request, multiPart.get(), this, callback);
+ VERIFY_REPLY_OK(methodPOST);
+ QVERIFY(serverSideRequest.body.contains("--boundary"_ba));
+ QVERIFY(serverSideRequest.body.contains("multipart_text"_ba));
+
+ manager.post(request, &bufferIoDevice, this, callback);
+ VERIFY_REPLY_OK(methodPOST);
+ QCOMPARE(serverSideRequest.body, ioDeviceData);
+
+ // PUT
+ manager.put(request, byteArrayData, this, callback);
+ VERIFY_REPLY_OK(methodPUT);
+ QCOMPARE(serverSideRequest.body, byteArrayData);
+
+ manager.put(request, QJsonDocument{jsonObjectData}, this, callback);
+ VERIFY_REPLY_OK(methodPUT);
+ QCOMPARE(QJsonDocument::fromJson(serverSideRequest.body).object(), jsonObjectData);
+
+ manager.put(request, QJsonDocument{jsonArrayData}, this, callback);
+ VERIFY_REPLY_OK(methodPUT);
+ QCOMPARE(QJsonDocument::fromJson(serverSideRequest.body).array(), jsonArrayData);
+
+ manager.put(request, variantMapData, this, callback);
+ VERIFY_REPLY_OK(methodPUT);
+ QCOMPARE(QJsonDocument::fromJson(serverSideRequest.body).object(), jsonObjectData);
+
+ multiPart = std::make_unique<QHttpMultiPart>(QHttpMultiPart::FormDataType);
+ multiPart->append(part);
+ manager.put(request, multiPart.get(), this, callback);
+ VERIFY_REPLY_OK(methodPUT);
+ QVERIFY(serverSideRequest.body.contains("--boundary"_ba));
+ QVERIFY(serverSideRequest.body.contains("multipart_text"_ba));
+
+ manager.put(request, &bufferIoDevice, this, callback);
+ VERIFY_REPLY_OK(methodPUT);
+ QCOMPARE(serverSideRequest.body, ioDeviceData);
+
+ // PATCH
+ manager.patch(request, byteArrayData, this, callback);
+ VERIFY_REPLY_OK(methodPATCH);
+ QCOMPARE(serverSideRequest.body, byteArrayData);
+
+ manager.patch(request, QJsonDocument{jsonObjectData}, this, callback);
+ VERIFY_REPLY_OK(methodPATCH);
+ QCOMPARE(QJsonDocument::fromJson(serverSideRequest.body).object(), jsonObjectData);
+
+ manager.patch(request, QJsonDocument{jsonArrayData}, this, callback);
+ VERIFY_REPLY_OK(methodPATCH);
+ QCOMPARE(QJsonDocument::fromJson(serverSideRequest.body).array(), jsonArrayData);
+
+ manager.patch(request, variantMapData, this, callback);
+ VERIFY_REPLY_OK(methodPATCH);
+ QCOMPARE(QJsonDocument::fromJson(serverSideRequest.body).object(), jsonObjectData);
+
+ manager.patch(request, &bufferIoDevice, this, callback);
+ VERIFY_REPLY_OK(methodPATCH);
+ QCOMPARE(serverSideRequest.body, ioDeviceData);
+
+ //These must NOT compile
+ //manager.get(request, [](){}); // callback without context object
+ //manager.get(request, ""_ba, [](){}); // callback without context object
+ //manager.get(request, QString()); // wrong datatype
+ //manager.get(request, 123); // wrong datatype
+ //manager.post(request, QString()); // wrong datatype
+ //manager.put(request, 123); // wrong datatype
+ //manager.post(request); // data is required
+ //manager.put(request, QString()); // wrong datatype
+ //manager.put(request); // data is required
+ //manager.patch(request, 123); // wrong datatype
+ //manager.patch(request, QString()); // wrong datatype
+ //manager.patch(request); // data is required
+ //manager.deleteResource(request, "f"_ba); // data not allowed
+ //manager.head(request, "f"_ba); // data not allowed
+ //manager.post(request, ""_ba, this, [](int param){}); // Wrong callback signature
+ //manager.get(request, this, [](int param){}); // Wrong callback signature
+ //manager.sendCustomRequest(request, this, [](){}); // No verb && no data
+ //manager.sendCustomRequest(request, "FOOBAR", this, [](){}); // No verb || no data
+}
+
+void tst_QRestAccessManager::memberHandler(QRestReply &reply)
+{
+ m_actualReplies.append(reply.networkReply());
+}
+
+// Class that is destroyed during an active request.
+// Used to test that the callbacks won't be called in these cases
+class Transient : public QObject
+{
+ Q_OBJECT
+public:
+ explicit Transient(tst_QRestAccessManager *test) : QObject(test), m_test(test) {}
+
+ void memberHandler(QRestReply &reply)
+ {
+ m_test->m_actualReplies.append(reply.networkReply());
+ }
+
+private:
+ tst_QRestAccessManager *m_test = nullptr;
+};
+
+template <typename Functor, std::enable_if_t<
+ QtPrivate::AreFunctionsCompatible<void(*)(QRestReply&), Functor>::value, bool> = true>
+inline constexpr bool isCompatibleCallback(Functor &&) { return true; }
+
+template <typename Functor, std::enable_if_t<
+ !QtPrivate::AreFunctionsCompatible<void(*)(QRestReply&), Functor>::value, bool> = true,
+ typename = void>
+inline constexpr bool isCompatibleCallback(Functor &&) { return false; }
+
+void tst_QRestAccessManager::callbacks()
+{
+ QNetworkAccessManager qnam;
+ QRestAccessManager manager(&qnam);
+
+ QNetworkRequest request{u"i_dont_exist"_s}; // Will result in ProtocolUnknown error
+
+ auto lambdaHandler = [this](QRestReply &reply) { m_actualReplies.append(reply.networkReply()); };
+ Transient *transient = nullptr;
+ QByteArray data{"some_data"};
+
+ // Compile-time tests for callback signatures
+ static_assert(isCompatibleCallback([](QRestReply&){})); // Correct signature
+ static_assert(isCompatibleCallback(lambdaHandler));
+ static_assert(isCompatibleCallback(&Transient::memberHandler));
+ static_assert(isCompatibleCallback([](){})); // Less parameters are allowed
+
+ static_assert(!isCompatibleCallback([](QString){})); // Wrong parameter type
+ static_assert(!isCompatibleCallback([](QRestReply*){})); // Wrong parameter type
+ static_assert(!isCompatibleCallback([](const QString &){})); // Wrong parameter type
+ static_assert(!isCompatibleCallback([](QRestReply&, QString){})); // Too many parameters
+
+ // -- Test without data
+ // Without callback using signals and slot
+ QNetworkReply* reply = manager.get(request);
+ m_expectedReplies.append(reply);
+ QObject::connect(reply, &QNetworkReply::finished, this,
+ [this, reply](){m_actualReplies.append(reply);});
+
+ // With lambda callback, without context object
+ m_expectedReplies.append(manager.get(request, nullptr, lambdaHandler));
+ m_expectedReplies.append(manager.get(request, nullptr,
+ [this](QRestReply &reply){m_actualReplies.append(reply.networkReply());}));
+ // With lambda callback and context object
+ m_expectedReplies.append(manager.get(request, this, lambdaHandler));
+ m_expectedReplies.append(manager.get(request, this,
+ [this](QRestReply &reply){m_actualReplies.append(reply.networkReply());}));
+ // With member callback and context object
+ m_expectedReplies.append(manager.get(request, this, &tst_QRestAccessManager::memberHandler));
+ // With context object that is destroyed, there should be no callback or eg. crash.
+ transient = new Transient(this);
+ manager.get(request, transient, &Transient::memberHandler); // Reply not added to expecteds
+ delete transient;
+
+ // Let requests finish
+ QTRY_COMPARE(m_actualReplies.size(), m_expectedReplies.size());
+ for (auto reply: m_actualReplies) {
+ QRestReply restReply(reply);
+ QVERIFY(!restReply.isSuccess());
+ QVERIFY(restReply.hasError());
+ QCOMPARE(restReply.error(), QNetworkReply::ProtocolUnknownError);
+ QCOMPARE(restReply.networkReply()->isFinished(), true);
+ restReply.networkReply()->deleteLater();
+ }
+ m_actualReplies.clear();
+ m_expectedReplies.clear();
+
+ // -- Test with data
+ // With lambda callback, without context object
+ m_expectedReplies.append(manager.post(request, data, nullptr, lambdaHandler));
+ m_expectedReplies.append(manager.post(request, data, nullptr,
+ [this](QRestReply &reply){m_actualReplies.append(reply.networkReply());}));
+ // With lambda callback and context object
+ m_expectedReplies.append(manager.post(request, data, this, lambdaHandler));
+ m_expectedReplies.append(manager.post(request, data, this,
+ [this](QRestReply &reply){m_actualReplies.append(reply.networkReply());}));
+ // With member callback and context object
+ m_expectedReplies.append(manager.post(request, data,
+ this, &tst_QRestAccessManager::memberHandler));
+ // With context object that is destroyed, there should be no callback or eg. crash
+ transient = new Transient(this);
+ manager.post(request, data, transient, &Transient::memberHandler); // Note: reply not expected
+ delete transient;
+
+ // Let requests finish
+ QTRY_COMPARE(m_actualReplies.size(), m_expectedReplies.size());
+ for (auto reply: m_actualReplies) {
+ QRestReply restReply(reply);
+ QVERIFY(!restReply.isSuccess());
+ QVERIFY(restReply.hasError());
+ QCOMPARE(restReply.error(), QNetworkReply::ProtocolUnknownError);
+ QCOMPARE(restReply.networkReply()->isFinished(), true);
+ reply->deleteLater();
+ }
+ m_actualReplies.clear();
+ m_expectedReplies.clear();
+
+ // -- Test GET with data separately, as GET provides methods that are usable with and
+ // without data, and fairly easy to get the qrestaccessmanager.h template SFINAE subtly wrong.
+ // With lambda callback, without context object
+ m_expectedReplies.append(manager.get(request, data, nullptr, lambdaHandler));
+ m_expectedReplies.append(manager.get(request, data, nullptr,
+ [this](QRestReply &reply){m_actualReplies.append(reply.networkReply());}));
+ // With lambda callback and context object
+ m_expectedReplies.append(manager.get(request, data, this, lambdaHandler));
+ m_expectedReplies.append(manager.get(request, data, this,
+ [this](QRestReply &reply){m_actualReplies.append(reply.networkReply());}));
+ // With member callback and context object
+ m_expectedReplies.append(manager.get(request, data,
+ this, &tst_QRestAccessManager::memberHandler));
+ // With context object that is destroyed, there should be no callback or eg. crash
+ transient = new Transient(this);
+ manager.get(request, data, transient, &Transient::memberHandler); // Reply not added
+ delete transient;
+
+ // Let requests finish
+ QTRY_COMPARE(m_actualReplies.size(), m_expectedReplies.size());
+ for (auto reply: m_actualReplies) {
+ QRestReply restReply(reply);
+ QVERIFY(!restReply.isSuccess());
+ QVERIFY(restReply.hasError());
+ QCOMPARE(restReply.error(), QNetworkReply::ProtocolUnknownError);
+ QCOMPARE(restReply.networkReply()->isFinished(), true);
+ restReply.networkReply()->deleteLater();
+ }
+ m_actualReplies.clear();
+ m_expectedReplies.clear();
+}
+
+void tst_QRestAccessManager::destruction()
+{
+ std::unique_ptr<QNetworkAccessManager> qnam = std::make_unique<QNetworkAccessManager>();
+ std::unique_ptr<QRestAccessManager> manager = std::make_unique<QRestAccessManager>(qnam.get());
+ QNetworkRequest request{u"i_dont_exist"_s}; // Will result in ProtocolUnknown error
+ m_expectedReplies.clear();
+ m_actualReplies.clear();
+ auto handler = [this](QRestReply &reply) { m_actualReplies.append(reply.networkReply()); };
+
+ // Delete reply immediately, make sure nothing bad happens and that there is no callback
+ QNetworkReply *networkReply = manager->get(request, this, handler);
+ delete networkReply;
+ QTest::qWait(20); // allow some time for the callback to arrive (it shouldn't)
+ QCOMPARE(m_actualReplies.size(), m_expectedReplies.size()); // Both should be 0
+
+ // Delete access manager immediately after request, make sure nothing bad happens
+ manager->get(request, this, handler);
+ manager->post(request, "data"_ba, this, handler);
+ QTest::ignoreMessage(QtWarningMsg, "Access manager destroyed while 2 requests were still"
+ " in progress");
+ manager.reset();
+ QTest::qWait(20);
+ QCOMPARE(m_actualReplies.size(), m_expectedReplies.size()); // Both should be 0
+
+ // Destroy the underlying QNAM while requests in progress
+ manager = std::make_unique<QRestAccessManager>(qnam.get());
+ manager->get(request, this, handler);
+ manager->post(request, "data"_ba, this, handler);
+ qnam.reset();
+ QTest::qWait(20);
+ QCOMPARE(m_actualReplies.size(), m_expectedReplies.size()); // Both should be 0
+}
+
+#define VERIFY_HTTP_ERROR_STATUS(STATUS) \
+{ \
+ serverSideResponse.status = STATUS; \
+ QRestReply restReply(manager.get(request)); \
+ QTRY_VERIFY(restReply.networkReply()->isFinished()); \
+ QVERIFY(!restReply.hasError()); \
+ QCOMPARE(restReply.httpStatus(), serverSideResponse.status); \
+ QCOMPARE(restReply.error(), QNetworkReply::NetworkError::NoError); \
+ QVERIFY(!restReply.isSuccess()); \
+ restReply.networkReply()->deleteLater(); \
+} \
+
+void tst_QRestAccessManager::errors()
+{
+ // Tests the distinction between HTTP and other (network/protocol) errors
+ QNetworkAccessManager qnam;
+ QRestAccessManager manager(&qnam);
+ HttpTestServer server;
+ QTRY_VERIFY(server.isListening());
+ QNetworkRequest request(server.url());
+
+ HttpData serverSideResponse; // The response data the server responds with
+ server.setHandler([&](const HttpData &, HttpData &response, ResponseControl &) {
+ response = serverSideResponse;
+ });
+
+ // Test few HTTP statuses in different categories
+ VERIFY_HTTP_ERROR_STATUS(301) // QNetworkReply::ProtocolUnknownError
+ VERIFY_HTTP_ERROR_STATUS(302) // QNetworkReply::ProtocolUnknownError
+ VERIFY_HTTP_ERROR_STATUS(400) // QNetworkReply::ProtocolInvalidOperationError
+ VERIFY_HTTP_ERROR_STATUS(401) // QNetworkReply::AuthenticationRequiredEror
+ VERIFY_HTTP_ERROR_STATUS(402) // QNetworkReply::UnknownContentError
+ VERIFY_HTTP_ERROR_STATUS(403) // QNetworkReply::ContentAccessDenied
+ VERIFY_HTTP_ERROR_STATUS(404) // QNetworkReply::ContentNotFoundError
+ VERIFY_HTTP_ERROR_STATUS(405) // QNetworkReply::ContentOperationNotPermittedError
+ VERIFY_HTTP_ERROR_STATUS(406) // QNetworkReply::UnknownContentError
+ VERIFY_HTTP_ERROR_STATUS(407) // QNetworkReply::ProxyAuthenticationRequiredError
+ VERIFY_HTTP_ERROR_STATUS(408) // QNetworkReply::UnknownContentError
+ VERIFY_HTTP_ERROR_STATUS(409) // QNetworkReply::ContentConflictError
+ VERIFY_HTTP_ERROR_STATUS(410) // QNetworkReply::ContentGoneError
+ VERIFY_HTTP_ERROR_STATUS(500) // QNetworkReply::InternalServerError
+ VERIFY_HTTP_ERROR_STATUS(501) // QNetworkReply::OperationNotImplementedError
+ VERIFY_HTTP_ERROR_STATUS(502) // QNetworkReply::UnknownServerError
+ VERIFY_HTTP_ERROR_STATUS(503) // QNetworkReply::ServiceUnavailableError
+ VERIFY_HTTP_ERROR_STATUS(504) // QNetworkReply::UnknownServerError
+ VERIFY_HTTP_ERROR_STATUS(505) // QNetworkReply::UnknownServerError
+
+ {
+ // Test that actual network/protocol errors come through
+ QRestReply restReply(manager.get({})); // Empty url
+ QTRY_VERIFY(restReply.networkReply()->isFinished());
+ QVERIFY(restReply.hasError());
+ QVERIFY(!restReply.isSuccess());
+ QCOMPARE(restReply.error(), QNetworkReply::ProtocolUnknownError);
+ restReply.networkReply()->deleteLater();
+ }
+
+ {
+ QRestReply restReply(manager.get(QNetworkRequest{{"http://non-existent.foo.bar.test"}}));
+ QTRY_VERIFY(restReply.networkReply()->isFinished());
+ QVERIFY(restReply.hasError());
+ QVERIFY(!restReply.isSuccess());
+ QCOMPARE(restReply.error(), QNetworkReply::HostNotFoundError);
+ restReply.networkReply()->deleteLater();
+ }
+
+ {
+ QRestReply restReply(manager.get(request));
+ restReply.networkReply()->abort();
+ QTRY_VERIFY(restReply.networkReply()->isFinished());
+ QVERIFY(restReply.hasError());
+ QVERIFY(!restReply.isSuccess());
+ QCOMPARE(restReply.error(), QNetworkReply::OperationCanceledError);
+ restReply.networkReply()->deleteLater();
+ }
+}
+
+void tst_QRestAccessManager::body()
+{
+ // Test using QRestReply::body() data accessor
+ QNetworkAccessManager qnam;
+ QRestAccessManager manager(&qnam);
+ HttpTestServer server;
+ QTRY_VERIFY(server.isListening());
+ QNetworkRequest request(server.url());
+ QNetworkReply *networkReply = nullptr;
+
+ HttpData serverSideRequest; // The request data the server received
+ HttpData serverSideResponse; // The response data the server responds with
+ server.setHandler([&](const HttpData &request, HttpData &response, ResponseControl&) {
+ serverSideRequest = request;
+ response = serverSideResponse;
+ });
+
+ {
+ serverSideResponse.status = 200;
+ serverSideResponse.body = "some_data"_ba;
+ manager.get(request, this, [&](QRestReply &reply) { networkReply = reply.networkReply(); });
+ QTRY_VERIFY(networkReply);
+ QRestReply restReply(networkReply);
+ QCOMPARE(restReply.readBody(), serverSideResponse.body);
+ QCOMPARE(restReply.httpStatus(), serverSideResponse.status);
+ QVERIFY(!restReply.hasError());
+ QVERIFY(restReply.isSuccess());
+ networkReply->deleteLater();
+ networkReply = nullptr;
+ }
+
+ {
+ serverSideResponse.status = 200;
+ serverSideResponse.body = ""_ba; // Empty
+ manager.get(request, this, [&](QRestReply &reply) { networkReply = reply.networkReply(); });
+ QTRY_VERIFY(networkReply);
+ QRestReply restReply(networkReply);
+ QCOMPARE(restReply.readBody(), serverSideResponse.body);
+ networkReply->deleteLater();
+ networkReply = nullptr;
+ }
+
+ {
+ serverSideResponse.status = 500;
+ serverSideResponse.body = "some_other_data"_ba;
+ manager.get(request, this, [&](QRestReply &reply) { networkReply = reply.networkReply(); });
+ QTRY_VERIFY(networkReply);
+ QRestReply restReply(networkReply);
+ QCOMPARE(restReply.readBody(), serverSideResponse.body);
+ QCOMPARE(restReply.httpStatus(), serverSideResponse.status);
+ QVERIFY(!restReply.hasError());
+ QVERIFY(!restReply.isSuccess());
+ networkReply->deleteLater();
+ networkReply = nullptr;
+ }
+}
+
+void tst_QRestAccessManager::json()
+{
+ // Tests using QRestReply::readJson()
+ QNetworkAccessManager qnam;
+ QRestAccessManager manager(&qnam);
+ HttpTestServer server;
+ QTRY_VERIFY(server.isListening());
+ QNetworkRequest request(server.url());
+ QNetworkReply *networkReply = nullptr;
+ QJsonDocument responseJsonDocument;
+ std::optional<QJsonDocument> json;
+ QJsonParseError parseError;
+
+ HttpData serverSideRequest; // The request data the server received
+ HttpData serverSideResponse; // The response data the server responds with
+ serverSideResponse.status = 200;
+ server.setHandler([&](const HttpData &request, HttpData &response, ResponseControl&) {
+ serverSideRequest = request;
+ response = serverSideResponse;
+ });
+
+ {
+ // Test receiving valid json object
+ serverSideResponse.body = "{\"key1\":\"value1\",""\"key2\":\"value2\"}\n"_ba;
+ networkReply = manager.get(request);
+ // Read unfinished reply
+ QVERIFY(!networkReply->isFinished());
+ QTest::ignoreMessage(QtWarningMsg, "readJson() called on an unfinished reply, ignoring");
+ parseError.error = QJsonParseError::ParseError::DocumentTooLarge; // Reset to impossible value
+ QRestReply restReply(networkReply);
+ QVERIFY(!restReply.readJson(&parseError));
+ QCOMPARE(parseError.error, QJsonParseError::ParseError::NoError);
+ // Read finished reply
+ QTRY_VERIFY(networkReply->isFinished());
+ parseError.error = QJsonParseError::ParseError::DocumentTooLarge;
+ json = restReply.readJson(&parseError);
+ QVERIFY(json);
+ QCOMPARE(parseError.error, QJsonParseError::ParseError::NoError);
+ responseJsonDocument = *json;
+ QVERIFY(responseJsonDocument.isObject());
+ QCOMPARE(responseJsonDocument["key1"], "value1");
+ QCOMPARE(responseJsonDocument["key2"], "value2");
+ networkReply->deleteLater();
+ networkReply = nullptr;
+ }
+
+ {
+ // Test receiving an invalid json object
+ serverSideResponse.body = "foobar"_ba;
+ manager.get(request, this, [&](QRestReply &reply) { networkReply = reply.networkReply(); });
+ QTRY_VERIFY(networkReply);
+ QRestReply restReply(networkReply);
+ parseError.error = QJsonParseError::ParseError::DocumentTooLarge;
+ const auto json = restReply.readJson(&parseError);
+ networkReply->deleteLater();
+ networkReply = nullptr;
+ QCOMPARE_EQ(json, std::nullopt);
+ QCOMPARE_NE(parseError.error, QJsonParseError::ParseError::NoError);
+ QCOMPARE_NE(parseError.error, QJsonParseError::ParseError::DocumentTooLarge);
+ QCOMPARE_GT(parseError.offset, 0);
+ }
+
+ {
+ // Test receiving valid json array
+ serverSideResponse.body = "[\"foo\", \"bar\"]\n"_ba;
+ manager.get(request, this, [&](QRestReply &reply) { networkReply = reply.networkReply(); });
+ QTRY_VERIFY(networkReply);
+ QRestReply restReply(networkReply);
+ parseError.error = QJsonParseError::ParseError::DocumentTooLarge;
+ json = restReply.readJson(&parseError);
+ networkReply->deleteLater();
+ networkReply = nullptr;
+ QCOMPARE(parseError.error, QJsonParseError::ParseError::NoError);
+ QVERIFY(json);
+ responseJsonDocument = *json;
+ QVERIFY(responseJsonDocument.isArray());
+ QCOMPARE(responseJsonDocument.array().size(), 2);
+ QCOMPARE(responseJsonDocument[0].toString(), "foo"_L1);
+ QCOMPARE(responseJsonDocument[1].toString(), "bar"_L1);
+ }
+}
+
+#define VERIFY_TEXT_REPLY_OK \
+{ \
+ manager.get(request, this, [&](QRestReply &reply) { networkReply = reply.networkReply(); }); \
+ QTRY_VERIFY(networkReply); \
+ QRestReply restReply(networkReply); \
+ responseString = restReply.readText(); \
+ networkReply->deleteLater(); \
+ networkReply = nullptr; \
+ QCOMPARE(responseString, sourceString); \
+}
+
+#define VERIFY_TEXT_REPLY_ERROR(WARNING_MESSAGE) \
+{ \
+ manager.get(request, this, [&](QRestReply &reply) { networkReply = reply.networkReply(); }); \
+ QTRY_VERIFY(networkReply); \
+ QTest::ignoreMessage(QtWarningMsg, WARNING_MESSAGE); \
+ QRestReply restReply(networkReply); \
+ responseString = restReply.readText(); \
+ networkReply->deleteLater(); \
+ networkReply = nullptr; \
+ QVERIFY(responseString.isEmpty()); \
+}
+
+void tst_QRestAccessManager::text()
+{
+ // Test using QRestReply::text() data accessor with various text encodings
+ QNetworkAccessManager qnam;
+ QRestAccessManager manager(&qnam);
+ HttpTestServer server;
+ QTRY_VERIFY(server.isListening());
+ QNetworkRequest request(server.url());
+ QNetworkReply *networkReply = nullptr;
+ QJsonObject responseJsonObject;
+
+ QStringEncoder encUTF8("UTF-8");
+ QStringEncoder encUTF16("UTF-16");
+ QStringEncoder encUTF32("UTF-32");
+ QString responseString;
+
+ HttpData serverSideRequest; // The request data the server received
+ HttpData serverSideResponse; // The response data the server responds with
+ serverSideResponse.status = 200;
+ server.setHandler([&](const HttpData &request, HttpData &response, ResponseControl&) {
+ serverSideRequest = request;
+ response = serverSideResponse;
+ });
+
+ const QString sourceString("this is a string"_L1);
+
+ // Charset parameter of Content-Type header may specify non-UTF-8 character encoding.
+ //
+ // QString is UTF-16, and in the tests below we encode the response data to various
+ // charset encodings (into byte arrays). When we get the response data, the text()
+ // should consider the indicated charset and convert it to an UTF-16 QString => the returned
+ // QString from text() should match with the original (UTF-16) QString.
+
+ // Successful UTF-8 (explicit)
+ serverSideResponse.headers.append(Header::ContentType, "text/plain; charset=UTF-8"_ba);
+ serverSideResponse.body = encUTF8(sourceString);
+ VERIFY_TEXT_REPLY_OK;
+
+ // Successful UTF-8 (obfuscated)
+ serverSideResponse.headers.removeAll(Header::ContentType);
+ serverSideResponse.headers.append(Header::ContentType, "text/plain; charset=\"UT\\F-8\""_ba);
+ serverSideResponse.body = encUTF8(sourceString);
+ VERIFY_TEXT_REPLY_OK;
+
+ // Successful UTF-8 (empty charset)
+ serverSideResponse.headers.removeAll(Header::ContentType);
+ serverSideResponse.headers.append(Header::ContentType, "text/plain; charset=\"\""_ba);
+ serverSideResponse.body = encUTF8(sourceString);
+ VERIFY_TEXT_REPLY_OK;
+
+ // Successful UTF-8 (implicit)
+ serverSideResponse.headers.removeAll(Header::ContentType);
+ serverSideResponse.headers.append(Header::ContentType, "text/plain"_ba);
+ serverSideResponse.body = encUTF8(sourceString);
+ VERIFY_TEXT_REPLY_OK;
+
+ // Successful UTF-16
+ serverSideResponse.headers.removeAll(Header::ContentType);
+ serverSideResponse.headers.append(Header::ContentType, "text/plain; charset=UTF-16"_ba);
+ serverSideResponse.body = encUTF16(sourceString);
+ VERIFY_TEXT_REPLY_OK;
+
+ // Successful UTF-16, parameter case insensitivity
+ serverSideResponse.headers.removeAll(Header::ContentType);
+ serverSideResponse.headers.append(Header::ContentType, "text/plain; chARset=uTf-16"_ba);
+ serverSideResponse.body = encUTF16(sourceString);
+ VERIFY_TEXT_REPLY_OK;
+
+ // Successful UTF-32
+ serverSideResponse.headers.removeAll(Header::ContentType);
+ serverSideResponse.headers.append(Header::ContentType, "text/plain; charset=UTF-32"_ba);
+ serverSideResponse.body = encUTF32(sourceString);
+ VERIFY_TEXT_REPLY_OK;
+
+ // Successful UTF-32 with spec-wise allowed extra trailing content in the Content-Type header value
+ serverSideResponse.headers.removeAll(Header::ContentType);
+ serverSideResponse.headers.append(Header::ContentType,
+ "text/plain; charset = \"UTF-32\";extraparameter=bar"_ba);
+ serverSideResponse.body = encUTF32(sourceString);
+ VERIFY_TEXT_REPLY_OK;
+
+ // Successful UTF-32 with spec-wise allowed extra leading content in the Content-Type header value
+ serverSideResponse.headers.removeAll(Header::ContentType);
+ serverSideResponse.headers.append(Header::ContentType,
+ "text/plain; extraparameter=bar;charset = \"UT\\F-32\""_ba);
+ serverSideResponse.body = encUTF32(sourceString);
+ VERIFY_TEXT_REPLY_OK;
+
+ {
+ // Unsuccessful UTF-32, wrong encoding indicated (indicated UTF-32 but data is UTF-8)
+ serverSideResponse.headers.removeAll(Header::ContentType);
+ serverSideResponse.headers.append(Header::ContentType, "text/plain; charset=UTF-32"_ba);
+ serverSideResponse.body = encUTF8(sourceString);
+ manager.get(request, this, [&](QRestReply &reply) { networkReply = reply.networkReply(); });
+ QTRY_VERIFY(networkReply);
+ QRestReply restReply(networkReply);
+ responseString = restReply.readText();
+ QCOMPARE_NE(responseString, sourceString);
+ networkReply->deleteLater();
+ networkReply = nullptr;
+ }
+
+ // Unsupported encoding
+ serverSideResponse.headers.removeAll(Header::ContentType);
+ serverSideResponse.headers.append(Header::ContentType, "text/plain; charset=foo"_ba);
+ serverSideResponse.body = encUTF8(sourceString);
+ VERIFY_TEXT_REPLY_ERROR("readText(): Charset \"foo\" is not supported")
+
+ // Broken UTF-8
+ serverSideResponse.headers.removeAll(Header::ContentType);
+ serverSideResponse.headers.append(Header::ContentType, "text/plain; charset=UTF-8"_ba);
+ serverSideResponse.body = "\xF0\x28\x8C\x28\xA0\xB0\xC0\xD0"; // invalid characters
+ VERIFY_TEXT_REPLY_ERROR("readText(): Decoding error occurred");
+}
+
+void tst_QRestAccessManager::textStreaming()
+{
+ // Tests textual data received in chunks
+ QNetworkAccessManager qnam;
+ QRestAccessManager manager(&qnam);
+ HttpTestServer server;
+ QTRY_VERIFY(server.isListening());
+ QNetworkRequest request(server.url());
+
+ // Create long text data
+ const QString expectedData = u"사랑abcd€fghiklmnΩpqrstuvwx愛사랑A사랑BCD€FGHIJKLMNΩPQRsTUVWXYZ愛"_s;
+ QString cumulativeReceivedText;
+ QStringEncoder encUTF8("UTF-8");
+ ResponseControl *responseControl = nullptr;
+
+ HttpData serverSideResponse; // The response data the server responds with
+ serverSideResponse.headers.removeAll(Header::ContentType);
+ serverSideResponse.headers.append(Header::ContentType, "text/plain; charset=UTF-8"_ba);
+ serverSideResponse.body = encUTF8(expectedData);
+ serverSideResponse.status = 200;
+
+ server.setHandler([&](const HttpData &, HttpData &response, ResponseControl &control) {
+ response = serverSideResponse;
+ responseControl = &control; // store for later
+ control.responseChunkSize = 5; // tell testserver to send data in chunks of this size
+ });
+
+ {
+ QRestReply restReply(manager.get(request));
+ QObject::connect(restReply.networkReply(), &QNetworkReply::readyRead, this, [&]() {
+ cumulativeReceivedText += restReply.readText();
+ // Tell testserver that test is ready for next chunk
+ responseControl->readyForNextChunk = true;
+ });
+ QTRY_VERIFY(restReply.networkReply()->isFinished());
+ QCOMPARE(cumulativeReceivedText, expectedData);
+ restReply.networkReply()->deleteLater();
+ }
+
+ {
+ cumulativeReceivedText.clear();
+ // Broken UTF-8 characters after first five ok characters
+ serverSideResponse.body =
+ "12345"_ba + "\xF0\x28\x8C\x28\xA0\xB0\xC0\xD0" + "abcde"_ba;
+ QRestReply restReply(manager.get(request));
+ QObject::connect(restReply.networkReply(), &QNetworkReply::readyRead, this, [&]() {
+ static bool firstTime = true;
+ if (!firstTime) // First text part is without warnings
+ QTest::ignoreMessage(QtWarningMsg, "readText(): Decoding error occurred");
+ firstTime = false;
+ cumulativeReceivedText += restReply.readText();
+ // Tell testserver that test is ready for next chunk
+ responseControl->readyForNextChunk = true;
+ });
+ QTRY_VERIFY(restReply.networkReply()->isFinished());
+ QCOMPARE(cumulativeReceivedText, "12345"_ba);
+ restReply.networkReply()->deleteLater();
+ }
+}
+
+QTEST_MAIN(tst_QRestAccessManager)
+#include "tst_qrestaccessmanager.moc"