summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
Diffstat (limited to 'src/network')
-rw-r--r--src/network/CMakeLists.txt123
-rw-r--r--src/network/access/http2/hpack.cpp49
-rw-r--r--src/network/access/http2/hpack_p.h3
-rw-r--r--src/network/access/http2/hpacktable.cpp9
-rw-r--r--src/network/access/http2/http2frames.cpp13
-rw-r--r--src/network/access/http2/http2frames_p.h19
-rw-r--r--src/network/access/http2/http2protocol.cpp50
-rw-r--r--src/network/access/http2/http2protocol_p.h6
-rw-r--r--src/network/access/qdecompresshelper.cpp26
-rw-r--r--src/network/access/qdecompresshelper_p.h4
-rw-r--r--src/network/access/qhsts.cpp62
-rw-r--r--src/network/access/qhsts_p.h6
-rw-r--r--src/network/access/qhstspolicy.cpp4
-rw-r--r--src/network/access/qhstsstore.cpp2
-rw-r--r--src/network/access/qhttp1configuration.cpp154
-rw-r--r--src/network/access/qhttp1configuration.h62
-rw-r--r--src/network/access/qhttp2configuration.cpp2
-rw-r--r--src/network/access/qhttp2configuration.h2
-rw-r--r--src/network/access/qhttp2connection.cpp1752
-rw-r--r--src/network/access/qhttp2connection_p.h372
-rw-r--r--src/network/access/qhttp2protocolhandler.cpp362
-rw-r--r--src/network/access/qhttp2protocolhandler_p.h5
-rw-r--r--src/network/access/qhttpheaderparser.cpp44
-rw-r--r--src/network/access/qhttpheaderparser_p.h15
-rw-r--r--src/network/access/qhttpheaders.cpp1551
-rw-r--r--src/network/access/qhttpheaders.h281
-rw-r--r--src/network/access/qhttpmultipart.cpp43
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp212
-rw-r--r--src/network/access/qhttpnetworkconnection_p.h39
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp68
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel_p.h1
-rw-r--r--src/network/access/qhttpnetworkheader.cpp6
-rw-r--r--src/network/access/qhttpnetworkheader_p.h11
-rw-r--r--src/network/access/qhttpnetworkreply.cpp56
-rw-r--r--src/network/access/qhttpnetworkreply_p.h32
-rw-r--r--src/network/access/qhttpnetworkrequest.cpp16
-rw-r--r--src/network/access/qhttpnetworkrequest_p.h4
-rw-r--r--src/network/access/qhttpprotocolhandler.cpp12
-rw-r--r--src/network/access/qhttpthreaddelegate.cpp24
-rw-r--r--src/network/access/qhttpthreaddelegate_p.h26
-rw-r--r--src/network/access/qnetworkaccessbackend.cpp2
-rw-r--r--src/network/access/qnetworkaccesscache.cpp3
-rw-r--r--src/network/access/qnetworkaccesscache_p.h3
-rw-r--r--src/network/access/qnetworkaccesscachebackend.cpp10
-rw-r--r--src/network/access/qnetworkaccessdebugpipebackend.cpp2
-rw-r--r--src/network/access/qnetworkaccessfilebackend.cpp2
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp245
-rw-r--r--src/network/access/qnetworkaccessmanager.h24
-rw-r--r--src/network/access/qnetworkaccessmanager_p.h2
-rw-r--r--src/network/access/qnetworkcookie.cpp105
-rw-r--r--src/network/access/qnetworkcookie.h3
-rw-r--r--src/network/access/qnetworkcookie_p.h8
-rw-r--r--src/network/access/qnetworkcookiejar.cpp69
-rw-r--r--src/network/access/qnetworkdiskcache.cpp118
-rw-r--r--src/network/access/qnetworkdiskcache_p.h17
-rw-r--r--src/network/access/qnetworkreply.cpp14
-rw-r--r--src/network/access/qnetworkreply.h6
-rw-r--r--src/network/access/qnetworkreplyfileimpl.cpp3
-rw-r--r--src/network/access/qnetworkreplyfileimpl_p.h8
-rw-r--r--src/network/access/qnetworkreplyhttpimpl.cpp247
-rw-r--r--src/network/access/qnetworkreplyhttpimpl_p.h6
-rw-r--r--src/network/access/qnetworkreplyimpl.cpp9
-rw-r--r--src/network/access/qnetworkreplyimpl_p.h3
-rw-r--r--src/network/access/qnetworkreplywasmimpl.cpp127
-rw-r--r--src/network/access/qnetworkreplywasmimpl_p.h5
-rw-r--r--src/network/access/qnetworkrequest.cpp330
-rw-r--r--src/network/access/qnetworkrequest.h46
-rw-r--r--src/network/access/qnetworkrequest_p.h2
-rw-r--r--src/network/access/qnetworkrequestfactory.cpp727
-rw-r--r--src/network/access/qnetworkrequestfactory.h100
-rw-r--r--src/network/access/qnetworkrequestfactory_p.h56
-rw-r--r--src/network/access/qrestaccessmanager.cpp828
-rw-r--r--src/network/access/qrestaccessmanager.h127
-rw-r--r--src/network/access/qrestaccessmanager_p.h89
-rw-r--r--src/network/access/qrestreply.cpp560
-rw-r--r--src/network/access/qrestreply.h71
-rw-r--r--src/network/access/qrestreply_p.h40
-rw-r--r--src/network/access/qsocketabstraction_p.h91
-rw-r--r--src/network/android/jar/CMakeLists.txt13
-rw-r--r--src/network/android/jar/build.gradle6
-rw-r--r--src/network/compat/removed_api.cpp67
-rw-r--r--src/network/configure.cmake145
-rw-r--r--src/network/doc/images/network-examples.pngbin8946 -> 0 bytes
-rw-r--r--src/network/doc/images/network-examples.webpbin0 -> 8250 bytes
-rw-r--r--src/network/doc/qtnetwork.qdocconf11
-rw-r--r--src/network/doc/snippets/CMakeLists.txt2
-rw-r--r--src/network/doc/snippets/code/doc_src_qtnetwork.cpp6
-rw-r--r--src/network/doc/snippets/code/src_network_access_qhttpmultipart.cpp2
-rw-r--r--src/network/doc/snippets/code/src_network_access_qhttppart.cpp2
-rw-r--r--src/network/doc/snippets/code/src_network_access_qnetworkdiskcache.cpp9
-rw-r--r--src/network/doc/snippets/code/src_network_access_qnetworkrequestfactory.cpp27
-rw-r--r--src/network/doc/snippets/code/src_network_access_qrestaccessmanager.cpp104
-rw-r--r--src/network/doc/snippets/code/src_network_kernel_qdnslookup.cpp3
-rw-r--r--src/network/doc/snippets/code/src_network_kernel_qhostinfo.cpp12
-rw-r--r--src/network/doc/snippets/code/src_network_kernel_qnetworkinterface.cpp2
-rw-r--r--src/network/doc/snippets/code/src_network_ssl_qsslconfiguration.cpp7
-rw-r--r--src/network/doc/snippets/code/src_network_ssl_qsslpresharedkeyauthenticator.cpp2
-rw-r--r--src/network/doc/snippets/code/src_network_ssl_qsslsocket.cpp10
-rw-r--r--src/network/doc/snippets/network/CMakeLists.txt2
-rw-r--r--src/network/doc/src/examples.qdoc8
-rw-r--r--src/network/doc/src/ssl.qdoc7
-rw-r--r--src/network/kernel/PSL-LICENSE.txt373
-rw-r--r--src/network/kernel/qauthenticator.cpp73
-rw-r--r--src/network/kernel/qauthenticator.h1
-rw-r--r--src/network/kernel/qauthenticator_p.h6
-rw-r--r--src/network/kernel/qdnslookup.cpp238
-rw-r--r--src/network/kernel/qdnslookup.h14
-rw-r--r--src/network/kernel/qdnslookup_android.cpp20
-rw-r--r--src/network/kernel/qdnslookup_dummy.cpp15
-rw-r--r--src/network/kernel/qdnslookup_p.h159
-rw-r--r--src/network/kernel/qdnslookup_unix.cpp543
-rw-r--r--src/network/kernel/qdnslookup_win.cpp150
-rw-r--r--src/network/kernel/qhostaddress.cpp46
-rw-r--r--src/network/kernel/qhostaddress.h3
-rw-r--r--src/network/kernel/qhostinfo.cpp128
-rw-r--r--src/network/kernel/qhostinfo.h67
-rw-r--r--src/network/kernel/qhostinfo_p.h32
-rw-r--r--src/network/kernel/qhostinfo_unix.cpp228
-rw-r--r--src/network/kernel/qnetconmonitor_darwin.mm2
-rw-r--r--src/network/kernel/qnetconmonitor_win.cpp47
-rw-r--r--src/network/kernel/qnetworkdatagram.cpp2
-rw-r--r--src/network/kernel/qnetworkdatagram.h2
-rw-r--r--src/network/kernel/qnetworkinformation.cpp90
-rw-r--r--src/network/kernel/qnetworkinformation.h1
-rw-r--r--src/network/kernel/qnetworkinformation_p.h37
-rw-r--r--src/network/kernel/qnetworkinterface.cpp2
-rw-r--r--src/network/kernel/qnetworkinterface_linux.cpp13
-rw-r--r--src/network/kernel/qnetworkinterface_unix.cpp62
-rw-r--r--src/network/kernel/qnetworkproxy.cpp5
-rw-r--r--src/network/kernel/qnetworkproxy.h1
-rw-r--r--src/network/kernel/qnetworkproxy_android.cpp2
-rw-r--r--src/network/kernel/qnetworkproxy_darwin.cpp (renamed from src/network/kernel/qnetworkproxy_mac.cpp)133
-rw-r--r--src/network/kernel/qnetworkproxy_libproxy.cpp3
-rw-r--r--src/network/kernel/qnetworkproxy_win.cpp4
-rw-r--r--src/network/kernel/qt_attribution.json28
-rw-r--r--src/network/kernel/qtldurl.cpp6
-rw-r--r--src/network/kernel/qtldurl_p.h3
-rw-r--r--src/network/kernel/qtnetworkglobal_p.h1
-rw-r--r--src/network/kernel/qurltlds_p.h4477
-rw-r--r--src/network/kernel/qurltlds_p.h.INFO17
-rw-r--r--src/network/qt_cmdline.cmake5
-rw-r--r--src/network/socket/qabstractsocket.cpp64
-rw-r--r--src/network/socket/qabstractsocket.h4
-rw-r--r--src/network/socket/qabstractsocket_p.h1
-rw-r--r--src/network/socket/qabstractsocketengine_p.h16
-rw-r--r--src/network/socket/qhttpsocketengine.cpp88
-rw-r--r--src/network/socket/qhttpsocketengine_p.h19
-rw-r--r--src/network/socket/qlocalserver.cpp20
-rw-r--r--src/network/socket/qlocalserver.h1
-rw-r--r--src/network/socket/qlocalserver_unix.cpp3
-rw-r--r--src/network/socket/qlocalsocket_unix.cpp22
-rw-r--r--src/network/socket/qnativesocketengine.cpp38
-rw-r--r--src/network/socket/qnativesocketengine_p.h222
-rw-r--r--src/network/socket/qnativesocketengine_p_p.h189
-rw-r--r--src/network/socket/qnativesocketengine_unix.cpp54
-rw-r--r--src/network/socket/qnativesocketengine_win.cpp47
-rw-r--r--src/network/socket/qnet_unix_p.h11
-rw-r--r--src/network/socket/qsctpserver.cpp2
-rw-r--r--src/network/socket/qsctpserver.h2
-rw-r--r--src/network/socket/qsctpsocket.cpp5
-rw-r--r--src/network/socket/qsctpsocket.h2
-rw-r--r--src/network/socket/qsocks5socketengine.cpp78
-rw-r--r--src/network/socket/qsocks5socketengine_p.h17
-rw-r--r--src/network/socket/qtcpserver.cpp10
-rw-r--r--src/network/socket/qtcpsocket.cpp6
-rw-r--r--src/network/socket/qtcpsocket.h2
-rw-r--r--src/network/socket/qudpsocket.cpp4
-rw-r--r--src/network/socket/qudpsocket.h2
-rw-r--r--src/network/ssl/qdtls.h2
-rw-r--r--src/network/ssl/qocspresponse.h2
-rw-r--r--src/network/ssl/qpassworddigestor.cpp100
-rw-r--r--src/network/ssl/qssl.cpp2
-rw-r--r--src/network/ssl/qssl.h13
-rw-r--r--src/network/ssl/qsslcertificate.cpp72
-rw-r--r--src/network/ssl/qsslcertificate.h2
-rw-r--r--src/network/ssl/qsslcertificate_p.h4
-rw-r--r--src/network/ssl/qsslconfiguration.cpp41
-rw-r--r--src/network/ssl/qsslconfiguration.h2
-rw-r--r--src/network/ssl/qssldiffiehellmanparameters.cpp16
-rw-r--r--src/network/ssl/qsslkey.h2
-rw-r--r--src/network/ssl/qsslpresharedkeyauthenticator.h1
-rw-r--r--src/network/ssl/qsslserver.cpp15
-rw-r--r--src/network/ssl/qsslsocket.cpp42
-rw-r--r--src/network/ssl/qsslsocket.h2
-rw-r--r--src/network/ssl/qtlsbackend.cpp36
-rw-r--r--src/network/ssl/qtlsbackend_p.h18
186 files changed, 10733 insertions, 7690 deletions
diff --git a/src/network/CMakeLists.txt b/src/network/CMakeLists.txt
index 166559764a..3f226a77dc 100644
--- a/src/network/CMakeLists.txt
+++ b/src/network/CMakeLists.txt
@@ -1,7 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-# Generated from network.pro.
+# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## Network Module:
@@ -22,12 +20,14 @@ qt_internal_add_module(Network
access/qnetworkcookie.cpp access/qnetworkcookie.h access/qnetworkcookie_p.h
access/qnetworkcookiejar.cpp access/qnetworkcookiejar.h access/qnetworkcookiejar_p.h
access/qnetworkfile.cpp access/qnetworkfile_p.h
+ access/qhttpheaders.cpp access/qhttpheaders.h
access/qhttpheaderparser.cpp access/qhttpheaderparser_p.h
access/qnetworkreply.cpp access/qnetworkreply.h access/qnetworkreply_p.h
access/qnetworkreplydataimpl.cpp access/qnetworkreplydataimpl_p.h
access/qnetworkreplyfileimpl.cpp access/qnetworkreplyfileimpl_p.h
access/qnetworkreplyimpl.cpp access/qnetworkreplyimpl_p.h
access/qnetworkrequest.cpp access/qnetworkrequest.h access/qnetworkrequest_p.h
+ compat/removed_api.cpp
kernel/qauthenticator.cpp kernel/qauthenticator.h kernel/qauthenticator_p.h
kernel/qhostaddress.cpp kernel/qhostaddress.h kernel/qhostaddress_p.h
kernel/qhostinfo.cpp kernel/qhostinfo.h kernel/qhostinfo_p.h
@@ -40,7 +40,7 @@ qt_internal_add_module(Network
kernel/qtnetworkglobal.h kernel/qtnetworkglobal_p.h
socket/qabstractsocket.cpp socket/qabstractsocket.h socket/qabstractsocket_p.h
socket/qabstractsocketengine.cpp socket/qabstractsocketengine_p.h
- socket/qnativesocketengine.cpp socket/qnativesocketengine_p.h
+ socket/qnativesocketengine.cpp socket/qnativesocketengine_p.h socket/qnativesocketengine_p_p.h
socket/qtcpserver.cpp socket/qtcpserver.h socket/qtcpserver_p.h
socket/qtcpsocket.cpp socket/qtcpsocket.h socket/qtcpsocket_p.h
socket/qudpsocket.cpp socket/qudpsocket.h
@@ -55,8 +55,14 @@ qt_internal_add_module(Network
ssl/qsslsocket.h
ssl/qtlsbackend.cpp ssl/qtlsbackend_p.h
DEFINES
+ QT_NO_CONTEXTLESS_CONNECT
QT_NO_FOREACH
QT_NO_USING_NAMESPACE
+ QT_NO_CAST_FROM_ASCII
+ QT_NO_CAST_TO_ASCII
+ QT_NO_CAST_FROM_BYTEARRAY
+ QT_NO_URL_CAST_FROM_STRING
+ QT_USE_NODISCARD_FILE_OPEN
INCLUDE_DIRECTORIES
kernel
LIBRARIES
@@ -65,15 +71,13 @@ qt_internal_add_module(Network
Qt::Core
PRIVATE_MODULE_INTERFACE
Qt::CorePrivate
+ NO_PCH_SOURCES
+ compat/removed_api.cpp
PRECOMPILED_HEADER
"../corelib/global/qt_pch.h"
GENERATE_CPP_EXPORTS
- GENERATE_PRIVATE_CPP_EXPORTS
)
-#### Keys ignored in scope 1:.:.:network.pro:<TRUE>:
-# QMAKE_LIBS = "$$QMAKE_LIBS_NETWORK"
-
## Scopes:
#####################################################################
@@ -121,7 +125,9 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_http
access/http2/huffman.cpp access/http2/huffman_p.h
access/qabstractprotocolhandler.cpp access/qabstractprotocolhandler_p.h
access/qdecompresshelper.cpp access/qdecompresshelper_p.h
+ access/qhttp1configuration.cpp access/qhttp1configuration.h
access/qhttp2configuration.cpp access/qhttp2configuration.h
+ access/qhttp2connection.cpp access/qhttp2connection_p.h
access/qhttp2protocolhandler.cpp access/qhttp2protocolhandler_p.h
access/qhttpmultipart.cpp access/qhttpmultipart.h access/qhttpmultipart_p.h
access/qhttpnetworkconnection.cpp access/qhttpnetworkconnection_p.h
@@ -132,6 +138,11 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_http
access/qhttpprotocolhandler.cpp access/qhttpprotocolhandler_p.h
access/qhttpthreaddelegate.cpp access/qhttpthreaddelegate_p.h
access/qnetworkreplyhttpimpl.cpp access/qnetworkreplyhttpimpl_p.h
+ access/qnetworkrequestfactory.cpp access/qnetworkrequestfactory_p.h
+ access/qnetworkrequestfactory.h
+ access/qrestaccessmanager.cpp access/qrestaccessmanager.h access/qrestaccessmanager_p.h
+ access/qrestreply.cpp access/qrestreply.h access/qrestreply_p.h
+ access/qsocketabstraction_p.h
socket/qhttpsocketengine.cpp socket/qhttpsocketengine_p.h
)
@@ -163,8 +174,9 @@ qt_internal_extend_target(Network CONDITION NOT QT_FEATURE_system_zlib AND NOT n
qt_internal_extend_target(Network CONDITION QT_FEATURE_topleveldomain
SOURCES
kernel/qtldurl.cpp kernel/qtldurl_p.h
- kernel/qurltlds_p.h
../3rdparty/libpsl/src/lookup_string_in_fixed_set.c
+ INCLUDE_DIRECTORIES
+ ../3rdparty/libpsl
)
qt_internal_extend_target(Network CONDITION QT_FEATURE_dnslookup
@@ -179,11 +191,6 @@ qt_internal_extend_target(Network CONDITION UNIX
socket/qnet_unix_p.h
)
-qt_internal_extend_target(Network CONDITION QT_FEATURE_dlopen AND UNIX
- LIBRARIES
- ${CMAKE_DL_LIBS}
-)
-
qt_internal_extend_target(Network CONDITION QT_FEATURE_linux_netlink AND UNIX
SOURCES
kernel/qnetworkinterface_linux.cpp
@@ -194,11 +201,6 @@ qt_internal_extend_target(Network CONDITION UNIX AND NOT QT_FEATURE_linux_netlin
kernel/qnetworkinterface_unix.cpp
)
-qt_internal_extend_target(Network CONDITION ANDROID AND QT_FEATURE_dnslookup
- SOURCES
- kernel/qdnslookup_android.cpp
-)
-
qt_internal_extend_target(Network CONDITION WIN32
SOURCES
kernel/qhostinfo_win.cpp
@@ -211,6 +213,26 @@ qt_internal_extend_target(Network CONDITION WIN32
iphlpapi
secur32
winhttp
+ DEFINES
+ NOMINMAX
+)
+
+qt_internal_extend_target(Network CONDITION APPLE AND NOT UIKIT
+ LIBRARIES
+ ${FWCoreServices}
+ ${FWSystemConfiguration}
+)
+
+qt_internal_extend_target(Network CONDITION APPLE
+ LIBRARIES
+ ${FWCFNetwork}
+)
+
+qt_internal_extend_target(Network CONDITION QT_FEATURE_dnslookup AND QT_FEATURE_libresolv
+ SOURCES
+ kernel/qdnslookup_unix.cpp
+ LIBRARIES
+ WrapResolv::WrapResolv
)
qt_internal_extend_target(Network CONDITION QT_FEATURE_dnslookup AND WIN32
@@ -218,13 +240,12 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_dnslookup AND WIN32
kernel/qdnslookup_win.cpp
)
-qt_internal_extend_target(Network CONDITION APPLE AND NOT UIKIT
- LIBRARIES
- ${FWCoreServices}
- ${FWSystemConfiguration}
+qt_internal_extend_target(Network CONDITION QT_FEATURE_dnslookup AND NOT QT_FEATURE_libresolv AND NOT WIN32
+ SOURCES
+ kernel/qdnslookup_dummy.cpp
)
-qt_internal_extend_target(Network CONDITION IOS OR MACOS
+qt_internal_extend_target(Network CONDITION APPLE
SOURCES
kernel/qnetconmonitor_darwin.mm
LIBRARIES
@@ -236,7 +257,7 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_networklistmanager AND NO
kernel/qnetconmonitor_win.cpp
)
-qt_internal_extend_target(Network CONDITION NOT IOS AND NOT MACOS AND NOT QT_FEATURE_networklistmanager
+qt_internal_extend_target(Network CONDITION NOT APPLE AND NOT QT_FEATURE_networklistmanager
SOURCES
kernel/qnetconmonitor_stub.cpp
)
@@ -251,9 +272,9 @@ qt_internal_extend_target(Network CONDITION UIKIT
kernel/qnetworkinterface_uikit_p.h
)
-qt_internal_extend_target(Network CONDITION MACOS
+qt_internal_extend_target(Network CONDITION APPLE AND NOT VISIONOS
SOURCES
- kernel/qnetworkproxy_mac.cpp
+ kernel/qnetworkproxy_darwin.cpp
)
qt_internal_extend_target(Network CONDITION QT_FEATURE_libproxy AND UNIX AND NOT MACOS
@@ -264,19 +285,19 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_libproxy AND UNIX AND NOT
PkgConfig::Libproxy
)
-qt_internal_extend_target(Network CONDITION ANDROID # special case
+qt_internal_extend_target(Network CONDITION ANDROID
SOURCES
kernel/qnetworkproxy_android.cpp
)
-qt_internal_extend_target(Network CONDITION UNIX AND NOT ANDROID AND NOT MACOS AND NOT QT_FEATURE_libproxy AND (UNIX OR WINRT) # special case
+qt_internal_extend_target(Network CONDITION UNIX AND NOT ANDROID AND NOT (APPLE AND NOT VISIONOS) AND NOT QT_FEATURE_libproxy AND (UNIX OR WINRT)
SOURCES
kernel/qnetworkproxy_generic.cpp
)
if(ANDROID AND (ANDROID))
set_property(TARGET Network APPEND PROPERTY QT_ANDROID_BUNDLED_JAR_DEPENDENCIES
- jar/Qt${QtBase_VERSION_MAJOR}AndroidNetwork.jar # special case
+ jar/Qt${QtBase_VERSION_MAJOR}AndroidNetwork.jar
)
endif()
@@ -322,6 +343,11 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_localserver AND WIN32
socket/qlocalsocket_win.cpp
)
+qt_internal_extend_target(Network CONDITION QT_FEATURE_openssl_linked AND QT_FEATURE_opensslv30
+ LIBRARIES
+ WrapOpenSSL::WrapOpenSSL
+)
+
qt_internal_extend_target(Network CONDITION QT_FEATURE_system_proxies
DEFINES
QT_USE_SYSTEM_PROXIES
@@ -351,21 +377,44 @@ qt_internal_extend_target(Network CONDITION QT_FEATURE_ocsp AND QT_FEATURE_opens
ssl/qocsp_p.h
)
-qt_internal_extend_target(Network CONDITION QT_FEATURE_dnslookup AND UNIX AND NOT ANDROID
- SOURCES
- kernel/qdnslookup_unix.cpp
-)
qt_internal_add_docs(Network
doc/qtnetwork.qdocconf
)
-qt_internal_extend_target(Network CONDITION WIN32 PUBLIC_LIBRARIES ws2_32) # special case: mkspecs/common/msvc-desktop.conf
+# See mkspecs/common/msvc-desktop.conf
+qt_internal_extend_target(Network CONDITION WIN32 PUBLIC_LIBRARIES ws2_32)
-qt_internal_extend_target(Network CONDITION QNX PUBLIC_LIBRARIES socket) # special case: mkspecs/common/qcc-base-qnx.conf
+# See mkspecs/common/qcc-base-qnx.conf
+qt_internal_extend_target(Network CONDITION QNX PUBLIC_LIBRARIES socket)
-qt_internal_extend_target(Network CONDITION SOLARIS PUBLIC_LIBRARIES socket nsl) # special case
+qt_internal_extend_target(Network CONDITION SOLARIS PUBLIC_LIBRARIES socket nsl)
+
+qt_internal_extend_target(Network CONDITION WIN32
+ NO_UNITY_BUILD_SOURCES
+ kernel/qauthenticator.cpp
+ kernel/qdnslookup_win.cpp
+ kernel/qhostaddress.cpp
+ kernel/qhostinfo.cpp
+ kernel/qhostinfo_win.cpp
+ kernel/qnetconmonitor_win.cpp
+ kernel/qnetworkinterface_win.cpp
+ kernel/qnetworkproxy_win.cpp
+ socket/qabstractsocket.cpp
+ socket/qlocalserver.cpp
+ socket/qlocalserver_win.cpp
+ socket/qlocalsocket_win.cpp
+ socket/qnativesocketengine.cpp
+ socket/qnativesocketengine_win.cpp
+)
# include the snippet projects for developer-builds
if(QT_FEATURE_private_tests)
add_subdirectory(doc/snippets/network)
endif()
+qt_internal_extend_target(Network
+ # Workaround for QTBUG-118229:
+ # Function called by inline methods taking a pointer to a private class as a parameter
+ EXTRA_LINKER_SCRIPT_EXPORTS
+ # QNetworkDatagram::destroy(QNetworkDatagramPrivate *d)
+ "_ZN*16QNetworkDatagram7destroyEP*23QNetworkDatagramPrivate*"
+)
diff --git a/src/network/access/http2/hpack.cpp b/src/network/access/http2/hpack.cpp
index 58af04bbb5..9e970dda53 100644
--- a/src/network/access/http2/hpack.cpp
+++ b/src/network/access/http2/hpack.cpp
@@ -91,7 +91,7 @@ bool read_bit_pattern(const BitPattern &pattern, BitIStream &inputStream)
return true;
}
-bool is_request_pseudo_header(const QByteArray &name)
+bool is_request_pseudo_header(QByteArrayView name)
{
return name == ":method" || name == ":scheme" ||
name == ":authority" || name == ":path";
@@ -194,8 +194,8 @@ bool Encoder::encodeRequestPseudoHeaders(BitOStream &outputStream,
using size_type = decltype(header.size());
bool methodFound = false;
- const char *headerName[] = {":authority", ":scheme", ":path"};
- const size_type nHeaders = sizeof headerName / sizeof headerName[0];
+ constexpr QByteArrayView headerName[] = {":authority", ":scheme", ":path"};
+ constexpr size_type nHeaders = std::size(headerName);
bool headerFound[nHeaders] = {};
for (const auto &field : header) {
@@ -504,6 +504,49 @@ void Decoder::handleStreamError(BitIStream &inputStream)
// HTTP2 layer will end with session error/COMPRESSION_ERROR.
}
+std::optional<QUrl> makePromiseKeyUrl(const HttpHeader &requestHeader)
+{
+ constexpr QByteArrayView names[] = { ":authority", ":method", ":path", ":scheme" };
+ enum PseudoHeaderEnum
+ {
+ Authority,
+ Method,
+ Path,
+ Scheme
+ };
+ std::array<std::optional<QByteArrayView>, std::size(names)> pseudoHeaders{};
+ for (const auto &field : requestHeader) {
+ const auto *it = std::find(std::begin(names), std::end(names), QByteArrayView(field.name));
+ if (it != std::end(names)) {
+ const auto index = std::distance(std::begin(names), it);
+ if (field.value.isEmpty() || pseudoHeaders.at(index).has_value())
+ return {};
+ pseudoHeaders[index] = field.value;
+ }
+ }
+
+ auto optionalIsSet = [](const auto &x) { return x.has_value(); };
+ if (!std::all_of(pseudoHeaders.begin(), pseudoHeaders.end(), optionalIsSet)) {
+ // All four required, HTTP/2 8.1.2.3.
+ return {};
+ }
+
+ const QByteArrayView method = pseudoHeaders[Method].value();
+ if (method.compare("get", Qt::CaseInsensitive) != 0 &&
+ method.compare("head", Qt::CaseInsensitive) != 0) {
+ return {};
+ }
+
+ QUrl url;
+ url.setScheme(QLatin1StringView(pseudoHeaders[Scheme].value()));
+ url.setAuthority(QLatin1StringView(pseudoHeaders[Authority].value()));
+ url.setPath(QLatin1StringView(pseudoHeaders[Path].value()));
+
+ if (!url.isValid())
+ return {};
+ return url;
+}
+
}
QT_END_NAMESPACE
diff --git a/src/network/access/http2/hpack_p.h b/src/network/access/http2/hpack_p.h
index 75693da73c..b407b81941 100644
--- a/src/network/access/http2/hpack_p.h
+++ b/src/network/access/http2/hpack_p.h
@@ -18,8 +18,10 @@
#include "hpacktable_p.h"
#include <QtCore/qglobal.h>
+#include <QtCore/qurl.h>
#include <vector>
+#include <optional>
QT_BEGIN_NAMESPACE
@@ -112,6 +114,7 @@ private:
FieldLookupTable lookupTable;
};
+std::optional<QUrl> makePromiseKeyUrl(const HttpHeader &requestHeader);
}
QT_END_NAMESPACE
diff --git a/src/network/access/http2/hpacktable.cpp b/src/network/access/http2/hpacktable.cpp
index 0b69ee86a9..2c728b37e3 100644
--- a/src/network/access/http2/hpacktable.cpp
+++ b/src/network/access/http2/hpacktable.cpp
@@ -26,8 +26,10 @@ HeaderSize entry_size(QByteArrayView name, QByteArrayView value)
// for counting the number of references to the name and value would have
// 32 octets of overhead."
- const unsigned sum = unsigned(name.size() + value.size());
- if (std::numeric_limits<unsigned>::max() - 32 < sum)
+ size_t sum;
+ if (qAddOverflow(size_t(name.size()), size_t(value.size()), &sum))
+ return HeaderSize();
+ if (sum > (std::numeric_limits<unsigned>::max() - 32))
return HeaderSize();
return HeaderSize(true, quint32(sum + 32));
}
@@ -346,8 +348,7 @@ quint32 FieldLookupTable::indexOfChunk(const Chunk *chunk) const
return quint32(i);
}
- Q_UNREACHABLE();
- return 0;
+ Q_UNREACHABLE_RETURN(0);
}
quint32 FieldLookupTable::keyToIndex(const SearchEntry &key) const
diff --git a/src/network/access/http2/http2frames.cpp b/src/network/access/http2/http2frames.cpp
index 0c70c98ef8..e07c96b803 100644
--- a/src/network/access/http2/http2frames.cpp
+++ b/src/network/access/http2/http2frames.cpp
@@ -135,6 +135,7 @@ FrameStatus Frame::validateHeader() const
// 6.6 PUSH_PROMISE
if (framePayloadSize < 4)
return FrameStatus::sizeError;
+ break;
default:
// DATA/HEADERS/CONTINUATION will be verified
// when we have payload.
@@ -258,7 +259,7 @@ const uchar *Frame::hpackBlockBegin() const
return begin;
}
-FrameStatus FrameReader::read(QAbstractSocket &socket)
+FrameStatus FrameReader::read(QIODevice &socket)
{
if (offset < frameHeaderSize) {
if (!readHeader(socket))
@@ -286,7 +287,7 @@ FrameStatus FrameReader::read(QAbstractSocket &socket)
return frame.validatePayload();
}
-bool FrameReader::readHeader(QAbstractSocket &socket)
+bool FrameReader::readHeader(QIODevice &socket)
{
Q_ASSERT(offset < frameHeaderSize);
@@ -302,7 +303,7 @@ bool FrameReader::readHeader(QAbstractSocket &socket)
return offset == frameHeaderSize;
}
-bool FrameReader::readPayload(QAbstractSocket &socket)
+bool FrameReader::readPayload(QIODevice &socket)
{
Q_ASSERT(offset < frame.buffer.size());
Q_ASSERT(frame.buffer.size() > frameHeaderSize);
@@ -393,7 +394,7 @@ void FrameWriter::updatePayloadSize()
setPayloadSize(size);
}
-bool FrameWriter::write(QAbstractSocket &socket) const
+bool FrameWriter::write(QIODevice &socket) const
{
auto &buffer = frame.buffer;
Q_ASSERT(buffer.size() >= frameHeaderSize);
@@ -407,7 +408,7 @@ bool FrameWriter::write(QAbstractSocket &socket) const
return nWritten != -1 && size_type(nWritten) == buffer.size();
}
-bool FrameWriter::writeHEADERS(QAbstractSocket &socket, quint32 sizeLimit)
+bool FrameWriter::writeHEADERS(QIODevice &socket, quint32 sizeLimit)
{
auto &buffer = frame.buffer;
Q_ASSERT(buffer.size() >= frameHeaderSize);
@@ -457,7 +458,7 @@ bool FrameWriter::writeHEADERS(QAbstractSocket &socket, quint32 sizeLimit)
return true;
}
-bool FrameWriter::writeDATA(QAbstractSocket &socket, quint32 sizeLimit,
+bool FrameWriter::writeDATA(QIODevice &socket, quint32 sizeLimit,
const uchar *src, quint32 size)
{
// With DATA frame(s) we always have:
diff --git a/src/network/access/http2/http2frames_p.h b/src/network/access/http2/http2frames_p.h
index be87f43fbe..48e3f751b7 100644
--- a/src/network/access/http2/http2frames_p.h
+++ b/src/network/access/http2/http2frames_p.h
@@ -27,7 +27,7 @@
QT_BEGIN_NAMESPACE
class QHttp2ProtocolHandler;
-class QAbstractSocket;
+class QIODevice;
namespace Http2
{
@@ -65,15 +65,15 @@ struct Q_AUTOTEST_EXPORT Frame
class Q_AUTOTEST_EXPORT FrameReader
{
public:
- FrameStatus read(QAbstractSocket &socket);
+ FrameStatus read(QIODevice &socket);
Frame &inboundFrame()
{
return frame;
}
private:
- bool readHeader(QAbstractSocket &socket);
- bool readPayload(QAbstractSocket &socket);
+ bool readHeader(QIODevice &socket);
+ bool readPayload(QIODevice &socket);
quint32 offset = 0;
Frame frame;
@@ -123,20 +123,25 @@ public:
{
append(&payload[0], &payload[0] + payload.size());
}
+ void append(QByteArrayView payload)
+ {
+ append(reinterpret_cast<const uchar *>(payload.begin()),
+ reinterpret_cast<const uchar *>(payload.end()));
+ }
void append(const uchar *begin, const uchar *end);
// Write as a single frame:
- bool write(QAbstractSocket &socket) const;
+ bool write(QIODevice &socket) const;
// Two types of frames we are sending are affected by frame size limits:
// HEADERS and DATA. HEADERS' payload (hpacked HTTP headers, following a
// frame header) is always in our 'buffer', we send the initial HEADERS
// frame first and then CONTINUTATION frame(s) if needed:
- bool writeHEADERS(QAbstractSocket &socket, quint32 sizeLimit);
+ bool writeHEADERS(QIODevice &socket, quint32 sizeLimit);
// With DATA frames the actual payload is never in our 'buffer', it's a
// 'readPointer' from QNonContiguousData. We split this payload as needed
// into DATA frames with correct payload size fitting into frame size limit:
- bool writeDATA(QAbstractSocket &socket, quint32 sizeLimit,
+ bool writeDATA(QIODevice &socket, quint32 sizeLimit,
const uchar *src, quint32 size);
private:
void updatePayloadSize();
diff --git a/src/network/access/http2/http2protocol.cpp b/src/network/access/http2/http2protocol.cpp
index 966f294e81..8e7e176c41 100644
--- a/src/network/access/http2/http2protocol.cpp
+++ b/src/network/access/http2/http2protocol.cpp
@@ -76,12 +76,10 @@ void appendProtocolUpgradeHeaders(const QHttp2Configuration &config, QHttpNetwor
Q_ASSERT(request);
// RFC 2616, 14.10
// RFC 7540, 3.2
- QByteArray value(request->headerField("Connection"));
+ const QByteArray connectionHeader = request->headerField("Connection");
+ const auto separator = connectionHeader.isEmpty() ? QByteArrayView() : QByteArrayView(", ");
// We _append_ 'Upgrade':
- if (value.size())
- value += ", ";
-
- value += "Upgrade, HTTP2-Settings";
+ QByteArray value = connectionHeader + separator + "Upgrade, HTTP2-Settings";
request->setHeaderField("Connection", value);
// This we just (re)write.
request->setHeaderField("Upgrade", "h2c");
@@ -186,19 +184,45 @@ QNetworkReply::NetworkError qt_error(quint32 errorCode)
bool is_protocol_upgraded(const QHttpNetworkReply &reply)
{
- if (reply.statusCode() == 101) {
- // Do some minimal checks here - we expect 'Upgrade: h2c' to be found.
- const auto &header = reply.header();
- for (const QPair<QByteArray, QByteArray> &field : header) {
- if (field.first.compare("upgrade", Qt::CaseInsensitive) == 0 &&
- field.second.compare("h2c", Qt::CaseInsensitive) == 0)
- return true;
- }
+ if (reply.statusCode() != 101)
+ return false;
+
+ // Do some minimal checks here - we expect 'Upgrade: h2c' to be found.
+ for (const auto &v : reply.header().values(QHttpHeaders::WellKnownHeader::Upgrade)) {
+ if (v.compare("h2c", Qt::CaseInsensitive) == 0)
+ return true;
}
return false;
}
+std::vector<uchar> assemble_hpack_block(const std::vector<Frame> &frames)
+{
+ std::vector<uchar> hpackBlock;
+
+ size_t total = 0;
+ for (const auto &frame : frames) {
+ if (qAddOverflow(total, size_t{frame.hpackBlockSize()}, &total))
+ return hpackBlock;
+ }
+
+ if (!total)
+ return hpackBlock;
+
+ hpackBlock.resize(total);
+ auto dst = hpackBlock.begin();
+ for (const auto &frame : frames) {
+ if (const auto hpackBlockSize = frame.hpackBlockSize()) {
+ const uchar *src = frame.hpackBlockBegin();
+ std::copy(src, src + hpackBlockSize, dst);
+ dst += hpackBlockSize;
+ }
+ }
+
+ return hpackBlock;
+}
+
+
} // namespace Http2
QT_END_NAMESPACE
diff --git a/src/network/access/http2/http2protocol_p.h b/src/network/access/http2/http2protocol_p.h
index d19208895a..fb5ff199c5 100644
--- a/src/network/access/http2/http2protocol_p.h
+++ b/src/network/access/http2/http2protocol_p.h
@@ -21,6 +21,8 @@
#include <QtCore/private/qglobal_p.h>
#include <QtCore/qmap.h>
+#include <vector>
+
// Different HTTP/2 constants/values as defined by RFC 7540.
QT_BEGIN_NAMESPACE
@@ -112,11 +114,13 @@ const quint32 lastValidStreamID((quint32(1) << 31) - 1); // HTTP/2, 5.1.1
// HTTP/2 servers are not afraid to immediately set it to the possible max,
// we do the same and split this window size between our concurrent streams.
const qint32 maxSessionReceiveWindowSize((quint32(1) << 31) - 1);
-const qint32 qtDefaultStreamReceiveWindowSize = maxSessionReceiveWindowSize / maxConcurrentStreams;
+// Presumably, we never use up to 100 streams so let it be 10 simultaneous:
+const qint32 qtDefaultStreamReceiveWindowSize = maxSessionReceiveWindowSize / 10;
struct Frame configurationToSettingsFrame(const QHttp2Configuration &configuration);
QByteArray settingsFrameToBase64(const Frame &settingsFrame);
void appendProtocolUpgradeHeaders(const QHttp2Configuration &configuration, QHttpNetworkRequest *request);
+std::vector<uchar> assemble_hpack_block(const std::vector<Frame> &frames);
extern const Q_AUTOTEST_EXPORT char Http2clientPreface[clientPrefaceLength];
diff --git a/src/network/access/qdecompresshelper.cpp b/src/network/access/qdecompresshelper.cpp
index 98c9860450..52a0d9fc06 100644
--- a/src/network/access/qdecompresshelper.cpp
+++ b/src/network/access/qdecompresshelper.cpp
@@ -3,7 +3,6 @@
#include "qdecompresshelper_p.h"
-#include <QtCore/private/qbytearray_p.h>
#include <QtCore/qiodevice.h>
#include <QtCore/qcoreapplication.h>
@@ -24,7 +23,7 @@ QT_BEGIN_NAMESPACE
namespace {
struct ContentEncodingMapping
{
- char name[8];
+ QByteArrayView name;
QDecompressHelper::ContentEncoding encoding;
};
@@ -39,10 +38,10 @@ constexpr ContentEncodingMapping contentEncodingMapping[] {
{ "deflate", QDecompressHelper::Deflate },
};
-QDecompressHelper::ContentEncoding encodingFromByteArray(const QByteArray &ce) noexcept
+QDecompressHelper::ContentEncoding encodingFromByteArray(QByteArrayView ce) noexcept
{
for (const auto &mapping : contentEncodingMapping) {
- if (ce.compare(QByteArrayView(mapping.name, strlen(mapping.name)), Qt::CaseInsensitive) == 0)
+ if (ce.compare(mapping.name, Qt::CaseInsensitive) == 0)
return mapping.encoding;
}
return QDecompressHelper::None;
@@ -68,22 +67,19 @@ ZSTD_DStream *toZstandardPointer(void *ptr)
#endif
}
-bool QDecompressHelper::isSupportedEncoding(const QByteArray &encoding)
+bool QDecompressHelper::isSupportedEncoding(QByteArrayView encoding)
{
return encodingFromByteArray(encoding) != QDecompressHelper::None;
}
QByteArrayList QDecompressHelper::acceptedEncoding()
{
- static QByteArrayList accepted = []() {
- QByteArrayList list;
- list.reserve(sizeof(contentEncodingMapping) / sizeof(contentEncodingMapping[0]));
- for (const auto &mapping : contentEncodingMapping) {
- list << QByteArray(mapping.name);
- }
- return list;
- }();
- return accepted;
+ QByteArrayList list;
+ list.reserve(std::size(contentEncodingMapping));
+ for (const auto &mapping : contentEncodingMapping) {
+ list << mapping.name.toByteArray();
+ }
+ return list;
}
QDecompressHelper::~QDecompressHelper()
@@ -91,7 +87,7 @@ QDecompressHelper::~QDecompressHelper()
clear();
}
-bool QDecompressHelper::setEncoding(const QByteArray &encoding)
+bool QDecompressHelper::setEncoding(QByteArrayView encoding)
{
Q_ASSERT(contentEncoding == QDecompressHelper::None);
if (contentEncoding != QDecompressHelper::None) {
diff --git a/src/network/access/qdecompresshelper_p.h b/src/network/access/qdecompresshelper_p.h
index 54a9fe92c8..c837c14521 100644
--- a/src/network/access/qdecompresshelper_p.h
+++ b/src/network/access/qdecompresshelper_p.h
@@ -37,7 +37,7 @@ public:
QDecompressHelper() = default;
~QDecompressHelper();
- bool setEncoding(const QByteArray &contentEncoding);
+ bool setEncoding(QByteArrayView contentEncoding);
bool isCountingBytes() const;
void setCountingBytesEnabled(bool shouldCount);
@@ -57,7 +57,7 @@ public:
void setDecompressedSafetyCheckThreshold(qint64 threshold);
- static bool isSupportedEncoding(const QByteArray &encoding);
+ static bool isSupportedEncoding(QByteArrayView encoding);
static QByteArrayList acceptedEncoding();
QString errorString() const;
diff --git a/src/network/access/qhsts.cpp b/src/network/access/qhsts.cpp
index 39905f3548..21ed08ce4a 100644
--- a/src/network/access/qhsts.cpp
+++ b/src/network/access/qhsts.cpp
@@ -3,6 +3,8 @@
#include "qhsts_p.h"
+#include "qhttpheaders.h"
+
#include "QtCore/private/qipaddress_p.h"
#include "QtCore/qlist.h"
@@ -40,7 +42,7 @@ static bool is_valid_domain_name(const QString &host)
return true;
}
-void QHstsCache::updateFromHeaders(const QList<QPair<QByteArray, QByteArray>> &headers,
+void QHstsCache::updateFromHeaders(const QHttpHeaders &headers,
const QUrl &url)
{
if (!url.isValid())
@@ -286,7 +288,7 @@ static bool isSeparator(char c)
return isLWS(c) || std::find(separators, end, c) != end;
}
-static QByteArray unescapeMaxAge(const QByteArray &value)
+static QByteArrayView unescapeMaxAge(QByteArrayView value)
{
if (value.size() < 2 || value[0] != '"')
return value;
@@ -324,27 +326,25 @@ quoted-pair = "\" CHAR
*/
-bool QHstsHeaderParser::parse(const QList<QPair<QByteArray, QByteArray>> &headers)
+bool QHstsHeaderParser::parse(const QHttpHeaders &headers)
{
- for (const auto &h : headers) {
- // We use '==' since header name was already 'trimmed' for us:
- if (h.first == "Strict-Transport-Security") {
- header = h.second;
- // RFC6797, 8.1:
- //
- // The UA MUST ignore any STS header fields not conforming to the
- // grammar specified in Section 6.1 ("Strict-Transport-Security HTTP
- // Response Header Field").
- //
- // If a UA receives more than one STS header field in an HTTP
- // response message over secure transport, then the UA MUST process
- // only the first such header field.
- //
- // We read this as: ignore all invalid headers and take the first valid:
- if (parseSTSHeader() && maxAgeFound) {
- expiry = QDateTime::currentDateTimeUtc().addSecs(maxAge);
- return true;
- }
+ for (const auto &value : headers.values(
+ QHttpHeaders::WellKnownHeader::StrictTransportSecurity)) {
+ header = value;
+ // RFC6797, 8.1:
+ //
+ // The UA MUST ignore any STS header fields not conforming to the
+ // grammar specified in Section 6.1 ("Strict-Transport-Security HTTP
+ // Response Header Field").
+ //
+ // If a UA receives more than one STS header field in an HTTP
+ // response message over secure transport, then the UA MUST process
+ // only the first such header field.
+ //
+ // We read this as: ignore all invalid headers and take the first valid:
+ if (parseSTSHeader() && maxAgeFound) {
+ expiry = QDateTime::currentDateTimeUtc().addSecs(maxAge);
+ return true;
}
}
@@ -400,7 +400,7 @@ bool QHstsHeaderParser::parseDirective()
if (token == ";") // That's a weird grammar, but that's what it is.
return true;
- if (!isTOKEN(token[0])) // Not a valid directive-name.
+ if (!isTOKEN(token.at(0))) // Not a valid directive-name.
return false;
const QByteArray directiveName = token;
@@ -445,7 +445,7 @@ bool QHstsHeaderParser::processDirective(const QByteArray &name, const QByteArra
return false;
}
- const QByteArray unescapedValue = unescapeMaxAge(value);
+ const QByteArrayView unescapedValue = unescapeMaxAge(value);
if (!unescapedValue.size())
return false;
@@ -481,13 +481,13 @@ bool QHstsHeaderParser::nextToken()
// Fortunately enough, by this point qhttpnetworkreply already got rid of
// [CRLF] parts, but we can have 1*(SP|HT) yet.
- while (tokenPos < header.size() && isLWS(header[tokenPos]))
+ while (tokenPos < header.size() && isLWS(header.at(tokenPos)))
++tokenPos;
if (tokenPos == header.size())
return true;
- const char ch = header[tokenPos];
+ const char ch = header.at(tokenPos);
if (ch == ';' || ch == '=') {
token.append(ch);
++tokenPos;
@@ -501,17 +501,17 @@ bool QHstsHeaderParser::nextToken()
if (ch == '"') {
int last = tokenPos + 1;
while (last < header.size()) {
- if (header[last] == '"') {
+ if (header.at(last) == '"') {
// The end of a quoted-string.
break;
- } else if (header[last] == '\\') {
+ } else if (header.at(last) == '\\') {
// quoted-pair = "\" CHAR
- if (last + 1 < header.size() && isCHAR(header[last + 1]))
+ if (last + 1 < header.size() && isCHAR(header.at(last + 1)))
last += 2;
else
return false;
} else {
- if (!isTEXT(header[last]))
+ if (!isTEXT(header.at(last)))
return false;
++last;
}
@@ -532,7 +532,7 @@ bool QHstsHeaderParser::nextToken()
return false;
int last = tokenPos + 1;
- while (last < header.size() && isTOKEN(header[last]))
+ while (last < header.size() && isTOKEN(header.at(last)))
++last;
token = header.mid(tokenPos, last - tokenPos);
diff --git a/src/network/access/qhsts_p.h b/src/network/access/qhsts_p.h
index 96d4ee8dc5..ff9378197b 100644
--- a/src/network/access/qhsts_p.h
+++ b/src/network/access/qhsts_p.h
@@ -31,11 +31,13 @@
QT_BEGIN_NAMESPACE
+class QHttpHeaders;
+
class Q_AUTOTEST_EXPORT QHstsCache
{
public:
- void updateFromHeaders(const QList<QPair<QByteArray, QByteArray>> &headers,
+ void updateFromHeaders(const QHttpHeaders &headers,
const QUrl &url);
void updateFromPolicies(const QList<QHstsPolicy> &hosts);
void updateKnownHost(const QUrl &url, const QDateTime &expires,
@@ -90,7 +92,7 @@ class Q_AUTOTEST_EXPORT QHstsHeaderParser
{
public:
- bool parse(const QList<QPair<QByteArray, QByteArray>> &headers);
+ bool parse(const QHttpHeaders &headers);
QDateTime expirationDate() const { return expiry; }
bool includeSubDomains() const { return subDomainsFound; }
diff --git a/src/network/access/qhstspolicy.cpp b/src/network/access/qhstspolicy.cpp
index 504c2203a4..323e562c3c 100644
--- a/src/network/access/qhstspolicy.cpp
+++ b/src/network/access/qhstspolicy.cpp
@@ -21,8 +21,8 @@ QT_BEGIN_NAMESPACE
RFC6797.
You can set expiry time and host name for this policy, and control whether it
- applies to subdomains, either in the constructor or by calling setExpiry(),
- setHost() and setIncludesSubdomains().
+ applies to subdomains, either in the constructor or by calling \l setExpiry(),
+ \l setHost() and \l setIncludesSubDomains().
\sa QNetworkAccessManager::setStrictTransportSecurityEnabled()
*/
diff --git a/src/network/access/qhstsstore.cpp b/src/network/access/qhstsstore.cpp
index 80bb06d656..a972a90ee7 100644
--- a/src/network/access/qhstsstore.cpp
+++ b/src/network/access/qhstsstore.cpp
@@ -80,7 +80,7 @@ void QHstsStore::synchronize()
if (observedPolicies.size()) {
beginHstsGroups();
- for (const QHstsPolicy &policy : qAsConst(observedPolicies)) {
+ for (const QHstsPolicy &policy : std::as_const(observedPolicies)) {
const QString key(host_name_to_settings_key(policy.host()));
// If we fail to write a new, updated policy, we also remove the old one.
if (policy.isExpired() || !serializePolicy(key, policy))
diff --git a/src/network/access/qhttp1configuration.cpp b/src/network/access/qhttp1configuration.cpp
new file mode 100644
index 0000000000..cfa929bca5
--- /dev/null
+++ b/src/network/access/qhttp1configuration.cpp
@@ -0,0 +1,154 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qhttp1configuration.h"
+
+#include <QtCore/private/qnumeric_p.h>
+#include <QtCore/qhashfunctions.h>
+
+QT_BEGIN_NAMESPACE
+
+// QHttp1ConfigurationPrivate is unused until we need it:
+static_assert(sizeof(QHttp1Configuration) == sizeof(void*),
+ "You have added too many members to QHttp1Configuration::ShortData. "
+ "Decrease their size or switch to using a d-pointer.");
+
+/*!
+ \class QHttp1Configuration
+ \brief The QHttp1Configuration class controls HTTP/1 parameters and settings.
+ \since 6.5
+
+ \reentrant
+ \inmodule QtNetwork
+ \ingroup network
+ \ingroup shared
+
+ QHttp1Configuration controls HTTP/1 parameters and settings that
+ QNetworkAccessManager will use to send requests and process responses.
+
+ \note The configuration must be set before the first request
+ was sent to a given host (and thus an HTTP/1 session established).
+
+ \sa QNetworkRequest::setHttp1Configuration(), QNetworkRequest::http1Configuration(), QNetworkAccessManager
+*/
+
+/*!
+ Default constructs a QHttp1Configuration object.
+*/
+QHttp1Configuration::QHttp1Configuration()
+ : u(ShortData{6, {}}) // QHttpNetworkConnectionPrivate::defaultHttpChannelCount
+{
+}
+
+/*!
+ Copy-constructs this QHttp1Configuration.
+*/
+QHttp1Configuration::QHttp1Configuration(const QHttp1Configuration &)
+ = default;
+
+/*!
+ \fn QHttp1Configuration::QHttp1Configuration(QHttp1Configuration &&other)
+
+ Move-constructs this QHttp1Configuration from \a other.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new value.
+*/
+
+/*!
+ Copy-assigns \a other to this QHttp1Configuration.
+*/
+QHttp1Configuration &QHttp1Configuration::operator=(const QHttp1Configuration &)
+ = default;
+
+/*!
+ \fn QHttp1Configuration &QHttp1Configuration::operator=(QHttp1Configuration &&)
+
+ Move-assigns \a other to this QHttp1Configuration.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new value.
+*/
+
+/*!
+ Destructor.
+*/
+QHttp1Configuration::~QHttp1Configuration()
+ = default;
+
+/*!
+ Sets the number of connections (minimum: 1; maximum: 255)
+ used per http(s) \e{host}:\e{port} combination to \a number.
+
+ If \a number is ≤ 0, does nothing. If \a number is > 255, 255 is used.
+
+ \sa numberOfConnectionsPerHost
+*/
+void QHttp1Configuration::setNumberOfConnectionsPerHost(qsizetype number)
+{
+ auto n = qt_saturate<std::uint8_t>(number);
+ if (n == 0)
+ return;
+ u.data.numConnectionsPerHost = n;
+}
+
+/*!
+ Returns the number of connections used per http(s) \c{host}:\e{port}
+ combination. The default is six (6).
+
+ \sa setNumberOfConnectionsPerHost
+*/
+qsizetype QHttp1Configuration::numberOfConnectionsPerHost() const
+{
+ return u.data.numConnectionsPerHost;
+}
+
+/*!
+ \fn void QHttp1Configuration::swap(QHttp1Configuration &other)
+
+ Swaps this HTTP/1 configuration with \a other. This operation is very fast
+ and never fails.
+*/
+
+/*!
+ \fn bool QHttp1Configuration::operator==(const QHttp1Configuration &lhs, const QHttp1Configuration &rhs) noexcept
+ \since 6.5
+
+ Returns \c true if \a lhs and \a rhs represent the same set of HTTP/1
+ parameters.
+*/
+
+/*!
+ \fn bool QHttp1Configuration::operator!=(const QHttp1Configuration &lhs, const QHttp1Configuration &rhs) noexcept
+ \since 6.5
+
+ Returns \c true if \a lhs and \a rhs do not represent the same set of
+ HTTP/1 parameters.
+*/
+
+/*!
+ \fn size_t QHttp1Configuration::qHash(const QHttp1Configuration &key, size_t seed)
+ \since 6.5
+
+ Returns the hash value for the \a key, using \a seed to seed the calculation.
+*/
+
+/*!
+ \internal
+*/
+bool QHttp1Configuration::equals(const QHttp1Configuration &other) const noexcept
+{
+ return u.data.numConnectionsPerHost == other.u.data.numConnectionsPerHost;
+}
+
+/*!
+ \internal
+*/
+size_t QHttp1Configuration::hash(size_t seed) const noexcept
+{
+ return qHash(u.data.numConnectionsPerHost, seed);
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qhttp1configuration.h b/src/network/access/qhttp1configuration.h
new file mode 100644
index 0000000000..128b8aa5aa
--- /dev/null
+++ b/src/network/access/qhttp1configuration.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QHTTP1CONFIGURATION_H
+#define QHTTP1CONFIGURATION_H
+
+#include <QtNetwork/qtnetworkglobal.h>
+
+#include <utility>
+#include <cstdint>
+
+QT_REQUIRE_CONFIG(http);
+
+QT_BEGIN_NAMESPACE
+
+class QHttp1ConfigurationPrivate;
+class QHttp1Configuration
+{
+public:
+ Q_NETWORK_EXPORT QHttp1Configuration();
+ Q_NETWORK_EXPORT QHttp1Configuration(const QHttp1Configuration &other);
+ QHttp1Configuration(QHttp1Configuration &&other) noexcept
+ : u{other.u} { other.u.d = nullptr; }
+
+ Q_NETWORK_EXPORT QHttp1Configuration &operator=(const QHttp1Configuration &other);
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QHttp1Configuration)
+
+ Q_NETWORK_EXPORT ~QHttp1Configuration();
+
+ Q_NETWORK_EXPORT void setNumberOfConnectionsPerHost(qsizetype amount);
+ Q_NETWORK_EXPORT qsizetype numberOfConnectionsPerHost() const;
+
+ void swap(QHttp1Configuration &other) noexcept
+ { std::swap(u, other.u); }
+
+private:
+ struct ShortData {
+ std::uint8_t numConnectionsPerHost;
+ char reserved[sizeof(void*) - sizeof(numConnectionsPerHost)];
+ };
+ union U {
+ U(ShortData _data) : data(_data) {}
+ QHttp1ConfigurationPrivate *d;
+ ShortData data;
+ } u;
+
+ Q_NETWORK_EXPORT bool equals(const QHttp1Configuration &other) const noexcept;
+ Q_NETWORK_EXPORT size_t hash(size_t seed) const noexcept;
+
+ friend bool operator==(const QHttp1Configuration &lhs, const QHttp1Configuration &rhs) noexcept
+ { return lhs.equals(rhs); }
+ friend bool operator!=(const QHttp1Configuration &lhs, const QHttp1Configuration &rhs) noexcept
+ { return !lhs.equals(rhs); }
+
+ friend size_t qHash(const QHttp1Configuration &key, size_t seed = 0) noexcept { return key.hash(seed); }
+};
+
+Q_DECLARE_SHARED(QHttp1Configuration)
+
+QT_END_NAMESPACE
+
+#endif // QHTTP1CONFIGURATION_H
diff --git a/src/network/access/qhttp2configuration.cpp b/src/network/access/qhttp2configuration.cpp
index f0dcb95fac..b718ddc755 100644
--- a/src/network/access/qhttp2configuration.cpp
+++ b/src/network/access/qhttp2configuration.cpp
@@ -217,7 +217,7 @@ bool QHttp2Configuration::setStreamReceiveWindowSize(unsigned size)
/*!
Returns the window size for stream-level flow control.
The default value QNetworkAccessManager will be using is
- 65535 octets (see \l {https://httpwg.org/specs/rfc7540.html#SettingValues}{RFC 7540}).
+ 214748364 octets (see \l {https://httpwg.org/specs/rfc7540.html#SettingValues}{RFC 7540}).
*/
unsigned QHttp2Configuration::streamReceiveWindowSize() const
{
diff --git a/src/network/access/qhttp2configuration.h b/src/network/access/qhttp2configuration.h
index 07d93ca1d6..ae08b664d4 100644
--- a/src/network/access/qhttp2configuration.h
+++ b/src/network/access/qhttp2configuration.h
@@ -8,9 +8,7 @@
#include <QtCore/qshareddata.h>
-#ifndef Q_CLANG_QDOC
QT_REQUIRE_CONFIG(http);
-#endif
QT_BEGIN_NAMESPACE
diff --git a/src/network/access/qhttp2connection.cpp b/src/network/access/qhttp2connection.cpp
new file mode 100644
index 0000000000..8560e0da38
--- /dev/null
+++ b/src/network/access/qhttp2connection.cpp
@@ -0,0 +1,1752 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qhttp2connection_p.h"
+
+#include <private/bitstreams_p.h>
+
+#include <QtCore/private/qnumeric_p.h>
+#include <QtCore/private/qiodevice_p.h>
+#include <QtCore/private/qnoncontiguousbytedevice_p.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/QRandomGenerator>
+#include <QtCore/qloggingcategory.h>
+
+#include <algorithm>
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qHttp2ConnectionLog, "qt.network.http2.connection", QtCriticalMsg)
+
+using namespace Qt::StringLiterals;
+using namespace Http2;
+
+/*!
+ \class QHttp2Stream
+ \inmodule QtNetwork
+ \internal
+
+ The QHttp2Stream class represents a single HTTP/2 stream.
+ Must be created by QHttp2Connection.
+
+ \sa QHttp2Connection
+*/
+
+QHttp2Stream::QHttp2Stream(QHttp2Connection *connection, quint32 streamID) noexcept
+ : QObject(connection), m_streamID(streamID)
+{
+ Q_ASSERT(connection);
+ Q_ASSERT(streamID); // stream id 0 is reserved for connection control messages
+ qCDebug(qHttp2ConnectionLog, "[%p] new stream %u", connection, streamID);
+}
+
+QHttp2Stream::~QHttp2Stream() noexcept = default;
+
+/*!
+ \fn quint32 QHttp2Stream::streamID() const noexcept
+
+ Returns the stream ID of this stream.
+*/
+
+/*!
+ \fn void QHttp2Stream::headersReceived(const HPack::HttpHeader &headers, bool endStream)
+
+ This signal is emitted when the remote peer has sent a HEADERS frame, and
+ potentially some CONTINUATION frames, ending with the END_HEADERS flag
+ to this stream.
+
+ The headers are internally combined and decompressed, and are accessible
+ through the \a headers parameter. If the END_STREAM flag was set, the
+ \a endStream parameter will be \c true, indicating that the peer does not
+ intend to send any more frames on this stream.
+
+ \sa receivedHeaders()
+*/
+
+/*!
+ \fn void QHttp2Stream::headersUpdated()
+
+ This signal may be emitted if a new HEADERS frame was received after
+ already processing a previous HEADERS frame.
+
+ \sa headersReceived(), receivedHeaders()
+*/
+
+/*!
+ \fn void QHttp2Stream::errorOccurred(quint32 errorCode, const QString &errorString)
+
+ This signal is emitted when the stream has encountered an error. The
+ \a errorCode parameter is the HTTP/2 error code, and the \a errorString
+ parameter is a human-readable description of the error.
+
+ \sa https://www.rfc-editor.org/rfc/rfc7540#section-7
+*/
+
+/*!
+ \fn void QHttp2Stream::stateChanged(State newState)
+
+ This signal is emitted when the state of the stream changes. The \a newState
+ parameter is the new state of the stream.
+
+ Examples of this is sending or receiving a frame with the END_STREAM flag.
+ This will transition the stream to the HalfClosedLocal or HalfClosedRemote
+ state, respectively.
+
+ \sa state()
+*/
+
+
+/*!
+ \fn void QHttp2Stream::promisedStreamReceived(quint32 newStreamID)
+
+ This signal is emitted when the remote peer has promised a new stream with
+ the given \a newStreamID.
+
+ \sa QHttp2Connection::promisedStream()
+*/
+
+/*!
+ \fn void QHttp2Stream::uploadBlocked()
+
+ This signal is emitted when the stream is unable to send more data because
+ the remote peer's receive window is full.
+
+ This is mostly intended for diagnostics as there is no expectation that the
+ user can do anything to react to this.
+*/
+
+/*!
+ \fn void QHttp2Stream::dataReceived(const QByteArray &data, bool endStream)
+
+ This signal is emitted when the stream has received a DATA frame from the
+ remote peer. The \a data parameter contains the payload of the frame, and
+ the \a endStream parameter is \c true if the END_STREAM flag was set.
+
+ \sa downloadBuffer()
+*/
+
+/*!
+ \fn void QHttp2Stream::bytesWritten(qint64 bytesWritten)
+
+ This signal is emitted when the stream has written \a bytesWritten bytes to
+ the network.
+*/
+
+/*!
+ \fn void QHttp2Stream::uploadDeviceError(const QString &errorString)
+
+ This signal is emitted if the upload device encounters an error while
+ sending data. The \a errorString parameter is a human-readable description
+ of the error.
+*/
+
+/*!
+ \fn void QHttp2Stream::uploadFinished()
+
+ This signal is emitted when the stream has finished sending all the data
+ from the upload device.
+
+ If the END_STREAM flag was set for sendData() then the stream will be
+ closed for further writes before this signal is emitted.
+*/
+
+/*!
+ \fn bool QHttp2Stream::isUploadingDATA() const noexcept
+
+ Returns \c true if the stream is currently sending DATA frames.
+*/
+
+/*!
+ \fn State QHttp2Stream::state() const noexcept
+
+ Returns the current state of the stream.
+
+ \sa stateChanged()
+*/
+/*!
+ \fn bool QHttp2Stream::isActive() const noexcept
+
+ Returns \c true if the stream has been opened and is not yet closed.
+*/
+/*!
+ \fn bool QHttp2Stream::isPromisedStream() const noexcept
+
+ Returns \c true if the stream was promised by the remote peer.
+*/
+/*!
+ \fn bool QHttp2Stream::wasReset() const noexcept
+
+ Returns \c true if the stream was reset by the remote peer.
+*/
+/*!
+ \fn quint32 QHttp2Stream::RST_STREAM_code() const noexcept
+
+ Returns the HTTP/2 error code if the stream was reset by the remote peer.
+ If the stream was not reset, this function returns 0.
+*/
+/*!
+ \fn HPack::HttpHeader QHttp2Stream::receivedHeaders() const noexcept
+
+ Returns the headers received from the remote peer, if any.
+*/
+/*!
+ \fn QByteDataBuffer QHttp2Stream::downloadBuffer() const noexcept
+
+ Returns the buffer containing the data received from the remote peer.
+*/
+
+void QHttp2Stream::finishWithError(quint32 errorCode, const QString &message)
+{
+ qCDebug(qHttp2ConnectionLog, "[%p] stream %u finished with error: %ls (error code: %u)",
+ getConnection(), m_streamID, qUtf16Printable(message), errorCode);
+ transitionState(StateTransition::RST);
+ emit errorOccurred(errorCode, message);
+}
+
+void QHttp2Stream::finishWithError(quint32 errorCode)
+{
+ QNetworkReply::NetworkError error = QNetworkReply::NoError;
+ QString message;
+ qt_error(errorCode, error, message);
+ finishWithError(error, message);
+}
+
+/*!
+ Sends a RST_STREAM frame with the given \a errorCode.
+ This closes the stream for both sides, any further frames will be dropped.
+
+ Returns \c false if the stream is closed or idle, also if it fails to send
+ the RST_STREAM frame. Otherwise, returns \c true.
+*/
+bool QHttp2Stream::sendRST_STREAM(quint32 errorCode)
+{
+ if (m_state == State::Closed || m_state == State::Idle)
+ return false;
+ qCDebug(qHttp2ConnectionLog, "[%p] sending RST_STREAM on stream %u, code: %u", getConnection(),
+ m_streamID, errorCode);
+ transitionState(StateTransition::RST);
+
+ QHttp2Connection *connection = getConnection();
+ FrameWriter &frameWriter = connection->frameWriter;
+ frameWriter.start(FrameType::RST_STREAM, FrameFlag::EMPTY, m_streamID);
+ frameWriter.append(errorCode);
+ return frameWriter.write(*connection->getSocket());
+}
+
+/*!
+ Sends a DATA frame with the bytes obtained from \a device.
+
+ This function will send as many DATA frames as needed to send all the data
+ from \a device. If \a endStream is \c true, the END_STREAM flag will be set.
+
+ \a device must stay alive for the duration of the upload.
+ A way of doing this is to heap-allocate the \a device and parent it to the
+ QHttp2Stream.
+*/
+void QHttp2Stream::sendDATA(QIODevice *device, bool endStream)
+{
+ Q_ASSERT(!m_uploadDevice);
+ Q_ASSERT(!m_uploadByteDevice);
+ Q_ASSERT(device);
+ if (m_state != State::Open && m_state != State::HalfClosedRemote)
+ return;
+
+ auto *byteDevice = QNonContiguousByteDeviceFactory::create(device);
+ connect(this, &QHttp2Stream::uploadFinished, byteDevice, &QObject::deleteLater);
+ byteDevice->setParent(this);
+ m_uploadDevice = device;
+ qCDebug(qHttp2ConnectionLog, "[%p] starting sendDATA on stream %u, of IODevice: %p",
+ getConnection(), m_streamID, device);
+ sendDATA(byteDevice, endStream);
+}
+
+/*!
+ Sends a DATA frame with the bytes obtained from \a device.
+
+ This function will send as many DATA frames as needed to send all the data
+ from \a device. If \a endStream is \c true, the END_STREAM flag will be set.
+
+ \a device must stay alive for the duration of the upload.
+ A way of doing this is to heap-allocate the \a device and parent it to the
+ QHttp2Stream.
+*/
+void QHttp2Stream::sendDATA(QNonContiguousByteDevice *device, bool endStream)
+{
+ Q_ASSERT(!m_uploadByteDevice);
+ Q_ASSERT(device);
+ if (m_state != State::Open && m_state != State::HalfClosedRemote)
+ return;
+
+ qCDebug(qHttp2ConnectionLog, "[%p] starting sendDATA on stream %u, of device: %p",
+ getConnection(), m_streamID, device);
+
+ m_uploadByteDevice = device;
+ m_endStreamAfterDATA = endStream;
+ connect(m_uploadByteDevice, &QNonContiguousByteDevice::readyRead, this,
+ &QHttp2Stream::maybeResumeUpload);
+ connect(m_uploadByteDevice, &QObject::destroyed, this, &QHttp2Stream::uploadDeviceDestroyed);
+
+ internalSendDATA();
+}
+
+void QHttp2Stream::internalSendDATA()
+{
+ Q_ASSERT(m_uploadByteDevice);
+ QHttp2Connection *connection = getConnection();
+ Q_ASSERT(connection->maxFrameSize > frameHeaderSize);
+ QIODevice *socket = connection->getSocket();
+
+ qCDebug(qHttp2ConnectionLog,
+ "[%p] stream %u, about to write to socket, current session window size: %d, stream "
+ "window size: %d, bytes available: %lld",
+ connection, m_streamID, connection->sessionSendWindowSize, m_sendWindow,
+ m_uploadByteDevice->size() - m_uploadByteDevice->pos());
+
+ qint32 remainingWindowSize = std::min<qint32>(connection->sessionSendWindowSize, m_sendWindow);
+ FrameWriter &frameWriter = connection->frameWriter;
+ qint64 totalBytesWritten = 0;
+ const auto deviceCanRead = [this, connection] {
+ // We take advantage of knowing the internals of one of the devices used.
+ // It will request X bytes to move over to the http thread if there's
+ // not enough left, so we give it a large size. It will anyway return
+ // the size it can actually provide.
+ const qint64 requestSize = connection->maxFrameSize * 10ll;
+ qint64 tmp = 0;
+ return m_uploadByteDevice->readPointer(requestSize, tmp) != nullptr && tmp > 0;
+ };
+
+ bool sentEND_STREAM = false;
+ while (remainingWindowSize && deviceCanRead()) {
+ quint32 bytesWritten = 0;
+ qint32 remainingBytesInFrame = qint32(connection->maxFrameSize);
+ frameWriter.start(FrameType::DATA, FrameFlag::EMPTY, streamID());
+
+ while (remainingWindowSize && deviceCanRead() && remainingBytesInFrame) {
+ const qint32 maxToWrite = std::min(remainingWindowSize, remainingBytesInFrame);
+
+ qint64 outBytesAvail = 0;
+ const char *readPointer = m_uploadByteDevice->readPointer(maxToWrite, outBytesAvail);
+ if (!readPointer || outBytesAvail <= 0) {
+ qCDebug(qHttp2ConnectionLog,
+ "[%p] stream %u, cannot write data, device (%p) has %lld bytes available",
+ connection, m_streamID, m_uploadByteDevice, outBytesAvail);
+ break;
+ }
+ const qint32 bytesToWrite = qint32(std::min<qint64>(maxToWrite, outBytesAvail));
+ frameWriter.append(QByteArrayView(readPointer, bytesToWrite));
+ m_uploadByteDevice->advanceReadPointer(bytesToWrite);
+
+ bytesWritten += bytesToWrite;
+
+ m_sendWindow -= bytesToWrite;
+ Q_ASSERT(m_sendWindow >= 0);
+ connection->sessionSendWindowSize -= bytesToWrite;
+ Q_ASSERT(connection->sessionSendWindowSize >= 0);
+ remainingBytesInFrame -= bytesToWrite;
+ Q_ASSERT(remainingBytesInFrame >= 0);
+ remainingWindowSize -= bytesToWrite;
+ Q_ASSERT(remainingWindowSize >= 0);
+ }
+
+ qCDebug(qHttp2ConnectionLog, "[%p] stream %u, writing %u bytes to socket", connection,
+ m_streamID, bytesWritten);
+ if (!deviceCanRead() && m_uploadByteDevice->atEnd() && m_endStreamAfterDATA) {
+ sentEND_STREAM = true;
+ frameWriter.addFlag(FrameFlag::END_STREAM);
+ }
+ if (!frameWriter.write(*socket)) {
+ qCDebug(qHttp2ConnectionLog, "[%p] stream %u, failed to write to socket", connection,
+ m_streamID);
+ finishWithError(QNetworkReply::ProtocolFailure, "failed to write to socket"_L1);
+ return;
+ }
+
+ totalBytesWritten += bytesWritten;
+ }
+
+ qCDebug(qHttp2ConnectionLog,
+ "[%p] stream %u, wrote %lld bytes total, if the device is not exhausted, we'll write "
+ "more later. Remaining window size: %d",
+ connection, m_streamID, totalBytesWritten, remainingWindowSize);
+
+ emit bytesWritten(totalBytesWritten);
+ if (sentEND_STREAM || (!deviceCanRead() && m_uploadByteDevice->atEnd())) {
+ qCDebug(qHttp2ConnectionLog,
+ "[%p] stream %u, exhausted device %p, sent END_STREAM? %d, %ssending end stream "
+ "after DATA",
+ connection, m_streamID, m_uploadByteDevice, sentEND_STREAM,
+ m_endStreamAfterDATA ? "" : "not ");
+ if (!sentEND_STREAM && m_endStreamAfterDATA) {
+ // We need to send an empty DATA frame with END_STREAM since we
+ // have exhausted the device, but we haven't sent END_STREAM yet.
+ // This can happen if we got a final readyRead to signify no more
+ // data available, but we hadn't sent the END_STREAM flag yet.
+ frameWriter.start(FrameType::DATA, FrameFlag::END_STREAM, streamID());
+ frameWriter.write(*socket);
+ }
+ finishSendDATA();
+ } else if (isUploadBlocked()) {
+ qCDebug(qHttp2ConnectionLog, "[%p] stream %u, upload blocked", connection, m_streamID);
+ emit uploadBlocked();
+ }
+}
+
+void QHttp2Stream::finishSendDATA()
+{
+ if (m_endStreamAfterDATA)
+ transitionState(StateTransition::CloseLocal);
+
+ disconnect(m_uploadByteDevice, nullptr, this, nullptr);
+ m_uploadDevice = nullptr;
+ m_uploadByteDevice = nullptr;
+ emit uploadFinished();
+}
+
+void QHttp2Stream::maybeResumeUpload()
+{
+ qCDebug(qHttp2ConnectionLog,
+ "[%p] stream %u, maybeResumeUpload. Upload device: %p, bytes available: %lld, blocked? "
+ "%d",
+ getConnection(), m_streamID, m_uploadByteDevice,
+ !m_uploadByteDevice ? 0 : m_uploadByteDevice->size() - m_uploadByteDevice->pos(),
+ isUploadBlocked());
+ if (isUploadingDATA() && !isUploadBlocked())
+ internalSendDATA();
+}
+
+/*!
+ Returns \c true if the stream is currently unable to send more data because
+ the remote peer's receive window is full.
+*/
+bool QHttp2Stream::isUploadBlocked() const noexcept
+{
+ constexpr auto MinFrameSize = Http2::frameHeaderSize + 1; // 1 byte payload
+ return isUploadingDATA()
+ && (m_sendWindow <= MinFrameSize
+ || getConnection()->sessionSendWindowSize <= MinFrameSize);
+}
+
+void QHttp2Stream::uploadDeviceReadChannelFinished()
+{
+ maybeResumeUpload();
+}
+
+/*!
+ Sends a HEADERS frame with the given \a headers and \a priority.
+ If \a endStream is \c true, the END_STREAM flag will be set, and the stream
+ will be closed for future writes.
+ If the headers are too large, or the stream is not in the correct state,
+ this function will return \c false. Otherwise, it will return \c true.
+*/
+bool QHttp2Stream::sendHEADERS(const HPack::HttpHeader &headers, bool endStream, quint8 priority)
+{
+ using namespace HPack;
+ if (auto hs = header_size(headers);
+ !hs.first || hs.second > getConnection()->maxHeaderListSize()) {
+ return false;
+ }
+
+ transitionState(StateTransition::Open);
+
+ Q_ASSERT(m_state == State::Open || m_state == State::HalfClosedRemote);
+
+ QHttp2Connection *connection = getConnection();
+
+ qCDebug(qHttp2ConnectionLog, "[%p] stream %u, sending HEADERS frame with %u entries",
+ connection, streamID(), uint(headers.size()));
+
+ QIODevice *socket = connection->getSocket();
+ FrameWriter &frameWriter = connection->frameWriter;
+
+ frameWriter.start(FrameType::HEADERS, FrameFlag::PRIORITY | FrameFlag::END_HEADERS, streamID());
+ if (endStream)
+ frameWriter.addFlag(FrameFlag::END_STREAM);
+
+ frameWriter.append(quint32()); // No stream dependency in Qt.
+ frameWriter.append(priority);
+
+ // Compress in-place:
+ BitOStream outputStream(frameWriter.outboundFrame().buffer);
+ if (connection->m_connectionType == QHttp2Connection::Type::Client) {
+ if (!connection->encoder.encodeRequest(outputStream, headers))
+ return false;
+ } else {
+ if (!connection->encoder.encodeResponse(outputStream, headers))
+ return false;
+ }
+
+ bool result = frameWriter.writeHEADERS(*socket, connection->maxFrameSize);
+ if (endStream)
+ transitionState(StateTransition::CloseLocal);
+
+ return result;
+}
+
+/*!
+ Sends a WINDOW_UPDATE frame with the given \a delta.
+ This increases our receive window size for this stream, allowing the remote
+ peer to send more data.
+*/
+void QHttp2Stream::sendWINDOW_UPDATE(quint32 delta)
+{
+ QHttp2Connection *connection = getConnection();
+ m_recvWindow += qint32(delta);
+ connection->sendWINDOW_UPDATE(streamID(), delta);
+}
+
+void QHttp2Stream::uploadDeviceDestroyed()
+{
+ if (isUploadingDATA()) {
+ // We're in the middle of sending DATA frames, we need to abort
+ // the stream.
+ sendRST_STREAM(CANCEL);
+ emit uploadDeviceError("Upload device destroyed while uploading"_L1);
+ }
+ m_uploadDevice = nullptr;
+}
+
+void QHttp2Stream::setState(State newState)
+{
+ if (m_state == newState)
+ return;
+ qCDebug(qHttp2ConnectionLog, "[%p] stream %u, state changed from %d to %d", getConnection(),
+ streamID(), int(m_state), int(newState));
+ m_state = newState;
+ emit stateChanged(newState);
+}
+
+// Changes the state as appropriate given the current state and the transition.
+// Always call this before emitting any signals since the recipient might rely
+// on the new state!
+void QHttp2Stream::transitionState(StateTransition transition)
+{
+ switch (m_state) {
+ case State::Idle:
+ if (transition == StateTransition::Open)
+ setState(State::Open);
+ else
+ Q_UNREACHABLE(); // We should transition to Open before ever getting here
+ break;
+ case State::Open:
+ switch (transition) {
+ case StateTransition::CloseLocal:
+ setState(State::HalfClosedLocal);
+ break;
+ case StateTransition::CloseRemote:
+ setState(State::HalfClosedRemote);
+ break;
+ case StateTransition::RST:
+ setState(State::Closed);
+ break;
+ case StateTransition::Open: // no-op
+ break;
+ }
+ break;
+ case State::HalfClosedLocal:
+ if (transition == StateTransition::CloseRemote || transition == StateTransition::RST)
+ setState(State::Closed);
+ break;
+ case State::HalfClosedRemote:
+ if (transition == StateTransition::CloseLocal || transition == StateTransition::RST)
+ setState(State::Closed);
+ break;
+ case State::ReservedRemote:
+ if (transition == StateTransition::RST) {
+ setState(State::Closed);
+ } else if (transition == StateTransition::CloseLocal) { // Receiving HEADER closes local
+ setState(State::HalfClosedLocal);
+ }
+ break;
+ case State::Closed:
+ break;
+ }
+}
+
+void QHttp2Stream::handleDATA(const Frame &inboundFrame)
+{
+ QHttp2Connection *connection = getConnection();
+
+ qCDebug(qHttp2ConnectionLog, "[%p] stream %u, received DATA frame with payload of %u bytes",
+ connection, m_streamID, inboundFrame.payloadSize());
+
+ if (qint32(inboundFrame.payloadSize()) > m_recvWindow) {
+ qCDebug(qHttp2ConnectionLog,
+ "[%p] stream %u, received DATA frame with payload size %u, "
+ "but recvWindow is %d, sending FLOW_CONTROL_ERROR",
+ connection, m_streamID, inboundFrame.payloadSize(), m_recvWindow);
+ finishWithError(QNetworkReply::ProtocolFailure, "flow control error"_L1);
+ sendRST_STREAM(FLOW_CONTROL_ERROR);
+ return;
+ }
+ m_recvWindow -= qint32(inboundFrame.payloadSize());
+ const bool endStream = inboundFrame.flags().testFlag(FrameFlag::END_STREAM);
+ // Uncompress data if needed and append it ...
+ if (inboundFrame.dataSize() > 0 || endStream) {
+ QByteArray fragment(reinterpret_cast<const char *>(inboundFrame.dataBegin()),
+ inboundFrame.dataSize());
+ if (endStream)
+ transitionState(StateTransition::CloseRemote);
+ emit dataReceived(fragment, endStream);
+ m_downloadBuffer.append(std::move(fragment));
+ }
+
+ if (!endStream && m_recvWindow < connection->streamInitialReceiveWindowSize / 2) {
+ // @future[consider]: emit signal instead
+ sendWINDOW_UPDATE(quint32(connection->streamInitialReceiveWindowSize - m_recvWindow));
+ }
+}
+
+void QHttp2Stream::handleHEADERS(Http2::FrameFlags frameFlags, const HPack::HttpHeader &headers)
+{
+ if (m_state == State::Idle)
+ transitionState(StateTransition::Open);
+ const bool endStream = frameFlags.testFlag(FrameFlag::END_STREAM);
+ if (endStream)
+ transitionState(StateTransition::CloseRemote);
+ if (!headers.empty()) {
+ m_headers.insert(m_headers.end(), headers.begin(), headers.end());
+ emit headersUpdated();
+ }
+ emit headersReceived(headers, endStream);
+}
+
+void QHttp2Stream::handleRST_STREAM(const Frame &inboundFrame)
+{
+ transitionState(StateTransition::RST);
+ m_RST_STREAM_code = qFromBigEndian<quint32>(inboundFrame.dataBegin());
+ if (isUploadingDATA()) {
+ disconnect(m_uploadByteDevice, nullptr, this, nullptr);
+ m_uploadDevice = nullptr;
+ m_uploadByteDevice = nullptr;
+ }
+ finishWithError(*m_RST_STREAM_code, ""_L1);
+}
+
+void QHttp2Stream::handleWINDOW_UPDATE(const Frame &inboundFrame)
+{
+ const quint32 delta = qFromBigEndian<quint32>(inboundFrame.dataBegin());
+ const bool valid = delta && delta <= quint32(std::numeric_limits<qint32>::max());
+ qint32 sum = 0;
+ if (!valid || qAddOverflow(m_sendWindow, qint32(delta), &sum)) {
+ qCDebug(qHttp2ConnectionLog,
+ "[%p] stream %u, received WINDOW_UPDATE frame with invalid delta %u, sending "
+ "PROTOCOL_ERROR",
+ getConnection(), m_streamID, delta);
+ finishWithError(QNetworkReply::ProtocolFailure, "invalid WINDOW_UPDATE delta"_L1);
+ sendRST_STREAM(PROTOCOL_ERROR);
+ return;
+ }
+ m_sendWindow = sum;
+ // Stream may have been unblocked, so maybe try to write again
+ if (isUploadingDATA())
+ maybeResumeUpload();
+}
+
+/*!
+ \class QHttp2Connection
+ \inmodule QtNetwork
+ \internal
+
+ The QHttp2Connection class represents a HTTP/2 connection.
+ It can only be created through the static functions
+ createDirectConnection(), createUpgradedConnection(),
+ and createDirectServerConnection().
+
+ createDirectServerConnection() is used for server-side connections, and has
+ certain limitations that a client does not.
+
+ As a client you can create a QHttp2Stream with createStream().
+
+ \sa QHttp2Stream
+*/
+
+/*!
+ \fn void QHttp2Connection::newIncomingStream(QHttp2Stream *stream)
+
+ This signal is emitted when a new \a stream is received from the remote
+ peer.
+*/
+
+/*!
+ \fn void QHttp2Connection::newPromisedStream(QHttp2Stream *stream)
+
+ This signal is emitted when the remote peer has promised a new \a stream.
+*/
+
+/*!
+ \fn void QHttp2Connection::errorReceived()
+
+ This signal is emitted when the connection has received an error.
+*/
+
+/*!
+ \fn void QHttp2Connection::connectionClosed()
+
+ This signal is emitted when the connection has been closed.
+*/
+
+/*!
+ \fn void QHttp2Connection::settingsFrameReceived()
+
+ This signal is emitted when the connection has received a SETTINGS frame.
+*/
+
+/*!
+ \fn void QHttp2Connection::errorOccurred(Http2::Http2Error errorCode, const QString &errorString)
+
+ This signal is emitted when the connection has encountered an error. The
+ \a errorCode parameter is the HTTP/2 error code, and the \a errorString
+ parameter is a human-readable description of the error.
+*/
+
+/*!
+ \fn void QHttp2Connection::receivedGOAWAY(quint32 errorCode, quint32 lastStreamID)
+
+ This signal is emitted when the connection has received a GOAWAY frame. The
+ \a errorCode parameter is the HTTP/2 error code, and the \a lastStreamID
+ parameter is the last stream ID that the remote peer will process.
+
+ Any streams of a higher stream ID created by us will be ignored or reset.
+*/
+
+/*!
+ Create a new HTTP2 connection given a \a config and a \a socket.
+ This function assumes that the Upgrade headers etc. in http/1 have already
+ been sent and that the connection is already upgraded to http/2.
+
+ The object returned will be a child to the \a socket, or null on failure.
+*/
+QHttp2Connection *QHttp2Connection::createUpgradedConnection(QIODevice *socket,
+ const QHttp2Configuration &config)
+{
+ Q_ASSERT(socket);
+
+ auto connection = std::unique_ptr<QHttp2Connection>(new QHttp2Connection(socket));
+ connection->setH2Configuration(config);
+ connection->m_connectionType = QHttp2Connection::Type::Client;
+ // HTTP2 connection is already established and request was sent, so stream 1
+ // is already 'active' and is closed for any further outgoing data.
+ QHttp2Stream *stream = connection->createStreamInternal().unwrap();
+ Q_ASSERT(stream->streamID() == 1);
+ stream->setState(QHttp2Stream::State::HalfClosedLocal);
+ connection->m_upgradedConnection = true;
+
+ if (!connection->sendClientPreface()) {
+ qCWarning(qHttp2ConnectionLog, "[%p] Failed to send client preface", connection.get());
+ return nullptr;
+ }
+
+ return connection.release();
+}
+
+/*!
+ Create a new HTTP2 connection given a \a config and a \a socket.
+ This function will immediately send the client preface.
+
+ The object returned will be a child to the \a socket, or null on failure.
+*/
+QHttp2Connection *QHttp2Connection::createDirectConnection(QIODevice *socket,
+ const QHttp2Configuration &config)
+{
+ auto connection = std::unique_ptr<QHttp2Connection>(new QHttp2Connection(socket));
+ connection->setH2Configuration(config);
+ connection->m_connectionType = QHttp2Connection::Type::Client;
+
+ if (!connection->sendClientPreface()) {
+ qCWarning(qHttp2ConnectionLog, "[%p] Failed to send client preface", connection.get());
+ return nullptr;
+ }
+
+ return connection.release();
+}
+
+/*!
+ Create a new HTTP2 connection given a \a config and a \a socket.
+
+ The object returned will be a child to the \a socket, or null on failure.
+*/
+QHttp2Connection *QHttp2Connection::createDirectServerConnection(QIODevice *socket,
+ const QHttp2Configuration &config)
+{
+ auto connection = std::unique_ptr<QHttp2Connection>(new QHttp2Connection(socket));
+ connection->setH2Configuration(config);
+ connection->m_connectionType = QHttp2Connection::Type::Server;
+
+ connection->m_nextStreamID = 2; // server-initiated streams must be even
+
+ connection->m_waitingForClientPreface = true;
+
+ return connection.release();
+}
+
+/*!
+ Creates a stream on this connection.
+
+ Automatically picks the next available stream ID and returns a pointer to
+ the new stream, if possible. Otherwise returns an error.
+
+ \sa QHttp2Connection::CreateStreamError, QHttp2Stream
+*/
+QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError> QHttp2Connection::createStream()
+{
+ Q_ASSERT(m_connectionType == Type::Client); // This overload is just for clients
+ if (m_nextStreamID > lastValidStreamID)
+ return { QHttp2Connection::CreateStreamError::StreamIdsExhausted };
+ return createStreamInternal();
+}
+
+QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError>
+QHttp2Connection::createStreamInternal()
+{
+ if (m_goingAway)
+ return { QHttp2Connection::CreateStreamError::ReceivedGOAWAY };
+ const quint32 streamID = m_nextStreamID;
+ if (size_t(m_maxConcurrentStreams) <= size_t(numActiveLocalStreams()))
+ return { QHttp2Connection::CreateStreamError::MaxConcurrentStreamsReached };
+ m_nextStreamID += 2;
+ return { createStreamInternal_impl(streamID) };
+}
+
+QHttp2Stream *QHttp2Connection::createStreamInternal_impl(quint32 streamID)
+{
+ qsizetype numStreams = m_streams.size();
+ QPointer<QHttp2Stream> &stream = m_streams[streamID];
+ if (numStreams == m_streams.size()) // stream already existed
+ return nullptr;
+ stream = new QHttp2Stream(this, streamID);
+ stream->m_recvWindow = streamInitialReceiveWindowSize;
+ stream->m_sendWindow = streamInitialSendWindowSize;
+ return stream;
+}
+
+qsizetype QHttp2Connection::numActiveStreamsImpl(quint32 mask) const noexcept
+{
+ const auto shouldCount = [mask](const QPointer<QHttp2Stream> &stream) -> bool {
+ return stream && (stream->streamID() & 1) == mask;
+ };
+ return std::count_if(m_streams.cbegin(), m_streams.cend(), shouldCount);
+}
+
+/*!
+ \internal
+ The number of streams the remote peer has started that are still active.
+*/
+qsizetype QHttp2Connection::numActiveRemoteStreams() const noexcept
+{
+ const quint32 RemoteMask = m_connectionType == Type::Client ? 0 : 1;
+ return numActiveStreamsImpl(RemoteMask);
+}
+
+/*!
+ \internal
+ The number of streams we have started that are still active.
+*/
+qsizetype QHttp2Connection::numActiveLocalStreams() const noexcept
+{
+ const quint32 LocalMask = m_connectionType == Type::Client ? 1 : 0;
+ return numActiveStreamsImpl(LocalMask);
+}
+
+/*!
+ Return a pointer to a stream with the given \a streamID, or null if no such
+ stream exists or it was deleted.
+*/
+QHttp2Stream *QHttp2Connection::getStream(quint32 streamID) const
+{
+ return m_streams.value(streamID, nullptr).get();
+}
+
+
+/*!
+ \fn QHttp2Stream *QHttp2Connection::promisedStream(const QUrl &streamKey) const
+
+ Returns a pointer to the stream that was promised with the given
+ \a streamKey, if any. Otherwise, returns null.
+*/
+
+/*!
+ \fn void QHttp2Connection::close()
+
+ This sends a GOAWAY frame on the connection stream, gracefully closing the
+ connection.
+*/
+
+/*!
+ \fn bool QHttp2Connection::isGoingAway() const noexcept
+
+ Returns \c true if the connection is in the process of being closed, or
+ \c false otherwise.
+*/
+
+/*!
+ \fn quint32 QHttp2Connection::maxConcurrentStreams() const noexcept
+
+ Returns the maximum number of concurrent streams we are allowed to have
+ active at any given time. This is a directional setting, and the remote
+ peer may have a different value.
+*/
+
+/*!
+ \fn quint32 QHttp2Connection::maxHeaderListSize() const noexcept
+
+ Returns the maximum size of the header which the peer is willing to accept.
+*/
+
+/*!
+ \fn bool QHttp2Connection::isUpgradedConnection() const noexcept
+
+ Returns \c true if this connection was created as a result of an HTTP/1
+ upgrade to HTTP/2, or \c false otherwise.
+*/
+
+QHttp2Connection::QHttp2Connection(QIODevice *socket) : QObject(socket)
+{
+ Q_ASSERT(socket);
+ Q_ASSERT(socket->isOpen());
+ Q_ASSERT(socket->openMode() & QIODevice::ReadWrite);
+ // We don't make any connections directly because this is used in
+ // in the http2 protocol handler, which is used by
+ // QHttpNetworkConnectionChannel. Which in turn owns and deals with all the
+ // socket connections.
+}
+
+QHttp2Connection::~QHttp2Connection() noexcept
+{
+ // delete streams now so that any calls it might make back to this
+ // Connection will operate on a valid object.
+ for (QPointer<QHttp2Stream> &stream : std::exchange(m_streams, {}))
+ delete stream.get();
+}
+
+bool QHttp2Connection::serverCheckClientPreface()
+{
+ if (!m_waitingForClientPreface)
+ return true;
+ auto *socket = getSocket();
+ if (socket->bytesAvailable() < Http2::clientPrefaceLength)
+ return false;
+ if (!readClientPreface()) {
+ socket->close();
+ emit errorOccurred(Http2Error::PROTOCOL_ERROR, "invalid client preface"_L1);
+ qCDebug(qHttp2ConnectionLog, "[%p] Invalid client preface", this);
+ return false;
+ }
+ qCDebug(qHttp2ConnectionLog, "[%p] Peer sent valid client preface", this);
+ m_waitingForClientPreface = false;
+ if (!sendServerPreface()) {
+ connectionError(Http2::INTERNAL_ERROR, "Failed to send server preface");
+ return false;
+ }
+ return true;
+}
+
+bool QHttp2Connection::sendPing()
+{
+ std::array<char, 8> data;
+
+ QRandomGenerator gen;
+ gen.generate(data.begin(), data.end());
+ return sendPing(data);
+}
+
+bool QHttp2Connection::sendPing(QByteArrayView data)
+{
+ frameWriter.start(FrameType::PING, FrameFlag::EMPTY, connectionStreamID);
+
+ Q_ASSERT(data.length() == 8);
+ if (!m_lastPingSignature) {
+ m_lastPingSignature = data.toByteArray();
+ } else {
+ qCWarning(qHttp2ConnectionLog, "[%p] No PING is sent while waiting for the previous PING.", this);
+ return false;
+ }
+
+ frameWriter.append((uchar*)data.data(), (uchar*)data.end());
+ frameWriter.write(*getSocket());
+ return true;
+}
+
+/*!
+ This function must be called when you have received a readyRead signal
+ (or equivalent) from the QIODevice. It will read and process any incoming
+ HTTP/2 frames and emit signals as appropriate.
+*/
+void QHttp2Connection::handleReadyRead()
+{
+ /* event loop */
+ if (m_connectionType == Type::Server && !serverCheckClientPreface())
+ return;
+
+ const auto streamIsActive = [](const QPointer<QHttp2Stream> &stream) {
+ return stream && stream->isActive();
+ };
+ if (m_goingAway && std::none_of(m_streams.cbegin(), m_streams.cend(), streamIsActive)) {
+ close();
+ return;
+ }
+ QIODevice *socket = getSocket();
+
+ qCDebug(qHttp2ConnectionLog, "[%p] Receiving data, %lld bytes available", this,
+ socket->bytesAvailable());
+
+ using namespace Http2;
+ while (!m_goingAway || std::any_of(m_streams.cbegin(), m_streams.cend(), streamIsActive)) {
+ const auto result = frameReader.read(*socket);
+ if (result != FrameStatus::goodFrame)
+ qCDebug(qHttp2ConnectionLog, "[%p] Tried to read frame, got %d", this, int(result));
+ switch (result) {
+ case FrameStatus::incompleteFrame:
+ return;
+ case FrameStatus::protocolError:
+ return connectionError(PROTOCOL_ERROR, "invalid frame");
+ case FrameStatus::sizeError:
+ return connectionError(FRAME_SIZE_ERROR, "invalid frame size");
+ default:
+ break;
+ }
+
+ Q_ASSERT(result == FrameStatus::goodFrame);
+
+ inboundFrame = std::move(frameReader.inboundFrame());
+
+ const auto frameType = inboundFrame.type();
+ qCDebug(qHttp2ConnectionLog, "[%p] Successfully read a frame, with type: %d", this,
+ int(frameType));
+ if (continuationExpected && frameType != FrameType::CONTINUATION)
+ return connectionError(PROTOCOL_ERROR, "CONTINUATION expected");
+
+ switch (frameType) {
+ case FrameType::DATA:
+ handleDATA();
+ break;
+ case FrameType::HEADERS:
+ handleHEADERS();
+ break;
+ case FrameType::PRIORITY:
+ handlePRIORITY();
+ break;
+ case FrameType::RST_STREAM:
+ handleRST_STREAM();
+ break;
+ case FrameType::SETTINGS:
+ handleSETTINGS();
+ break;
+ case FrameType::PUSH_PROMISE:
+ handlePUSH_PROMISE();
+ break;
+ case FrameType::PING:
+ handlePING();
+ break;
+ case FrameType::GOAWAY:
+ handleGOAWAY();
+ break;
+ case FrameType::WINDOW_UPDATE:
+ handleWINDOW_UPDATE();
+ break;
+ case FrameType::CONTINUATION:
+ handleCONTINUATION();
+ break;
+ case FrameType::LAST_FRAME_TYPE:
+ // 5.1 - ignore unknown frames.
+ break;
+ }
+ }
+}
+
+bool QHttp2Connection::readClientPreface()
+{
+ auto *socket = getSocket();
+ Q_ASSERT(socket->bytesAvailable() >= Http2::clientPrefaceLength);
+ char buffer[Http2::clientPrefaceLength];
+ const qint64 read = socket->read(buffer, Http2::clientPrefaceLength);
+ if (read != Http2::clientPrefaceLength)
+ return false;
+ return memcmp(buffer, Http2::Http2clientPreface, Http2::clientPrefaceLength) == 0;
+}
+
+/*!
+ This function must be called when the socket has been disconnected, and will
+ end all remaining streams with an error.
+*/
+void QHttp2Connection::handleConnectionClosure()
+{
+ const auto errorString = QCoreApplication::translate("QHttp", "Connection closed");
+ for (auto it = m_streams.begin(), end = m_streams.end(); it != end; ++it) {
+ auto stream = it.value();
+ if (stream && stream->isActive())
+ stream->finishWithError(QNetworkReply::RemoteHostClosedError, errorString);
+ }
+}
+
+void QHttp2Connection::setH2Configuration(QHttp2Configuration config)
+{
+ m_config = std::move(config);
+
+ // These values comes from our own API so trust it to be sane.
+ maxSessionReceiveWindowSize = qint32(m_config.sessionReceiveWindowSize());
+ pushPromiseEnabled = m_config.serverPushEnabled();
+ streamInitialReceiveWindowSize = qint32(m_config.streamReceiveWindowSize());
+ encoder.setCompressStrings(m_config.huffmanCompressionEnabled());
+}
+
+void QHttp2Connection::connectionError(Http2Error errorCode, const char *message)
+{
+ Q_ASSERT(message);
+ if (m_goingAway)
+ return;
+
+ qCCritical(qHttp2ConnectionLog, "[%p] Connection error: %s (%d)", this, message,
+ int(errorCode));
+
+ m_goingAway = true;
+ sendGOAWAY(errorCode);
+ const auto error = qt_error(errorCode);
+ auto messageView = QLatin1StringView(message);
+
+ for (QHttp2Stream *stream : std::as_const(m_streams)) {
+ if (stream && stream->isActive())
+ stream->finishWithError(error, messageView);
+ }
+
+ closeSession();
+}
+
+void QHttp2Connection::closeSession()
+{
+ emit connectionClosed();
+}
+
+bool QHttp2Connection::streamWasReset(quint32 streamID) noexcept
+{
+ return m_resetStreamIDs.contains(streamID);
+}
+
+bool QHttp2Connection::isInvalidStream(quint32 streamID) noexcept
+{
+ auto stream = m_streams.value(streamID, nullptr);
+ return !stream && !streamWasReset(streamID);
+}
+
+bool QHttp2Connection::sendClientPreface()
+{
+ QIODevice *socket = getSocket();
+ // 3.5 HTTP/2 Connection Preface
+ const qint64 written = socket->write(Http2clientPreface, clientPrefaceLength);
+ if (written != clientPrefaceLength)
+ return false;
+
+ if (!sendSETTINGS()) {
+ qCWarning(qHttp2ConnectionLog, "[%p] Failed to send SETTINGS", this);
+ return false;
+ }
+ return true;
+}
+
+bool QHttp2Connection::sendServerPreface()
+{
+ // We send our SETTINGS frame and ACK the client's SETTINGS frame when it
+ // arrives.
+ if (!sendSETTINGS()) {
+ qCWarning(qHttp2ConnectionLog, "[%p] Failed to send SETTINGS", this);
+ return false;
+ }
+ return true;
+}
+
+bool QHttp2Connection::sendSETTINGS()
+{
+ QIODevice *socket = getSocket();
+ // 6.5 SETTINGS
+ frameWriter.setOutboundFrame(configurationToSettingsFrame(m_config));
+ qCDebug(qHttp2ConnectionLog, "[%p] Sending SETTINGS frame, %d bytes", this,
+ frameWriter.outboundFrame().payloadSize());
+ Q_ASSERT(frameWriter.outboundFrame().payloadSize());
+
+ if (!frameWriter.write(*socket))
+ return false;
+
+ sessionReceiveWindowSize = maxSessionReceiveWindowSize;
+ // We only send WINDOW_UPDATE for the connection if the size differs from the
+ // default 64 KB:
+ const auto delta = maxSessionReceiveWindowSize - defaultSessionWindowSize;
+ if (delta && !sendWINDOW_UPDATE(connectionStreamID, delta))
+ return false;
+
+ waitingForSettingsACK = true;
+ return true;
+}
+
+bool QHttp2Connection::sendWINDOW_UPDATE(quint32 streamID, quint32 delta)
+{
+ qCDebug(qHttp2ConnectionLog, "[%p] Sending WINDOW_UPDATE frame, stream %d, delta %u", this,
+ streamID, delta);
+ frameWriter.start(FrameType::WINDOW_UPDATE, FrameFlag::EMPTY, streamID);
+ frameWriter.append(delta);
+ return frameWriter.write(*getSocket());
+}
+
+bool QHttp2Connection::sendGOAWAY(quint32 errorCode)
+{
+ frameWriter.start(FrameType::GOAWAY, FrameFlag::EMPTY,
+ Http2PredefinedParameters::connectionStreamID);
+ frameWriter.append(quint32(m_lastIncomingStreamID));
+ frameWriter.append(errorCode);
+ return frameWriter.write(*getSocket());
+}
+
+bool QHttp2Connection::sendSETTINGS_ACK()
+{
+ frameWriter.start(FrameType::SETTINGS, FrameFlag::ACK, Http2::connectionStreamID);
+ return frameWriter.write(*getSocket());
+}
+
+void QHttp2Connection::handleDATA()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::DATA);
+
+ const auto streamID = inboundFrame.streamID();
+ if (streamID == connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "DATA on the connection stream");
+
+ if (isInvalidStream(streamID))
+ return connectionError(ENHANCE_YOUR_CALM, "DATA on invalid stream");
+
+ if (qint32(inboundFrame.payloadSize()) > sessionReceiveWindowSize) {
+ qCDebug(qHttp2ConnectionLog,
+ "[%p] Received DATA frame with payload size %u, "
+ "but recvWindow is %d, sending FLOW_CONTROL_ERROR",
+ this, inboundFrame.payloadSize(), sessionReceiveWindowSize);
+ return connectionError(FLOW_CONTROL_ERROR, "Flow control error");
+ }
+
+ sessionReceiveWindowSize -= inboundFrame.payloadSize();
+
+ auto it = m_streams.constFind(streamID);
+ if (it != m_streams.cend() && it.value())
+ it.value()->handleDATA(inboundFrame);
+
+ if (sessionReceiveWindowSize < maxSessionReceiveWindowSize / 2) {
+ // @future[consider]: emit signal instead
+ QMetaObject::invokeMethod(this, &QHttp2Connection::sendWINDOW_UPDATE, Qt::QueuedConnection,
+ quint32(connectionStreamID),
+ quint32(maxSessionReceiveWindowSize - sessionReceiveWindowSize));
+ sessionReceiveWindowSize = maxSessionReceiveWindowSize;
+ }
+}
+
+void QHttp2Connection::handleHEADERS()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::HEADERS);
+
+ const auto streamID = inboundFrame.streamID();
+ qCDebug(qHttp2ConnectionLog, "[%p] Received HEADERS frame on stream %d", this, streamID);
+
+ if (streamID == connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "HEADERS on 0x0 stream");
+
+ const bool isClient = m_connectionType == Type::Client;
+ const bool isClientInitiatedStream = !!(streamID & 1);
+ const bool isRemotelyInitiatedStream = isClient ^ isClientInitiatedStream;
+
+ if (isRemotelyInitiatedStream && streamID > m_lastIncomingStreamID) {
+ QHttp2Stream *newStream = createStreamInternal_impl(streamID);
+ Q_ASSERT(newStream);
+ m_lastIncomingStreamID = streamID;
+ qCDebug(qHttp2ConnectionLog, "[%p] Created new incoming stream %d", this, streamID);
+ emit newIncomingStream(newStream);
+ } else if (auto it = m_streams.constFind(streamID); it == m_streams.cend()) {
+ qCDebug(qHttp2ConnectionLog, "[%p] Received HEADERS on non-existent stream %d", this,
+ streamID);
+ return connectionError(PROTOCOL_ERROR, "HEADERS on invalid stream");
+ } else if (!*it || (*it)->wasReset()) {
+ qCDebug(qHttp2ConnectionLog, "[%p] Received HEADERS on reset stream %d", this, streamID);
+ return connectionError(ENHANCE_YOUR_CALM, "HEADERS on invalid stream");
+ }
+
+ const auto flags = inboundFrame.flags();
+ if (flags.testFlag(FrameFlag::PRIORITY)) {
+ qCDebug(qHttp2ConnectionLog, "[%p] HEADERS frame on stream %d has PRIORITY flag", this,
+ streamID);
+ handlePRIORITY();
+ if (m_goingAway)
+ return;
+ }
+
+ const bool endHeaders = flags.testFlag(FrameFlag::END_HEADERS);
+ continuedFrames.clear();
+ continuedFrames.push_back(std::move(inboundFrame));
+ if (!endHeaders) {
+ continuationExpected = true;
+ return;
+ }
+
+ handleContinuedHEADERS();
+}
+
+void QHttp2Connection::handlePRIORITY()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::PRIORITY
+ || inboundFrame.type() == FrameType::HEADERS);
+
+ const auto streamID = inboundFrame.streamID();
+ if (streamID == connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "PRIORITY on 0x0 stream");
+
+ if (isInvalidStream(streamID))
+ return connectionError(ENHANCE_YOUR_CALM, "PRIORITY on invalid stream");
+
+ quint32 streamDependency = 0;
+ uchar weight = 0;
+ const bool noErr = inboundFrame.priority(&streamDependency, &weight);
+ Q_UNUSED(noErr);
+ Q_ASSERT(noErr);
+
+ const bool exclusive = streamDependency & 0x80000000;
+ streamDependency &= ~0x80000000;
+
+ // Ignore this for now ...
+ // Can be used for streams (re)prioritization - 5.3
+ Q_UNUSED(exclusive);
+ Q_UNUSED(weight);
+}
+
+void QHttp2Connection::handleRST_STREAM()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::RST_STREAM);
+
+ // "RST_STREAM frames MUST be associated with a stream.
+ // If a RST_STREAM frame is received with a stream identifier of 0x0,
+ // the recipient MUST treat this as a connection error (Section 5.4.1)
+ // of type PROTOCOL_ERROR.
+ const auto streamID = inboundFrame.streamID();
+ if (streamID == connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "RST_STREAM on 0x0");
+
+ if (!(streamID & 0x1)) { // @future[server]: must be updated for server-side handling
+ // RST_STREAM on a promised stream:
+ // since we do not keep track of such streams,
+ // just ignore.
+ return;
+ }
+
+ // Anything greater than m_nextStreamID has not been started yet.
+ if (streamID >= m_nextStreamID) {
+ // "RST_STREAM frames MUST NOT be sent for a stream
+ // in the "idle" state. .. the recipient MUST treat this
+ // as a connection error (Section 5.4.1) of type PROTOCOL_ERROR."
+ return connectionError(PROTOCOL_ERROR, "RST_STREAM on idle stream");
+ }
+
+ Q_ASSERT(inboundFrame.dataSize() == 4);
+
+ if (QPointer<QHttp2Stream> stream = m_streams[streamID])
+ stream->handleRST_STREAM(inboundFrame);
+}
+
+void QHttp2Connection::handleSETTINGS()
+{
+ // 6.5 SETTINGS.
+ Q_ASSERT(inboundFrame.type() == FrameType::SETTINGS);
+
+ if (inboundFrame.streamID() != connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "SETTINGS on invalid stream");
+
+ if (inboundFrame.flags().testFlag(FrameFlag::ACK)) {
+ if (!waitingForSettingsACK)
+ return connectionError(PROTOCOL_ERROR, "unexpected SETTINGS ACK");
+ qCDebug(qHttp2ConnectionLog, "[%p] Received SETTINGS ACK", this);
+ waitingForSettingsACK = false;
+ return;
+ }
+ qCDebug(qHttp2ConnectionLog, "[%p] Received SETTINGS frame", this);
+
+ if (inboundFrame.dataSize()) {
+ auto src = inboundFrame.dataBegin();
+ for (const uchar *end = src + inboundFrame.dataSize(); src != end; src += 6) {
+ const Settings identifier = Settings(qFromBigEndian<quint16>(src));
+ const quint32 intVal = qFromBigEndian<quint32>(src + 2);
+ if (!acceptSetting(identifier, intVal)) {
+ // If not accepted - we finish with connectionError.
+ qCDebug(qHttp2ConnectionLog, "[%p] Received an unacceptable setting, %u, %u", this,
+ quint32(identifier), intVal);
+ return; // connectionError already called in acceptSetting.
+ }
+ }
+ }
+
+ qCDebug(qHttp2ConnectionLog, "[%p] Sending SETTINGS ACK", this);
+ emit settingsFrameReceived();
+ sendSETTINGS_ACK();
+}
+
+void QHttp2Connection::handlePUSH_PROMISE()
+{
+ // 6.6 PUSH_PROMISE.
+ Q_ASSERT(inboundFrame.type() == FrameType::PUSH_PROMISE);
+
+ if (!pushPromiseEnabled && !waitingForSettingsACK) {
+ // This means, server ACKed our 'NO PUSH',
+ // but sent us PUSH_PROMISE anyway.
+ return connectionError(PROTOCOL_ERROR, "unexpected PUSH_PROMISE frame");
+ }
+
+ const auto streamID = inboundFrame.streamID();
+ if (streamID == connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "PUSH_PROMISE with invalid associated stream (0x0)");
+
+ auto it = m_streams.constFind(streamID);
+#if 0 // Needs to be done after some timeout in case the stream has only just been reset
+ if (it != m_streams.constEnd()) {
+ QHttp2Stream *associatedStream = it->get();
+ if (associatedStream->state() != QHttp2Stream::State::Open
+ && associatedStream->state() != QHttp2Stream::State::HalfClosedLocal) {
+ // Cause us to error out below:
+ it = m_streams.constEnd();
+ }
+ }
+#endif
+ if (it == m_streams.constEnd())
+ return connectionError(ENHANCE_YOUR_CALM, "PUSH_PROMISE with invalid associated stream");
+
+ const auto reservedID = qFromBigEndian<quint32>(inboundFrame.dataBegin());
+ if ((reservedID & 1) || reservedID <= m_lastIncomingStreamID || reservedID > lastValidStreamID)
+ return connectionError(PROTOCOL_ERROR, "PUSH_PROMISE with invalid promised stream ID");
+
+ auto *stream = createStreamInternal_impl(reservedID);
+ if (!stream)
+ return connectionError(PROTOCOL_ERROR, "PUSH_PROMISE with already active stream ID");
+ m_lastIncomingStreamID = reservedID;
+ stream->setState(QHttp2Stream::State::ReservedRemote);
+
+ if (!pushPromiseEnabled) {
+ // "ignoring a PUSH_PROMISE frame causes the stream state to become
+ // indeterminate" - let's send RST_STREAM frame with REFUSE_STREAM code.
+ stream->sendRST_STREAM(REFUSE_STREAM);
+ }
+
+ const bool endHeaders = inboundFrame.flags().testFlag(FrameFlag::END_HEADERS);
+ continuedFrames.clear();
+ continuedFrames.push_back(std::move(inboundFrame));
+
+ if (!endHeaders) {
+ continuationExpected = true;
+ return;
+ }
+
+ handleContinuedHEADERS();
+}
+
+void QHttp2Connection::handlePING()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::PING);
+ Q_ASSERT(inboundFrame.dataSize() == 8);
+
+ if (inboundFrame.streamID() != connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "PING on invalid stream");
+
+ if (inboundFrame.flags() & FrameFlag::ACK) {
+ QByteArrayView pingSignature(reinterpret_cast<const char *>(inboundFrame.dataBegin()), 8);
+ if (!m_lastPingSignature.has_value()) {
+ emit pingFrameRecived(PingState::PongNoPingSent);
+ qCWarning(qHttp2ConnectionLog, "[%p] PING with ACK received but no PING was sent.", this);
+ } else if (pingSignature != m_lastPingSignature) {
+ emit pingFrameRecived(PingState::PongSignatureChanged);
+ qCWarning(qHttp2ConnectionLog, "[%p] PING signature does not match the last PING.", this);
+ } else {
+ emit pingFrameRecived(PingState::PongSignatureIdentical);
+ }
+ m_lastPingSignature.reset();
+ return;
+ } else {
+ emit pingFrameRecived(PingState::Ping);
+
+ }
+
+
+ frameWriter.start(FrameType::PING, FrameFlag::ACK, connectionStreamID);
+ frameWriter.append(inboundFrame.dataBegin(), inboundFrame.dataBegin() + 8);
+ frameWriter.write(*getSocket());
+}
+
+void QHttp2Connection::handleGOAWAY()
+{
+ // 6.8 GOAWAY
+
+ Q_ASSERT(inboundFrame.type() == FrameType::GOAWAY);
+ // "An endpoint MUST treat a GOAWAY frame with a stream identifier
+ // other than 0x0 as a connection error (Section 5.4.1) of type PROTOCOL_ERROR."
+ if (inboundFrame.streamID() != connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "GOAWAY on invalid stream");
+
+ const uchar *const src = inboundFrame.dataBegin();
+ quint32 lastStreamID = qFromBigEndian<quint32>(src);
+ const quint32 errorCode = qFromBigEndian<quint32>(src + 4);
+
+ if (!lastStreamID) {
+ // "The last stream identifier can be set to 0 if no
+ // streams were processed."
+ lastStreamID = 1;
+ } else if (!(lastStreamID & 0x1)) {
+ // 5.1.1 - we (client) use only odd numbers as stream identifiers.
+ return connectionError(PROTOCOL_ERROR, "GOAWAY with invalid last stream ID");
+ } else if (lastStreamID >= m_nextStreamID) {
+ // "A server that is attempting to gracefully shut down a connection SHOULD
+ // send an initial GOAWAY frame with the last stream identifier set to 2^31-1
+ // and a NO_ERROR code."
+ if (lastStreamID != lastValidStreamID || errorCode != HTTP2_NO_ERROR)
+ return connectionError(PROTOCOL_ERROR, "GOAWAY invalid stream/error code");
+ } else {
+ lastStreamID += 2;
+ }
+
+ m_goingAway = true;
+
+ emit receivedGOAWAY(errorCode, lastStreamID);
+
+ for (quint32 id = lastStreamID; id < m_nextStreamID; id += 2) {
+ QHttp2Stream *stream = m_streams.value(id, nullptr);
+ if (stream && stream->isActive())
+ stream->finishWithError(errorCode, "Received GOAWAY"_L1);
+ }
+
+ const auto isActive = [](const QHttp2Stream *stream) { return stream && stream->isActive(); };
+ if (std::none_of(m_streams.cbegin(), m_streams.cend(), isActive))
+ closeSession();
+}
+
+void QHttp2Connection::handleWINDOW_UPDATE()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::WINDOW_UPDATE);
+
+ const quint32 delta = qFromBigEndian<quint32>(inboundFrame.dataBegin());
+ const bool valid = delta && delta <= quint32(std::numeric_limits<qint32>::max());
+ const auto streamID = inboundFrame.streamID();
+
+ qCDebug(qHttp2ConnectionLog(), "[%p] Received WINDOW_UPDATE, stream %d, delta %d", this,
+ streamID, delta);
+ if (streamID == connectionStreamID) {
+ qint32 sum = 0;
+ if (!valid || qAddOverflow(sessionSendWindowSize, qint32(delta), &sum))
+ return connectionError(PROTOCOL_ERROR, "WINDOW_UPDATE invalid delta");
+ sessionSendWindowSize = sum;
+ for (auto &stream : m_streams) {
+ if (!stream || !stream->isActive())
+ continue;
+ // Stream may have been unblocked, so maybe try to write again
+ if (stream->isUploadingDATA() && !stream->isUploadBlocked())
+ QMetaObject::invokeMethod(stream, &QHttp2Stream::maybeResumeUpload,
+ Qt::QueuedConnection);
+ }
+ } else {
+ QHttp2Stream *stream = m_streams.value(streamID);
+ if (!stream || !stream->isActive()) {
+ // WINDOW_UPDATE on closed streams can be ignored.
+ qCDebug(qHttp2ConnectionLog, "[%p] Received WINDOW_UPDATE on closed stream %d", this,
+ streamID);
+ return;
+ }
+ stream->handleWINDOW_UPDATE(inboundFrame);
+ }
+}
+
+void QHttp2Connection::handleCONTINUATION()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::CONTINUATION);
+ if (continuedFrames.empty())
+ return connectionError(PROTOCOL_ERROR,
+ "CONTINUATION without a preceding HEADERS or PUSH_PROMISE");
+
+ if (inboundFrame.streamID() != continuedFrames.front().streamID())
+ return connectionError(PROTOCOL_ERROR, "CONTINUATION on invalid stream");
+
+ const bool endHeaders = inboundFrame.flags().testFlag(FrameFlag::END_HEADERS);
+ continuedFrames.push_back(std::move(inboundFrame));
+
+ if (!endHeaders)
+ return;
+
+ continuationExpected = false;
+ handleContinuedHEADERS();
+}
+
+void QHttp2Connection::handleContinuedHEADERS()
+{
+ // 'Continued' HEADERS can be: the initial HEADERS/PUSH_PROMISE frame
+ // with/without END_HEADERS flag set plus, if no END_HEADERS flag,
+ // a sequence of one or more CONTINUATION frames.
+ Q_ASSERT(!continuedFrames.empty());
+ const auto firstFrameType = continuedFrames[0].type();
+ Q_ASSERT(firstFrameType == FrameType::HEADERS || firstFrameType == FrameType::PUSH_PROMISE);
+
+ const auto streamID = continuedFrames[0].streamID();
+
+ const auto streamIt = m_streams.constFind(streamID);
+ if (firstFrameType == FrameType::HEADERS) {
+ if (streamIt != m_streams.cend()) {
+ QHttp2Stream *stream = streamIt.value();
+ if (stream->state() != QHttp2Stream::State::HalfClosedLocal
+ && stream->state() != QHttp2Stream::State::ReservedRemote
+ && stream->state() != QHttp2Stream::State::Idle
+ && stream->state() != QHttp2Stream::State::Open) {
+ // We can receive HEADERS on streams initiated by our requests
+ // (these streams are in halfClosedLocal or open state) or
+ // remote-reserved streams from a server's PUSH_PROMISE.
+ stream->finishWithError(QNetworkReply::ProtocolFailure,
+ "HEADERS on invalid stream"_L1);
+ stream->sendRST_STREAM(CANCEL);
+ return;
+ }
+ }
+ // Else: we cannot just ignore our peer's HEADERS frames - they change
+ // HPACK context - even though the stream was reset; apparently the peer
+ // has yet to see the reset.
+ }
+
+ std::vector<uchar> hpackBlock(assemble_hpack_block(continuedFrames));
+ const bool hasHeaderFields = !hpackBlock.empty();
+ if (hasHeaderFields) {
+ HPack::BitIStream inputStream{ hpackBlock.data(), hpackBlock.data() + hpackBlock.size() };
+ if (!decoder.decodeHeaderFields(inputStream))
+ return connectionError(COMPRESSION_ERROR, "HPACK decompression failed");
+ } else {
+ if (firstFrameType == FrameType::PUSH_PROMISE) {
+ // It could be a PRIORITY sent in HEADERS - already handled by this
+ // point in handleHEADERS. If it was PUSH_PROMISE (HTTP/2 8.2.1):
+ // "The header fields in PUSH_PROMISE and any subsequent CONTINUATION
+ // frames MUST be a valid and complete set of request header fields
+ // (Section 8.1.2.3) ... If a client receives a PUSH_PROMISE that does
+ // not include a complete and valid set of header fields or the :method
+ // pseudo-header field identifies a method that is not safe, it MUST
+ // respond with a stream error (Section 5.4.2) of type PROTOCOL_ERROR."
+ if (streamIt != m_streams.cend())
+ (*streamIt)->sendRST_STREAM(PROTOCOL_ERROR);
+ return;
+ }
+
+ // We got back an empty hpack block. Now let's figure out if there was an error.
+ constexpr auto hpackBlockHasContent = [](const auto &c) { return c.hpackBlockSize() > 0; };
+ const bool anyHpackBlock = std::any_of(continuedFrames.cbegin(), continuedFrames.cend(),
+ hpackBlockHasContent);
+ if (anyHpackBlock) // There was hpack block data, but returned empty => it overflowed.
+ return connectionError(FRAME_SIZE_ERROR, "HEADERS frame too large");
+ }
+
+ if (streamIt == m_streams.cend()) // No more processing without a stream from here on.
+ return;
+
+ switch (firstFrameType) {
+ case FrameType::HEADERS:
+ streamIt.value()->handleHEADERS(continuedFrames[0].flags(), decoder.decodedHeader());
+ break;
+ case FrameType::PUSH_PROMISE: {
+ std::optional<QUrl> promiseKey = HPack::makePromiseKeyUrl(decoder.decodedHeader());
+ if (!promiseKey)
+ return; // invalid URL/key !
+ if (m_promisedStreams.contains(*promiseKey))
+ return; // already promised!
+ const auto promiseID = qFromBigEndian<quint32>(continuedFrames[0].dataBegin());
+ QHttp2Stream *stream = m_streams.value(promiseID);
+ stream->transitionState(QHttp2Stream::StateTransition::CloseLocal);
+ stream->handleHEADERS(continuedFrames[0].flags(), decoder.decodedHeader());
+ emit newPromisedStream(stream); // @future[consider] add promise key as argument?
+ m_promisedStreams.emplace(*promiseKey, promiseID);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+bool QHttp2Connection::acceptSetting(Http2::Settings identifier, quint32 newValue)
+{
+ switch (identifier) {
+ case Settings::HEADER_TABLE_SIZE_ID: {
+ qCDebug(qHttp2ConnectionLog, "[%p] Received SETTINGS HEADER_TABLE_SIZE %d", this, newValue);
+ if (newValue > maxAcceptableTableSize) {
+ connectionError(PROTOCOL_ERROR, "SETTINGS invalid table size");
+ return false;
+ }
+ encoder.setMaxDynamicTableSize(newValue);
+ break;
+ }
+ case Settings::INITIAL_WINDOW_SIZE_ID: {
+ qCDebug(qHttp2ConnectionLog, "[%p] Received SETTINGS INITIAL_WINDOW_SIZE %d", this,
+ newValue);
+ // For every active stream - adjust its window
+ // (and handle possible overflows as errors).
+ if (newValue > quint32(std::numeric_limits<qint32>::max())) {
+ connectionError(FLOW_CONTROL_ERROR, "SETTINGS invalid initial window size");
+ return false;
+ }
+
+ const qint32 delta = qint32(newValue) - streamInitialSendWindowSize;
+ streamInitialSendWindowSize = qint32(newValue);
+
+ qCDebug(qHttp2ConnectionLog, "[%p] Adjusting initial window size for %zu streams by %d",
+ this, size_t(m_streams.size()), delta);
+ for (const QPointer<QHttp2Stream> &stream : std::as_const(m_streams)) {
+ if (!stream || !stream->isActive())
+ continue;
+ qint32 sum = 0;
+ if (qAddOverflow(stream->m_sendWindow, delta, &sum)) {
+ stream->sendRST_STREAM(PROTOCOL_ERROR);
+ stream->finishWithError(QNetworkReply::ProtocolFailure,
+ "SETTINGS window overflow"_L1);
+ continue;
+ }
+ stream->m_sendWindow = sum;
+ if (delta > 0 && stream->isUploadingDATA() && !stream->isUploadBlocked()) {
+ QMetaObject::invokeMethod(stream, &QHttp2Stream::maybeResumeUpload,
+ Qt::QueuedConnection);
+ }
+ }
+ break;
+ }
+ case Settings::MAX_CONCURRENT_STREAMS_ID: {
+ qCDebug(qHttp2ConnectionLog, "[%p] Received SETTINGS MAX_CONCURRENT_STREAMS %d", this,
+ newValue);
+ m_maxConcurrentStreams = newValue;
+ break;
+ }
+ case Settings::MAX_FRAME_SIZE_ID: {
+ qCDebug(qHttp2ConnectionLog, "[%p] Received SETTINGS MAX_FRAME_SIZE %d", this, newValue);
+ if (newValue < Http2::minPayloadLimit || newValue > Http2::maxPayloadSize) {
+ connectionError(PROTOCOL_ERROR, "SETTINGS max frame size is out of range");
+ return false;
+ }
+ maxFrameSize = newValue;
+ break;
+ }
+ case Settings::MAX_HEADER_LIST_SIZE_ID: {
+ qCDebug(qHttp2ConnectionLog, "[%p] Received SETTINGS MAX_HEADER_LIST_SIZE %d", this,
+ newValue);
+ // We just remember this value, it can later
+ // prevent us from sending any request (and this
+ // will end up in request/reply error).
+ m_maxHeaderListSize = newValue;
+ break;
+ }
+ case Http2::Settings::ENABLE_PUSH_ID:
+ qCDebug(qHttp2ConnectionLog, "[%p] Received SETTINGS ENABLE_PUSH %d", this, newValue);
+ if (newValue != 0 && newValue != 1) {
+ connectionError(PROTOCOL_ERROR, "SETTINGS peer sent illegal value for ENABLE_PUSH");
+ return false;
+ }
+ if (m_connectionType == Type::Client) {
+ if (newValue == 1) {
+ connectionError(PROTOCOL_ERROR, "SETTINGS server sent ENABLE_PUSH=1");
+ return false;
+ }
+ } else { // server-side
+ pushPromiseEnabled = newValue;
+ break;
+ }
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qhttp2connection_p.cpp"
diff --git a/src/network/access/qhttp2connection_p.h b/src/network/access/qhttp2connection_p.h
new file mode 100644
index 0000000000..ca2cae58e0
--- /dev/null
+++ b/src/network/access/qhttp2connection_p.h
@@ -0,0 +1,372 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef HTTP2CONNECTION_P_H
+#define HTTP2CONNECTION_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qtnetworkglobal_p.h>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qxpfunctional.h>
+#include <QtNetwork/qhttp2configuration.h>
+#include <QtNetwork/qtcpsocket.h>
+
+#include <private/http2protocol_p.h>
+#include <private/http2streams_p.h>
+#include <private/http2frames_p.h>
+#include <private/hpack_p.h>
+
+#include <variant>
+#include <optional>
+#include <type_traits>
+#include <limits>
+
+QT_BEGIN_NAMESPACE
+
+template <typename T, typename Err>
+class QH2Expected
+{
+ static_assert(!std::is_same_v<T, Err>, "T and Err must be different types");
+public:
+ // Rule Of Zero applies
+ QH2Expected(T &&value) : m_data(std::move(value)) { }
+ QH2Expected(const T &value) : m_data(value) { }
+ QH2Expected(Err &&error) : m_data(std::move(error)) { }
+ QH2Expected(const Err &error) : m_data(error) { }
+
+ QH2Expected &operator=(T &&value)
+ {
+ m_data = std::move(value);
+ return *this;
+ }
+ QH2Expected &operator=(const T &value)
+ {
+ m_data = value;
+ return *this;
+ }
+ QH2Expected &operator=(Err &&error)
+ {
+ m_data = std::move(error);
+ return *this;
+ }
+ QH2Expected &operator=(const Err &error)
+ {
+ m_data = error;
+ return *this;
+ }
+ T unwrap() const
+ {
+ Q_ASSERT(ok());
+ return std::get<T>(m_data);
+ }
+ Err error() const
+ {
+ Q_ASSERT(has_error());
+ return std::get<Err>(m_data);
+ }
+ bool ok() const noexcept { return std::holds_alternative<T>(m_data); }
+ bool has_value() const noexcept { return ok(); }
+ bool has_error() const noexcept { return std::holds_alternative<Err>(m_data); }
+ void clear() noexcept { m_data.reset(); }
+
+private:
+ std::variant<T, Err> m_data;
+};
+
+class QHttp2Connection;
+class Q_NETWORK_EXPORT QHttp2Stream : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY_MOVE(QHttp2Stream)
+
+public:
+ enum class State { Idle, ReservedRemote, Open, HalfClosedLocal, HalfClosedRemote, Closed };
+ Q_ENUM(State)
+ constexpr static quint8 DefaultPriority = 127;
+
+ ~QHttp2Stream() noexcept;
+
+ // HTTP2 things
+ quint32 streamID() const noexcept { return m_streamID; }
+
+ // Are we waiting for a larger send window before sending more data?
+ bool isUploadBlocked() const noexcept;
+ bool isUploadingDATA() const noexcept { return m_uploadByteDevice != nullptr; }
+ State state() const noexcept { return m_state; }
+ bool isActive() const noexcept { return m_state != State::Closed && m_state != State::Idle; }
+ bool isPromisedStream() const noexcept { return m_isReserved; }
+ bool wasReset() const noexcept { return m_RST_STREAM_code.has_value(); }
+ quint32 RST_STREAM_code() const noexcept { return m_RST_STREAM_code.value_or(0); }
+ // Just the list of headers, as received, may contain duplicates:
+ HPack::HttpHeader receivedHeaders() const noexcept { return m_headers; }
+
+ QByteDataBuffer downloadBuffer() const noexcept { return m_downloadBuffer; }
+
+Q_SIGNALS:
+ void headersReceived(const HPack::HttpHeader &headers, bool endStream);
+ void headersUpdated();
+ void errorOccurred(quint32 errorCode, const QString &errorString);
+ void stateChanged(QHttp2Stream::State newState);
+ void promisedStreamReceived(quint32 newStreamID);
+ void uploadBlocked();
+ void dataReceived(const QByteArray &data, bool endStream);
+
+ void bytesWritten(qint64 bytesWritten);
+ void uploadDeviceError(const QString &errorString);
+ void uploadFinished();
+
+public Q_SLOTS:
+ bool sendRST_STREAM(quint32 errorCode);
+ bool sendHEADERS(const HPack::HttpHeader &headers, bool endStream,
+ quint8 priority = DefaultPriority);
+ void sendDATA(QIODevice *device, bool endStream);
+ void sendDATA(QNonContiguousByteDevice *device, bool endStream);
+ void sendWINDOW_UPDATE(quint32 delta);
+
+private Q_SLOTS:
+ void maybeResumeUpload();
+ void uploadDeviceReadChannelFinished();
+ void uploadDeviceDestroyed();
+
+private:
+ friend class QHttp2Connection;
+ QHttp2Stream(QHttp2Connection *connection, quint32 streamID) noexcept;
+
+ [[nodiscard]] QHttp2Connection *getConnection() const
+ {
+ return qobject_cast<QHttp2Connection *>(parent());
+ }
+
+ enum class StateTransition {
+ Open,
+ CloseLocal,
+ CloseRemote,
+ RST,
+ };
+
+ void setState(State newState);
+ void transitionState(StateTransition transition);
+ void internalSendDATA();
+ void finishSendDATA();
+
+ void handleDATA(const Http2::Frame &inboundFrame);
+ void handleHEADERS(Http2::FrameFlags frameFlags, const HPack::HttpHeader &headers);
+ void handleRST_STREAM(const Http2::Frame &inboundFrame);
+ void handleWINDOW_UPDATE(const Http2::Frame &inboundFrame);
+
+ void finishWithError(quint32 errorCode, const QString &message);
+ void finishWithError(quint32 errorCode);
+
+ // Keep it const since it never changes after creation
+ const quint32 m_streamID = 0;
+ qint32 m_recvWindow = 0;
+ qint32 m_sendWindow = 0;
+ bool m_endStreamAfterDATA = false;
+ std::optional<quint32> m_RST_STREAM_code;
+
+ QIODevice *m_uploadDevice = nullptr;
+ QNonContiguousByteDevice *m_uploadByteDevice = nullptr;
+
+ QByteDataBuffer m_downloadBuffer;
+ State m_state = State::Idle;
+ HPack::HttpHeader m_headers;
+ bool m_isReserved = false;
+};
+
+class Q_NETWORK_EXPORT QHttp2Connection : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY_MOVE(QHttp2Connection)
+
+public:
+ enum class CreateStreamError {
+ MaxConcurrentStreamsReached,
+ StreamIdsExhausted,
+ ReceivedGOAWAY,
+ };
+ Q_ENUM(CreateStreamError)
+
+ enum class PingState {
+ Ping,
+ PongSignatureIdentical,
+ PongSignatureChanged,
+ PongNoPingSent, // We got an ACKed ping but had not sent any
+ };
+
+ // For a pre-established connection:
+ [[nodiscard]] static QHttp2Connection *
+ createUpgradedConnection(QIODevice *socket, const QHttp2Configuration &config);
+ // For a new connection, potential TLS handshake must already be finished:
+ [[nodiscard]] static QHttp2Connection *createDirectConnection(QIODevice *socket,
+ const QHttp2Configuration &config);
+ [[nodiscard]] static QHttp2Connection *
+ createDirectServerConnection(QIODevice *socket, const QHttp2Configuration &config);
+ ~QHttp2Connection();
+
+ [[nodiscard]] QH2Expected<QHttp2Stream *, CreateStreamError> createStream();
+
+ QHttp2Stream *getStream(quint32 streamId) const;
+ QHttp2Stream *promisedStream(const QUrl &streamKey) const
+ {
+ if (quint32 id = m_promisedStreams.value(streamKey, 0); id)
+ return m_streams.value(id);
+ return nullptr;
+ }
+
+ void close() { sendGOAWAY(Http2::HTTP2_NO_ERROR); }
+
+ bool isGoingAway() const noexcept { return m_goingAway; }
+
+ quint32 maxConcurrentStreams() const noexcept { return m_maxConcurrentStreams; }
+ quint32 maxHeaderListSize() const noexcept { return m_maxHeaderListSize; }
+
+ bool isUpgradedConnection() const noexcept { return m_upgradedConnection; }
+
+Q_SIGNALS:
+ void newIncomingStream(QHttp2Stream *stream);
+ void newPromisedStream(QHttp2Stream *stream);
+ void errorReceived(/*@future: add as needed?*/); // Connection errors only, no stream-specific errors
+ void connectionClosed();
+ void settingsFrameReceived();
+ void pingFrameRecived(QHttp2Connection::PingState state);
+ void errorOccurred(Http2::Http2Error errorCode, const QString &errorString);
+ void receivedGOAWAY(quint32 errorCode, quint32 lastStreamID);
+public Q_SLOTS:
+ bool sendPing();
+ bool sendPing(QByteArrayView data);
+ void handleReadyRead();
+ void handleConnectionClosure();
+
+private:
+ friend class QHttp2Stream;
+ [[nodiscard]] QIODevice *getSocket() const { return qobject_cast<QIODevice *>(parent()); }
+
+ QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError> createStreamInternal();
+ QHttp2Stream *createStreamInternal_impl(quint32 streamID);
+
+ bool isInvalidStream(quint32 streamID) noexcept;
+ bool streamWasReset(quint32 streamID) noexcept;
+
+ void connectionError(Http2::Http2Error errorCode,
+ const char *message); // Connection failed to be established?
+ void setH2Configuration(QHttp2Configuration config);
+ void closeSession();
+ qsizetype numActiveStreamsImpl(quint32 mask) const noexcept;
+ qsizetype numActiveRemoteStreams() const noexcept;
+ qsizetype numActiveLocalStreams() const noexcept;
+
+ bool sendClientPreface();
+ bool sendSETTINGS();
+ bool sendServerPreface();
+ bool serverCheckClientPreface();
+ bool sendWINDOW_UPDATE(quint32 streamID, quint32 delta);
+ bool sendGOAWAY(quint32 errorCode);
+ bool sendSETTINGS_ACK();
+
+ void handleDATA();
+ void handleHEADERS();
+ void handlePRIORITY();
+ void handleRST_STREAM();
+ void handleSETTINGS();
+ void handlePUSH_PROMISE();
+ void handlePING();
+ void handleGOAWAY();
+ void handleWINDOW_UPDATE();
+ void handleCONTINUATION();
+
+ void handleContinuedHEADERS();
+
+ bool acceptSetting(Http2::Settings identifier, quint32 newValue);
+
+ bool readClientPreface();
+
+ explicit QHttp2Connection(QIODevice *socket);
+
+ enum class Type { Client, Server } m_connectionType = Type::Client;
+
+ bool waitingForSettingsACK = false;
+
+ static constexpr quint32 maxAcceptableTableSize = 16 * HPack::FieldLookupTable::DefaultSize;
+ // HTTP/2 4.3: Header compression is stateful. One compression context and
+ // one decompression context are used for the entire connection.
+ HPack::Decoder decoder = HPack::Decoder(HPack::FieldLookupTable::DefaultSize);
+ HPack::Encoder encoder = HPack::Encoder(HPack::FieldLookupTable::DefaultSize, true);
+
+ QHttp2Configuration m_config;
+ QHash<quint32, QPointer<QHttp2Stream>> m_streams;
+ QHash<QUrl, quint32> m_promisedStreams;
+ QVarLengthArray<quint32> m_resetStreamIDs;
+ std::optional<QByteArray> m_lastPingSignature = std::nullopt;
+ quint32 m_nextStreamID = 1;
+
+ // Peer's max frame size (this min is the default value
+ // we start with, that can be updated by SETTINGS frame):
+ quint32 maxFrameSize = Http2::minPayloadLimit;
+
+ Http2::FrameReader frameReader;
+ Http2::Frame inboundFrame;
+ Http2::FrameWriter frameWriter;
+
+ // Temporary storage to assemble HEADERS' block
+ // from several CONTINUATION frames ...
+ bool continuationExpected = false;
+ std::vector<Http2::Frame> continuedFrames;
+
+ // Control flow:
+
+ // This is how many concurrent streams our peer allows us, 100 is the
+ // initial value, can be updated by the server's SETTINGS frame(s):
+ quint32 m_maxConcurrentStreams = Http2::maxConcurrentStreams;
+ // While we allow sending SETTTINGS_MAX_CONCURRENT_STREAMS to limit our peer,
+ // it's just a hint and we do not actually enforce it (and we can continue
+ // sending requests and creating streams while maxConcurrentStreams allows).
+
+ // This is our (client-side) maximum possible receive window size, we set
+ // it in a ctor from QHttp2Configuration, it does not change after that.
+ // The default is 64Kb:
+ qint32 maxSessionReceiveWindowSize = Http2::defaultSessionWindowSize;
+
+ // Our session current receive window size, updated in a ctor from
+ // QHttp2Configuration. Signed integer since it can become negative
+ // (it's still a valid window size).
+ qint32 sessionReceiveWindowSize = Http2::defaultSessionWindowSize;
+ // Our per-stream receive window size, default is 64 Kb, will be updated
+ // from QHttp2Configuration. Again, signed - can become negative.
+ qint32 streamInitialReceiveWindowSize = Http2::defaultSessionWindowSize;
+
+ // These are our peer's receive window sizes, they will be updated by the
+ // peer's SETTINGS and WINDOW_UPDATE frames, defaults presumed to be 64Kb.
+ qint32 sessionSendWindowSize = Http2::defaultSessionWindowSize;
+ qint32 streamInitialSendWindowSize = Http2::defaultSessionWindowSize;
+
+ // Our peer's header size limitations. It's unlimited by default, but can
+ // be changed via peer's SETTINGS frame.
+ quint32 m_maxHeaderListSize = (std::numeric_limits<quint32>::max)();
+ // While we can send SETTINGS_MAX_HEADER_LIST_SIZE value (our limit on
+ // the headers size), we never enforce it, it's just a hint to our peer.
+
+ bool m_upgradedConnection = false;
+ bool m_goingAway = false;
+ bool pushPromiseEnabled = false;
+ quint32 m_lastIncomingStreamID = Http2::connectionStreamID;
+
+ // Server-side only:
+ bool m_waitingForClientPreface = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // HTTP2CONNECTION_P_H
diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp
index 562b883242..d9341dc643 100644
--- a/src/network/access/qhttp2protocolhandler.cpp
+++ b/src/network/access/qhttp2protocolhandler.cpp
@@ -10,10 +10,12 @@
#include <private/qnoncontiguousbytedevice_p.h>
#include <QtNetwork/qabstractsocket.h>
+
#include <QtCore/qloggingcategory.h>
#include <QtCore/qendian.h>
#include <QtCore/qdebug.h>
#include <QtCore/qlist.h>
+#include <QtCore/qnumeric.h>
#include <QtCore/qurl.h>
#include <qhttp2configuration.h>
@@ -26,6 +28,7 @@
#include <algorithm>
#include <vector>
+#include <optional>
QT_BEGIN_NAMESPACE
@@ -45,10 +48,10 @@ HPack::HttpHeader build_headers(const QHttpNetworkRequest &request, quint32 maxH
// 1. Before anything - mandatory fields, if they do not fit into maxHeaderList -
// then stop immediately with error.
const auto auth = request.url().authority(QUrl::FullyEncoded | QUrl::RemoveUserInfo).toLatin1();
- header.push_back(HeaderField(":authority", auth));
- header.push_back(HeaderField(":method", request.methodName()));
- header.push_back(HeaderField(":path", request.uri(useProxy)));
- header.push_back(HeaderField(":scheme", request.url().scheme().toLatin1()));
+ header.emplace_back(":authority", auth);
+ header.emplace_back(":method", request.methodName());
+ header.emplace_back(":path", request.uri(useProxy));
+ header.emplace_back(":scheme", request.url().scheme().toLatin1());
HeaderSize size = header_size(header);
if (!size.first) // Ooops!
@@ -57,9 +60,11 @@ HPack::HttpHeader build_headers(const QHttpNetworkRequest &request, quint32 maxH
if (size.second > maxHeaderListSize)
return HttpHeader(); // Bad, we cannot send this request ...
- const auto requestHeader = request.header();
- for (const auto &field : requestHeader) {
- const HeaderSize delta = entry_size(field.first, field.second);
+ const QHttpHeaders requestHeader = request.header();
+ for (qsizetype i = 0; i < requestHeader.size(); ++i) {
+ const auto name = requestHeader.nameAt(i);
+ const auto value = requestHeader.valueAt(i);
+ const HeaderSize delta = entry_size(name, value);
if (!delta.first) // Overflow???
break;
if (std::numeric_limits<quint32>::max() - delta.second < size.second)
@@ -68,47 +73,22 @@ HPack::HttpHeader build_headers(const QHttpNetworkRequest &request, quint32 maxH
if (size.second > maxHeaderListSize)
break;
- if (field.first.compare("connection", Qt::CaseInsensitive) == 0 ||
- field.first.compare("host", Qt::CaseInsensitive) == 0 ||
- field.first.compare("keep-alive", Qt::CaseInsensitive) == 0 ||
- field.first.compare("proxy-connection", Qt::CaseInsensitive) == 0 ||
- field.first.compare("transfer-encoding", Qt::CaseInsensitive) == 0)
+ if (name == "connection"_L1 || name == "host"_L1 || name == "keep-alive"_L1
+ || name == "proxy-connection"_L1 || name == "transfer-encoding"_L1) {
continue; // Those headers are not valid (section 3.2.1) - from QSpdyProtocolHandler
+ }
// TODO: verify with specs, which fields are valid to send ....
- // toLower - 8.1.2 .... "header field names MUST be converted to lowercase prior
- // to their encoding in HTTP/2.
- // A request or response containing uppercase header field names
- // MUST be treated as malformed (Section 8.1.2.6)".
- header.push_back(HeaderField(field.first.toLower(), field.second));
+ //
+ // Note: RFC 7450 8.1.2 (HTTP/2) states that header field names must be lower-cased
+ // prior to their encoding in HTTP/2; header name fields in QHttpHeaders are already
+ // lower-cased
+ header.emplace_back(QByteArray{name.data(), name.size()},
+ QByteArray{value.data(), value.size()});
}
return header;
}
-std::vector<uchar> assemble_hpack_block(const std::vector<Http2::Frame> &frames)
-{
- std::vector<uchar> hpackBlock;
-
- quint32 total = 0;
- for (const auto &frame : frames)
- total += frame.hpackBlockSize();
-
- if (!total)
- return hpackBlock;
-
- hpackBlock.resize(total);
- auto dst = hpackBlock.begin();
- for (const auto &frame : frames) {
- if (const auto hpackBlockSize = frame.hpackBlockSize()) {
- const uchar *src = frame.hpackBlockBegin();
- std::copy(src, src + hpackBlockSize, dst);
- dst += hpackBlockSize;
- }
- }
-
- return hpackBlock;
-}
-
QUrl urlkey_from_request(const QHttpNetworkRequest &request)
{
QUrl url;
@@ -120,21 +100,11 @@ QUrl urlkey_from_request(const QHttpNetworkRequest &request)
return url;
}
-bool sum_will_overflow(qint32 windowSize, qint32 delta)
-{
- if (windowSize > 0)
- return std::numeric_limits<qint32>::max() - windowSize < delta;
- return std::numeric_limits<qint32>::min() - windowSize > delta;
-}
-
}// Unnamed namespace
// Since we anyway end up having this in every function definition:
using namespace Http2;
-const std::deque<quint32>::size_type QHttp2ProtocolHandler::maxRecycledStreams = 10000;
-const quint32 QHttp2ProtocolHandler::maxAcceptableTableSize;
-
QHttp2ProtocolHandler::QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *channel)
: QAbstractProtocolHandler(channel),
decoder(HPack::FieldLookupTable::DefaultSize),
@@ -318,8 +288,7 @@ bool QHttp2ProtocolHandler::sendRequest()
auto &requests = m_channel->h2RequestsToSend;
for (auto it = requests.begin(), endIt = requests.end(); it != endIt;) {
const auto &pair = *it;
- const QString scheme(pair.first.url().scheme());
- if (scheme == "preconnect-http"_L1 || scheme == "preconnect-https"_L1) {
+ if (pair.first.isPreConnect()) {
m_connection->preConnectFinished();
emit pair.second->finished();
it = requests.erase(it);
@@ -357,11 +326,13 @@ bool QHttp2ProtocolHandler::sendRequest()
initReplyFromPushPromise(message, key);
}
- const auto streamsToUse = std::min<quint32>(maxConcurrentStreams > quint32(activeStreams.size())
- ? maxConcurrentStreams - quint32(activeStreams.size()) : 0,
- requests.size());
+ const auto isClientSide = [](const auto &pair) -> bool { return (pair.first & 1) == 1; };
+ const auto activeClientSideStreams = std::count_if(
+ activeStreams.constKeyValueBegin(), activeStreams.constKeyValueEnd(), isClientSide);
+ const qint64 streamsToUse = qBound(0, qint64(maxConcurrentStreams) - activeClientSideStreams,
+ requests.size());
auto it = requests.begin();
- for (quint32 i = 0; i < streamsToUse; ++i) {
+ for (qint64 i = 0; i < streamsToUse; ++i) {
const qint32 newStreamID = createNewStream(*it);
if (!newStreamID) {
// TODO: actually we have to open a new connection.
@@ -504,7 +475,7 @@ bool QHttp2ProtocolHandler::sendDATA(Stream &stream)
}
frameWriter.start(FrameType::DATA, FrameFlag::EMPTY, stream.streamID);
- const qint32 bytesWritten = std::min<qint32>(slot, chunkSize);
+ const qint32 bytesWritten = qint32(std::min<qint64>(slot, chunkSize));
if (!frameWriter.writeDATA(*m_socket, maxFrameSize, src, bytesWritten))
return false;
@@ -576,8 +547,9 @@ void QHttp2ProtocolHandler::handleDATA()
sessionReceiveWindowSize -= inboundFrame.payloadSize();
- if (activeStreams.contains(streamID)) {
- auto &stream = activeStreams[streamID];
+ auto it = activeStreams.find(streamID);
+ if (it != activeStreams.end()) {
+ Stream &stream = it.value();
if (qint32(inboundFrame.payloadSize()) > stream.recvWindow) {
finishStreamWithError(stream, QNetworkReply::ProtocolFailure, "flow control error"_L1);
@@ -883,16 +855,19 @@ void QHttp2ProtocolHandler::handleWINDOW_UPDATE()
const auto streamID = inboundFrame.streamID();
if (streamID == Http2::connectionStreamID) {
- if (!valid || sum_will_overflow(sessionSendWindowSize, delta))
+ qint32 sum = 0;
+ if (!valid || qAddOverflow(sessionSendWindowSize, qint32(delta), &sum))
return connectionError(PROTOCOL_ERROR, "WINDOW_UPDATE invalid delta");
- sessionSendWindowSize += delta;
+ sessionSendWindowSize = sum;
} else {
- if (!activeStreams.contains(streamID)) {
+ auto it = activeStreams.find(streamID);
+ if (it == activeStreams.end()) {
// WINDOW_UPDATE on closed streams can be ignored.
return;
}
- auto &stream = activeStreams[streamID];
- if (!valid || sum_will_overflow(stream.sendWindow, delta)) {
+ Stream &stream = it.value();
+ qint32 sum = 0;
+ if (!valid || qAddOverflow(stream.sendWindow, qint32(delta), &sum)) {
finishStreamWithError(stream, QNetworkReply::ProtocolFailure,
"invalid WINDOW_UPDATE delta"_L1);
sendRST_STREAM(streamID, PROTOCOL_ERROR);
@@ -900,7 +875,7 @@ void QHttp2ProtocolHandler::handleWINDOW_UPDATE()
deleteActiveStream(streamID);
return;
}
- stream.sendWindow += delta;
+ stream.sendWindow = sum;
}
// Since we're in _q_receiveReply at the moment, let's first handle other
@@ -939,9 +914,10 @@ void QHttp2ProtocolHandler::handleContinuedHEADERS()
const auto streamID = continuedFrames[0].streamID();
+ const auto streamIt = activeStreams.find(streamID);
if (firstFrameType == FrameType::HEADERS) {
- if (activeStreams.contains(streamID)) {
- Stream &stream = activeStreams[streamID];
+ if (streamIt != activeStreams.end()) {
+ Stream &stream = streamIt.value();
if (stream.state != Stream::halfClosedLocal
&& stream.state != Stream::remoteReserved
&& stream.state != Stream::open) {
@@ -963,8 +939,13 @@ void QHttp2ProtocolHandler::handleContinuedHEADERS()
// has yet to see the reset.
}
- std::vector<uchar> hpackBlock(assemble_hpack_block(continuedFrames));
- if (!hpackBlock.size()) {
+ std::vector<uchar> hpackBlock(Http2::assemble_hpack_block(continuedFrames));
+ const bool hasHeaderFields = !hpackBlock.empty();
+ if (hasHeaderFields) {
+ HPack::BitIStream inputStream{&hpackBlock[0], &hpackBlock[0] + hpackBlock.size()};
+ if (!decoder.decodeHeaderFields(inputStream))
+ return connectionError(COMPRESSION_ERROR, "HPACK decompression failed");
+ } else if (firstFrameType == FrameType::PUSH_PROMISE) {
// It could be a PRIORITY sent in HEADERS - already handled by this
// point in handleHEADERS. If it was PUSH_PROMISE (HTTP/2 8.2.1):
// "The header fields in PUSH_PROMISE and any subsequent CONTINUATION
@@ -973,21 +954,16 @@ void QHttp2ProtocolHandler::handleContinuedHEADERS()
// not include a complete and valid set of header fields or the :method
// pseudo-header field identifies a method that is not safe, it MUST
// respond with a stream error (Section 5.4.2) of type PROTOCOL_ERROR."
- if (firstFrameType == FrameType::PUSH_PROMISE)
- resetPromisedStream(continuedFrames[0], Http2::PROTOCOL_ERROR);
-
+ resetPromisedStream(continuedFrames[0], Http2::PROTOCOL_ERROR);
return;
}
- HPack::BitIStream inputStream{&hpackBlock[0], &hpackBlock[0] + hpackBlock.size()};
- if (!decoder.decodeHeaderFields(inputStream))
- return connectionError(COMPRESSION_ERROR, "HPACK decompression failed");
-
switch (firstFrameType) {
case FrameType::HEADERS:
- if (activeStreams.contains(streamID)) {
- Stream &stream = activeStreams[streamID];
- updateStream(stream, decoder.decodedHeader());
+ if (streamIt != activeStreams.end()) {
+ Stream &stream = streamIt.value();
+ if (hasHeaderFields)
+ updateStream(stream, decoder.decodedHeader());
// Needs to resend the request; we should finish and delete the current stream
const bool needResend = stream.request().d->needResendWithCredentials;
// No DATA frames. Or needs to resend.
@@ -1030,11 +1006,12 @@ bool QHttp2ProtocolHandler::acceptSetting(Http2::Settings identifier, quint32 ne
std::vector<quint32> brokenStreams;
brokenStreams.reserve(activeStreams.size());
for (auto &stream : activeStreams) {
- if (sum_will_overflow(stream.sendWindow, delta)) {
+ qint32 sum = 0;
+ if (qAddOverflow(stream.sendWindow, delta, &sum)) {
brokenStreams.push_back(stream.streamID);
continue;
}
- stream.sendWindow += delta;
+ stream.sendWindow = sum;
}
for (auto id : brokenStreams) {
@@ -1101,7 +1078,7 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const HPack::HttpHeader
int statusCode = 0;
for (const auto &pair : headers) {
const auto &name = pair.name;
- auto value = pair.value;
+ const auto value = QByteArrayView(pair.value);
// TODO: part of this code copies what SPDY protocol handler does when
// processing headers. Binary nature of HTTP/2 and SPDY saves us a lot
@@ -1121,68 +1098,16 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const HPack::HttpHeader
if (ok)
httpReply->setContentLength(length);
} else {
- QByteArray binder(", ");
- if (name == "set-cookie")
- binder = "\n";
- httpReply->appendHeaderField(name, value.replace('\0', binder));
+ const auto binder = name == "set-cookie" ? QByteArrayView("\n") : QByteArrayView(", ");
+ httpReply->appendHeaderField(name, QByteArray(pair.value).replace('\0', binder));
}
}
- const auto handleAuth = [&, this](const QByteArray &authField, bool isProxy) -> bool {
- Q_ASSERT(httpReply);
- const auto auth = authField.trimmed();
- if (auth.startsWith("Negotiate") || auth.startsWith("NTLM")) {
- // @todo: We're supposed to fall back to http/1.1:
- // https://docs.microsoft.com/en-us/iis/get-started/whats-new-in-iis-10/http2-on-iis#when-is-http2-not-supported
- // "Windows authentication (NTLM/Kerberos/Negotiate) is not supported with HTTP/2.
- // In this case IIS will fall back to HTTP/1.1."
- // Though it might be OK to ignore this. The server shouldn't let us connect with
- // HTTP/2 if it doesn't support us using it.
- } else if (!auth.isEmpty()) {
- // Somewhat mimics parts of QHttpNetworkConnectionChannel::handleStatus
- bool resend = false;
- const bool authenticateHandled = m_connection->d_func()->handleAuthenticateChallenge(
- m_socket, httpReply, isProxy, resend);
- if (authenticateHandled && resend) {
- httpReply->d_func()->eraseData();
- // Add the request back in queue, we'll retry later now that
- // we've gotten some username/password set on it:
- httpRequest.d->needResendWithCredentials = true;
- m_channel->h2RequestsToSend.insert(httpRequest.priority(), stream.httpPair);
- httpReply->d_func()->clearHeaders();
- // If we have data we were uploading we need to reset it:
- if (stream.data()) {
- stream.data()->reset();
- httpReplyPrivate->totallyUploadedData = 0;
- }
- return true;
- } // else: Authentication failed or was cancelled
- }
- return false;
- };
-
- if (httpReply) {
- // See Note further down. These statuses would in HTTP/1.1 be handled
- // by QHttpNetworkConnectionChannel::handleStatus. But because h2 has
- // multiple streams/requests in a single channel this structure does not
- // map properly to that function.
- if (httpReply->statusCode() == 401) {
- const auto wwwAuth = httpReply->headerField("www-authenticate");
- if (handleAuth(wwwAuth, false)) {
- sendRST_STREAM(stream.streamID, CANCEL);
- markAsReset(stream.streamID);
- // The stream is finalized and deleted after returning
- return;
- } // else: errors handled later
- } else if (httpReply->statusCode() == 407) {
- const auto proxyAuth = httpReply->headerField("proxy-authenticate");
- if (handleAuth(proxyAuth, true)) {
- sendRST_STREAM(stream.streamID, CANCEL);
- markAsReset(stream.streamID);
- // The stream is finalized and deleted after returning
- return;
- } // else: errors handled later
- }
+ // Discard all informational (1xx) replies with the exception of 101.
+ // Also see RFC 9110 (Chapter 15.2)
+ if (statusCode == 100 || (102 <= statusCode && statusCode <= 199)) {
+ httpReplyPrivate->clearHttpLayerInformation();
+ return;
}
if (QHttpNetworkReply::isHttpRedirect(statusCode) && httpRequest.isFollowRedirects()) {
@@ -1242,8 +1167,7 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const Frame &frame,
replyPrivate->totalProgress += length;
- const QByteArray wrapped(data, length);
- replyPrivate->responseData.append(wrapped);
+ replyPrivate->responseData.append(QByteArray(data, length));
if (replyPrivate->shouldEmitSignals()) {
if (connectionType == Qt::DirectConnection) {
@@ -1260,6 +1184,91 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const Frame &frame,
}
}
+// After calling this function, either the request will be re-sent or
+// the reply will be finishedWithError! Do not emit finished() or similar on the
+// reply after this!
+void QHttp2ProtocolHandler::handleAuthorization(Stream &stream)
+{
+ auto *httpReply = stream.reply();
+ auto *httpReplyPrivate = httpReply->d_func();
+ auto &httpRequest = stream.request();
+
+ Q_ASSERT(httpReply && (httpReply->statusCode() == 401 || httpReply->statusCode() == 407));
+
+ const auto handleAuth = [&, this](QByteArrayView authField, bool isProxy) -> bool {
+ Q_ASSERT(httpReply);
+ const QByteArrayView auth = authField.trimmed();
+ if (auth.startsWith("Negotiate") || auth.startsWith("NTLM")) {
+ // @todo: We're supposed to fall back to http/1.1:
+ // https://docs.microsoft.com/en-us/iis/get-started/whats-new-in-iis-10/http2-on-iis#when-is-http2-not-supported
+ // "Windows authentication (NTLM/Kerberos/Negotiate) is not supported with HTTP/2.
+ // In this case IIS will fall back to HTTP/1.1."
+ // Though it might be OK to ignore this. The server shouldn't let us connect with
+ // HTTP/2 if it doesn't support us using it.
+ return false;
+ }
+ // Somewhat mimics parts of QHttpNetworkConnectionChannel::handleStatus
+ bool resend = false;
+ const bool authenticateHandled = m_connection->d_func()->handleAuthenticateChallenge(
+ m_socket, httpReply, isProxy, resend);
+ if (authenticateHandled) {
+ if (resend) {
+ httpReply->d_func()->eraseData();
+ // Add the request back in queue, we'll retry later now that
+ // we've gotten some username/password set on it:
+ httpRequest.d->needResendWithCredentials = true;
+ m_channel->h2RequestsToSend.insert(httpRequest.priority(), stream.httpPair);
+ httpReply->d_func()->clearHeaders();
+ // If we have data we were uploading we need to reset it:
+ if (stream.data()) {
+ stream.data()->reset();
+ httpReplyPrivate->totallyUploadedData = 0;
+ }
+ // We automatically try to send new requests when the stream is
+ // closed, so we don't need to call sendRequest ourselves.
+ return true;
+ } // else: we're just not resending the request.
+ // @note In the http/1.x case we (at time of writing) call close()
+ // for the connectionChannel (which is a bit weird, we could surely
+ // reuse the open socket outside "connection:close"?), but in http2
+ // we only have one channel, so we won't close anything.
+ } else {
+ // No authentication header or authentication isn't supported, but
+ // we got a 401/407 so we cannot succeed. We need to emit signals
+ // for headers and data, and then finishWithError.
+ emit httpReply->headerChanged();
+ emit httpReply->readyRead();
+ QNetworkReply::NetworkError error = httpReply->statusCode() == 401
+ ? QNetworkReply::AuthenticationRequiredError
+ : QNetworkReply::ProxyAuthenticationRequiredError;
+ finishStreamWithError(stream, QNetworkReply::AuthenticationRequiredError,
+ m_connection->d_func()->errorDetail(error, m_socket));
+ }
+ return false;
+ };
+
+ // These statuses would in HTTP/1.1 be handled by
+ // QHttpNetworkConnectionChannel::handleStatus. But because h2 has
+ // multiple streams/requests in a single channel this structure does not
+ // map properly to that function.
+ bool authOk = true;
+ switch (httpReply->statusCode()) {
+ case 401:
+ authOk = handleAuth(httpReply->headerField("www-authenticate"), false);
+ break;
+ case 407:
+ authOk = handleAuth(httpReply->headerField("proxy-authenticate"), true);
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+ if (authOk) {
+ markAsReset(stream.streamID);
+ deleteActiveStream(stream.streamID);
+ } // else: errors handled inside handleAuth
+}
+
+// Called when we have received a frame with the END_STREAM flag set
void QHttp2ProtocolHandler::finishStream(Stream &stream, Qt::ConnectionType connectionType)
{
Q_ASSERT(stream.state == Stream::remoteReserved || stream.reply());
@@ -1267,6 +1276,15 @@ void QHttp2ProtocolHandler::finishStream(Stream &stream, Qt::ConnectionType conn
stream.state = Stream::closed;
auto httpReply = stream.reply();
if (httpReply) {
+ int statusCode = httpReply->statusCode();
+ if (statusCode == 401 || statusCode == 407) {
+ // handleAuthorization will either re-send the request or
+ // finishWithError. In either case we don't want to emit finished
+ // here.
+ handleAuthorization(stream);
+ return;
+ }
+
httpReply->disconnect(this);
if (stream.data())
stream.data()->disconnect(this);
@@ -1390,9 +1408,10 @@ quint32 QHttp2ProtocolHandler::popStreamToResume()
auto &queue = suspendedStreams[rank];
auto it = queue.begin();
for (; it != queue.end(); ++it) {
- if (!activeStreams.contains(*it))
+ auto stream = activeStreams.constFind(*it);
+ if (stream == activeStreams.cend())
continue;
- if (activeStreams[*it].sendWindow > 0)
+ if (stream->sendWindow > 0)
break;
}
@@ -1415,8 +1434,8 @@ void QHttp2ProtocolHandler::removeFromSuspended(quint32 streamID)
void QHttp2ProtocolHandler::deleteActiveStream(quint32 streamID)
{
- if (activeStreams.contains(streamID)) {
- auto &stream = activeStreams[streamID];
+ if (const auto it = activeStreams.constFind(streamID); it != activeStreams.cend()) {
+ const Stream &stream = it.value();
if (stream.reply()) {
stream.reply()->disconnect(this);
streamIDs.remove(stream.reply());
@@ -1425,7 +1444,7 @@ void QHttp2ProtocolHandler::deleteActiveStream(quint32 streamID)
stream.data()->disconnect(this);
streamIDs.remove(stream.data());
}
- activeStreams.remove(streamID);
+ activeStreams.erase(it);
}
removeFromSuspended(streamID);
@@ -1448,10 +1467,11 @@ void QHttp2ProtocolHandler::resumeSuspendedStreams()
if (!streamID)
return;
- if (!activeStreams.contains(streamID))
+ auto it = activeStreams.find(streamID);
+ if (it == activeStreams.end())
continue;
+ Stream &stream = it.value();
- Stream &stream = activeStreams[streamID];
if (!sendDATA(stream)) {
finishStreamWithError(stream, QNetworkReply::UnknownNetworkError,
"failed to send DATA"_L1);
@@ -1480,42 +1500,18 @@ bool QHttp2ProtocolHandler::tryReserveStream(const Http2::Frame &pushPromiseFram
{
Q_ASSERT(pushPromiseFrame.type() == FrameType::PUSH_PROMISE);
- QMap<QByteArray, QByteArray> pseudoHeaders;
- for (const auto &field : requestHeader) {
- if (field.name == ":scheme" || field.name == ":path"
- || field.name == ":authority" || field.name == ":method") {
- if (field.value.isEmpty() || pseudoHeaders.contains(field.name))
- return false;
- pseudoHeaders[field.name] = field.value;
- }
- }
-
- if (pseudoHeaders.size() != 4) {
- // All four required, HTTP/2 8.1.2.3.
- return false;
- }
-
- const QByteArray method = pseudoHeaders[":method"];
- if (method.compare("get", Qt::CaseInsensitive) != 0 &&
- method.compare("head", Qt::CaseInsensitive) != 0)
- return false;
-
- QUrl url;
- url.setScheme(QLatin1StringView(pseudoHeaders[":scheme"]));
- url.setAuthority(QLatin1StringView(pseudoHeaders[":authority"]));
- url.setPath(QLatin1StringView(pseudoHeaders[":path"]));
-
- if (!url.isValid())
+ const auto url = HPack::makePromiseKeyUrl(requestHeader);
+ if (!url.has_value())
return false;
Q_ASSERT(activeStreams.contains(pushPromiseFrame.streamID()));
const Stream &associatedStream = activeStreams[pushPromiseFrame.streamID()];
const auto associatedUrl = urlkey_from_request(associatedStream.request());
- if (url.adjusted(QUrl::RemovePath) != associatedUrl.adjusted(QUrl::RemovePath))
+ if (url->adjusted(QUrl::RemovePath) != associatedUrl.adjusted(QUrl::RemovePath))
return false;
- const auto urlKey = url.toString();
+ const auto urlKey = url->toString();
if (promisedData.contains(urlKey)) // duplicate push promise
return false;
@@ -1554,8 +1550,8 @@ void QHttp2ProtocolHandler::initReplyFromPushPromise(const HttpMessagePair &mess
bool replyFinished = false;
Stream *promisedStream = nullptr;
- if (activeStreams.contains(promise.reservedID)) {
- promisedStream = &activeStreams[promise.reservedID];
+ if (auto it = activeStreams.find(promise.reservedID); it != activeStreams.end()) {
+ promisedStream = &it.value();
// Ok, we have an active (not closed yet) stream waiting for more frames,
// let's pretend we requested it:
promisedStream->httpPair = message;
@@ -1565,8 +1561,8 @@ void QHttp2ProtocolHandler::initReplyFromPushPromise(const HttpMessagePair &mess
streamInitialSendWindowSize,
streamInitialReceiveWindowSize);
closedStream.state = Stream::halfClosedLocal;
- activeStreams.insert(promise.reservedID, closedStream);
- promisedStream = &activeStreams[promise.reservedID];
+ it = activeStreams.insert(promise.reservedID, closedStream);
+ promisedStream = &it.value();
replyFinished = true;
}
diff --git a/src/network/access/qhttp2protocolhandler_p.h b/src/network/access/qhttp2protocolhandler_p.h
index c4340d7eeb..3b818771a6 100644
--- a/src/network/access/qhttp2protocolhandler_p.h
+++ b/src/network/access/qhttp2protocolhandler_p.h
@@ -94,6 +94,7 @@ private:
bool acceptSetting(Http2::Settings identifier, quint32 newValue);
+ void handleAuthorization(Stream &stream);
void updateStream(Stream &stream, const HPack::HttpHeader &headers,
Qt::ConnectionType connectionType = Qt::DirectConnection);
void updateStream(Stream &stream, const Http2::Frame &dataFrame,
@@ -120,7 +121,7 @@ private:
// the client's preface 24-byte message.
bool waitingForSettingsACK = false;
- static const quint32 maxAcceptableTableSize = 16 * HPack::FieldLookupTable::DefaultSize;
+ inline static const quint32 maxAcceptableTableSize = 16 * HPack::FieldLookupTable::DefaultSize;
// HTTP/2 4.3: Header compression is stateful. One compression context and
// one decompression context are used for the entire connection.
HPack::Decoder decoder;
@@ -129,7 +130,7 @@ private:
QHash<QObject *, int> streamIDs;
QHash<quint32, Stream> activeStreams;
std::deque<quint32> suspendedStreams[3]; // 3 for priorities: High, Normal, Low.
- static const std::deque<quint32>::size_type maxRecycledStreams;
+ inline static const std::deque<quint32>::size_type maxRecycledStreams = 10000;
std::deque<quint32> recycledStreams;
// Peer's max frame size (this min is the default value
diff --git a/src/network/access/qhttpheaderparser.cpp b/src/network/access/qhttpheaderparser.cpp
index 558f8dbd5c..0b7882c18a 100644
--- a/src/network/access/qhttpheaderparser.cpp
+++ b/src/network/access/qhttpheaderparser.cpp
@@ -52,11 +52,11 @@ bool QHttpHeaderParser::parseHeaders(QByteArrayView header)
if (header.size() - (header.endsWith("\r\n") ? 2 : 1) > maxTotalSize)
return false;
- QList<QPair<QByteArray, QByteArray>> result;
+ QHttpHeaders result;
while (!header.empty()) {
const qsizetype colon = header.indexOf(':');
if (colon == -1) // if no colon check if empty headers
- return result.empty() && (header == "\n" || header == "\r\n");
+ return result.isEmpty() && (header == "\n" || header == "\r\n");
if (result.size() >= maxFieldCount)
return false;
QByteArrayView name = header.first(colon);
@@ -75,14 +75,14 @@ bool QHttpHeaderParser::parseHeaders(QByteArrayView header)
line = line.trimmed();
if (!line.empty()) {
if (value.size())
- value += ' ' + line.toByteArray();
+ value += ' ' + line;
else
value = line.toByteArray();
}
header = header.sliced(endLine + 1);
} while (hSpaceStart(header));
Q_ASSERT(name.size() + 1 + value.size() <= maxFieldSize);
- result.append(qMakePair(name.toByteArray(), value));
+ result.append(name, value);
}
fields = result;
@@ -102,7 +102,7 @@ bool QHttpHeaderParser::parseStatus(QByteArrayView status)
static const int spacePos = 8;
static const char httpMagic[] = "HTTP/";
- if (status.length() < minLength
+ if (status.size() < minLength
|| !status.startsWith(httpMagic)
|| status.at(dotPos) != '.'
|| status.at(spacePos) != ' ') {
@@ -128,22 +128,18 @@ bool QHttpHeaderParser::parseStatus(QByteArrayView status)
return ok && uint(majorVersion) <= 9 && uint(minorVersion) <= 9;
}
-const QList<QPair<QByteArray, QByteArray> >& QHttpHeaderParser::headers() const
+const QHttpHeaders& QHttpHeaderParser::headers() const
{
return fields;
}
-QByteArray QHttpHeaderParser::firstHeaderField(const QByteArray &name,
+QByteArray QHttpHeaderParser::firstHeaderField(QByteArrayView name,
const QByteArray &defaultValue) const
{
- for (auto it = fields.constBegin(); it != fields.constEnd(); ++it) {
- if (name.compare(it->first, Qt::CaseInsensitive) == 0)
- return it->second;
- }
- return defaultValue;
+ return fields.value(name, defaultValue).toByteArray();
}
-QByteArray QHttpHeaderParser::combinedHeaderValue(const QByteArray &name, const QByteArray &defaultValue) const
+QByteArray QHttpHeaderParser::combinedHeaderValue(QByteArrayView name, const QByteArray &defaultValue) const
{
const QList<QByteArray> allValues = headerFieldValues(name);
if (allValues.isEmpty())
@@ -151,38 +147,30 @@ QByteArray QHttpHeaderParser::combinedHeaderValue(const QByteArray &name, const
return allValues.join(", ");
}
-QList<QByteArray> QHttpHeaderParser::headerFieldValues(const QByteArray &name) const
+QList<QByteArray> QHttpHeaderParser::headerFieldValues(QByteArrayView name) const
{
- QList<QByteArray> result;
- for (auto it = fields.constBegin(); it != fields.constEnd(); ++it)
- if (name.compare(it->first, Qt::CaseInsensitive) == 0)
- result += it->second;
-
- return result;
+ return fields.values(name);
}
-void QHttpHeaderParser::removeHeaderField(const QByteArray &name)
+void QHttpHeaderParser::removeHeaderField(QByteArrayView name)
{
- auto firstEqualsName = [&name](const QPair<QByteArray, QByteArray> &header) {
- return name.compare(header.first, Qt::CaseInsensitive) == 0;
- };
- fields.removeIf(firstEqualsName);
+ fields.removeAll(name);
}
void QHttpHeaderParser::setHeaderField(const QByteArray &name, const QByteArray &data)
{
removeHeaderField(name);
- fields.append(qMakePair(name, data));
+ fields.append(name, data);
}
void QHttpHeaderParser::prependHeaderField(const QByteArray &name, const QByteArray &data)
{
- fields.prepend(qMakePair(name, data));
+ fields.insert(0, name, data);
}
void QHttpHeaderParser::appendHeaderField(const QByteArray &name, const QByteArray &data)
{
- fields.append(qMakePair(name, data));
+ fields.append(name, data);
}
void QHttpHeaderParser::clearHeaders()
diff --git a/src/network/access/qhttpheaderparser_p.h b/src/network/access/qhttpheaderparser_p.h
index 9b149570e0..5e8f3c8130 100644
--- a/src/network/access/qhttpheaderparser_p.h
+++ b/src/network/access/qhttpheaderparser_p.h
@@ -16,6 +16,7 @@
//
#include <QtNetwork/private/qtnetworkglobal_p.h>
+#include <QtNetwork/qhttpheaders.h>
#include <QByteArray>
#include <QList>
@@ -43,7 +44,7 @@ static constexpr int MAX_TOTAL_HEADER_SIZE = 256 * 1024;
}
-class Q_NETWORK_PRIVATE_EXPORT QHttpHeaderParser
+class Q_NETWORK_EXPORT QHttpHeaderParser
{
public:
QHttpHeaderParser();
@@ -52,7 +53,7 @@ public:
bool parseHeaders(QByteArrayView headers);
bool parseStatus(QByteArrayView status);
- const QList<QPair<QByteArray, QByteArray> >& headers() const;
+ const QHttpHeaders& headers() const;
void setStatusCode(int code);
int getStatusCode() const;
int getMajorVersion() const;
@@ -62,15 +63,15 @@ public:
QString getReasonPhrase() const;
void setReasonPhrase(const QString &reason);
- QByteArray firstHeaderField(const QByteArray &name,
+ QByteArray firstHeaderField(QByteArrayView name,
const QByteArray &defaultValue = QByteArray()) const;
- QByteArray combinedHeaderValue(const QByteArray &name,
+ QByteArray combinedHeaderValue(QByteArrayView name,
const QByteArray &defaultValue = QByteArray()) const;
- QList<QByteArray> headerFieldValues(const QByteArray &name) const;
+ QList<QByteArray> headerFieldValues(QByteArrayView name) const;
void setHeaderField(const QByteArray &name, const QByteArray &data);
void prependHeaderField(const QByteArray &name, const QByteArray &data);
void appendHeaderField(const QByteArray &name, const QByteArray &data);
- void removeHeaderField(const QByteArray &name);
+ void removeHeaderField(QByteArrayView name);
void clearHeaders();
void setMaxHeaderFieldSize(qsizetype size) { maxFieldSize = size; }
@@ -83,7 +84,7 @@ public:
qsizetype maxHeaderFields() const { return maxFieldCount; }
private:
- QList<QPair<QByteArray, QByteArray> > fields;
+ QHttpHeaders fields;
QString reasonPhrase;
int statusCode;
int majorVersion;
diff --git a/src/network/access/qhttpheaders.cpp b/src/network/access/qhttpheaders.cpp
new file mode 100644
index 0000000000..c63da899a8
--- /dev/null
+++ b/src/network/access/qhttpheaders.cpp
@@ -0,0 +1,1551 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qhttpheaders.h"
+
+#include <private/qoffsetstringarray_p.h>
+
+#include <QtCore/qcompare.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qset.h>
+#include <QtCore/qttypetraits.h>
+
+#include <q20algorithm.h>
+#include <string_view>
+#include <variant>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcQHttpHeaders, "qt.network.http.headers");
+
+/*!
+ \class QHttpHeaders
+ \since 6.7
+ \ingroup
+ \inmodule QtNetwork
+
+ \brief QHttpHeaders is a class for holding HTTP headers.
+
+ The class is an interface type for Qt networking APIs that
+ use or consume such headers.
+
+ \section1 Allowed field name and value characters
+
+ An HTTP header consists of \e name and \e value.
+ When setting these, QHttpHeaders validates \e name and \e value
+ to only contain characters allowed by the HTTP RFCs. For detailed
+ information see
+ \l {https://datatracker.ietf.org/doc/html/rfc9110#name-field-values}
+ {RFC 9110 Chapters 5.1 and 5.5}.
+
+ In all, this means:
+ \list
+ \li \c name must consist of visible ASCII characters, and must not be
+ empty
+ \li \c value may consist of arbitrary bytes, as long as header
+ and use case specific encoding rules are adhered to. \c value
+ may be empty
+ \endlist
+
+ The setters of this class automatically remove any leading or trailing
+ whitespaces from \e value, as they must be ignored during the
+ \e value processing.
+
+ \section1 Combining values
+
+ Most HTTP header values can be combined with a single comma \c {','}
+ plus an optional whitespace, and the semantic meaning is preserved.
+ As an example, these two should be semantically similar:
+ \badcode
+ // Values as separate header entries
+ myheadername: myheadervalue1
+ myheadername: myheadervalue2
+ // Combined value
+ myheadername: myheadervalue1, myheadervalue2
+ \endcode
+
+ However, there is a notable exception to this rule:
+ \l {https://datatracker.ietf.org/doc/html/rfc9110#name-field-order}
+ {Set-Cookie}. Due to this and the possibility of custom use cases,
+ QHttpHeaders does not automatically combine the values.
+
+ \section1 Performance
+
+ Most QHttpHeaders functions provide both
+ \l QHttpHeaders::WellKnownHeader and \l QAnyStringView overloads.
+ From a memory-usage and computation point of view it is recommended
+ to use the \l QHttpHeaders::WellKnownHeader overloads.
+*/
+
+// This list is from IANA HTTP Field Name Registry
+// https://www.iana.org/assignments/http-fields
+// It contains entries that are either "permanent"
+// or "deprecated" as of October 2023.
+// Usage relies on enum values keeping in same order.
+// ### Qt7 check if some of these headers have been obsoleted,
+// and also check if the enums benefit from reordering
+static constexpr auto headerNames = qOffsetStringArray(
+ // IANA Permanent status:
+ "a-im",
+ "accept",
+ "accept-additions",
+ "accept-ch",
+ "accept-datetime",
+ "accept-encoding",
+ "accept-features",
+ "accept-language",
+ "accept-patch",
+ "accept-post",
+ "accept-ranges",
+ "accept-signature",
+ "access-control-allow-credentials",
+ "access-control-allow-headers",
+ "access-control-allow-methods",
+ "access-control-allow-origin",
+ "access-control-expose-headers",
+ "access-control-max-age",
+ "access-control-request-headers",
+ "access-control-request-method",
+ "age",
+ "allow",
+ "alpn",
+ "alt-svc",
+ "alt-used",
+ "alternates",
+ "apply-to-redirect-ref",
+ "authentication-control",
+ "authentication-info",
+ "authorization",
+ "cache-control",
+ "cache-status",
+ "cal-managed-id",
+ "caldav-timezones",
+ "capsule-protocol",
+ "cdn-cache-control",
+ "cdn-loop",
+ "cert-not-after",
+ "cert-not-before",
+ "clear-site-data",
+ "client-cert",
+ "client-cert-chain",
+ "close",
+ "connection",
+ "content-digest",
+ "content-disposition",
+ "content-encoding",
+ "content-id",
+ "content-language",
+ "content-length",
+ "content-location",
+ "content-range",
+ "content-security-policy",
+ "content-security-policy-report-only",
+ "content-type",
+ "cookie",
+ "cross-origin-embedder-policy",
+ "cross-origin-embedder-policy-report-only",
+ "cross-origin-opener-policy",
+ "cross-origin-opener-policy-report-only",
+ "cross-origin-resource-policy",
+ "dasl",
+ "date",
+ "dav",
+ "delta-base",
+ "depth",
+ "destination",
+ "differential-id",
+ "dpop",
+ "dpop-nonce",
+ "early-data",
+ "etag",
+ "expect",
+ "expect-ct",
+ "expires",
+ "forwarded",
+ "from",
+ "hobareg",
+ "host",
+ "if",
+ "if-match",
+ "if-modified-since",
+ "if-none-match",
+ "if-range",
+ "if-schedule-tag-match",
+ "if-unmodified-since",
+ "im",
+ "include-referred-token-binding-id",
+ "keep-alive",
+ "label",
+ "last-event-id",
+ "last-modified",
+ "link",
+ "location",
+ "lock-token",
+ "max-forwards",
+ "memento-datetime",
+ "meter",
+ "mime-version",
+ "negotiate",
+ "nel",
+ "odata-entityid",
+ "odata-isolation",
+ "odata-maxversion",
+ "odata-version",
+ "optional-www-authenticate",
+ "ordering-type",
+ "origin",
+ "origin-agent-cluster",
+ "oscore",
+ "oslc-core-version",
+ "overwrite",
+ "ping-from",
+ "ping-to",
+ "position",
+ "prefer",
+ "preference-applied",
+ "priority",
+ "proxy-authenticate",
+ "proxy-authentication-info",
+ "proxy-authorization",
+ "proxy-status",
+ "public-key-pins",
+ "public-key-pins-report-only",
+ "range",
+ "redirect-ref",
+ "referer",
+ "refresh",
+ "replay-nonce",
+ "repr-digest",
+ "retry-after",
+ "schedule-reply",
+ "schedule-tag",
+ "sec-purpose",
+ "sec-token-binding",
+ "sec-websocket-accept",
+ "sec-websocket-extensions",
+ "sec-websocket-key",
+ "sec-websocket-protocol",
+ "sec-websocket-version",
+ "server",
+ "server-timing",
+ "set-cookie",
+ "signature",
+ "signature-input",
+ "slug",
+ "soapaction",
+ "status-uri",
+ "strict-transport-security",
+ "sunset",
+ "surrogate-capability",
+ "surrogate-control",
+ "tcn",
+ "te",
+ "timeout",
+ "topic",
+ "traceparent",
+ "tracestate",
+ "trailer",
+ "transfer-encoding",
+ "ttl",
+ "upgrade",
+ "urgency",
+ "user-agent",
+ "variant-vary",
+ "vary",
+ "via",
+ "want-content-digest",
+ "want-repr-digest",
+ "www-authenticate",
+ "x-content-type-options",
+ "x-frame-options",
+ // IANA Deprecated status:
+ "accept-charset",
+ "c-pep-info",
+ "pragma",
+ "protocol-info",
+ "protocol-query"
+ // If you append here, regenerate the index table
+);
+
+namespace {
+struct ByIndirectHeaderName
+{
+ constexpr bool operator()(quint8 lhs, quint8 rhs) const noexcept
+ {
+ return (*this)(map(lhs), map(rhs));
+ }
+ constexpr bool operator()(quint8 lhs, QByteArrayView rhs) const noexcept
+ {
+ return (*this)(map(lhs), rhs);
+ }
+ constexpr bool operator()(QByteArrayView lhs, quint8 rhs) const noexcept
+ {
+ return (*this)(lhs, map(rhs));
+ }
+ constexpr bool operator()(QByteArrayView lhs, QByteArrayView rhs) const noexcept
+ {
+ // ### just `lhs < rhs` when QByteArrayView relational operators are constexpr
+ return std::string_view(lhs) < std::string_view(rhs);
+ }
+private:
+ static constexpr QByteArrayView map(quint8 i) noexcept
+ {
+ return headerNames.viewAt(i);
+ }
+};
+} // unnamed namespace
+
+// This index table contains the indexes of 'headerNames' entries (above) in alphabetical order.
+// This allows a more efficient binary search for the names [O(logN)]. The 'headerNames' itself
+// cannot be guaranteed to be in alphabetical order, as it must keep the same order as the
+// WellKnownHeader enum, which may get appended over time.
+//
+// Note: when appending new enums, this must be regenerated
+static constexpr quint8 orderedHeaderNameIndexes[] = {
+ 0, // a-im
+ 1, // accept
+ 2, // accept-additions
+ 3, // accept-ch
+ 172, // accept-charset
+ 4, // accept-datetime
+ 5, // accept-encoding
+ 6, // accept-features
+ 7, // accept-language
+ 8, // accept-patch
+ 9, // accept-post
+ 10, // accept-ranges
+ 11, // accept-signature
+ 12, // access-control-allow-credentials
+ 13, // access-control-allow-headers
+ 14, // access-control-allow-methods
+ 15, // access-control-allow-origin
+ 16, // access-control-expose-headers
+ 17, // access-control-max-age
+ 18, // access-control-request-headers
+ 19, // access-control-request-method
+ 20, // age
+ 21, // allow
+ 22, // alpn
+ 23, // alt-svc
+ 24, // alt-used
+ 25, // alternates
+ 26, // apply-to-redirect-ref
+ 27, // authentication-control
+ 28, // authentication-info
+ 29, // authorization
+ 173, // c-pep-info
+ 30, // cache-control
+ 31, // cache-status
+ 32, // cal-managed-id
+ 33, // caldav-timezones
+ 34, // capsule-protocol
+ 35, // cdn-cache-control
+ 36, // cdn-loop
+ 37, // cert-not-after
+ 38, // cert-not-before
+ 39, // clear-site-data
+ 40, // client-cert
+ 41, // client-cert-chain
+ 42, // close
+ 43, // connection
+ 44, // content-digest
+ 45, // content-disposition
+ 46, // content-encoding
+ 47, // content-id
+ 48, // content-language
+ 49, // content-length
+ 50, // content-location
+ 51, // content-range
+ 52, // content-security-policy
+ 53, // content-security-policy-report-only
+ 54, // content-type
+ 55, // cookie
+ 56, // cross-origin-embedder-policy
+ 57, // cross-origin-embedder-policy-report-only
+ 58, // cross-origin-opener-policy
+ 59, // cross-origin-opener-policy-report-only
+ 60, // cross-origin-resource-policy
+ 61, // dasl
+ 62, // date
+ 63, // dav
+ 64, // delta-base
+ 65, // depth
+ 66, // destination
+ 67, // differential-id
+ 68, // dpop
+ 69, // dpop-nonce
+ 70, // early-data
+ 71, // etag
+ 72, // expect
+ 73, // expect-ct
+ 74, // expires
+ 75, // forwarded
+ 76, // from
+ 77, // hobareg
+ 78, // host
+ 79, // if
+ 80, // if-match
+ 81, // if-modified-since
+ 82, // if-none-match
+ 83, // if-range
+ 84, // if-schedule-tag-match
+ 85, // if-unmodified-since
+ 86, // im
+ 87, // include-referred-token-binding-id
+ 88, // keep-alive
+ 89, // label
+ 90, // last-event-id
+ 91, // last-modified
+ 92, // link
+ 93, // location
+ 94, // lock-token
+ 95, // max-forwards
+ 96, // memento-datetime
+ 97, // meter
+ 98, // mime-version
+ 99, // negotiate
+ 100, // nel
+ 101, // odata-entityid
+ 102, // odata-isolation
+ 103, // odata-maxversion
+ 104, // odata-version
+ 105, // optional-www-authenticate
+ 106, // ordering-type
+ 107, // origin
+ 108, // origin-agent-cluster
+ 109, // oscore
+ 110, // oslc-core-version
+ 111, // overwrite
+ 112, // ping-from
+ 113, // ping-to
+ 114, // position
+ 174, // pragma
+ 115, // prefer
+ 116, // preference-applied
+ 117, // priority
+ 175, // protocol-info
+ 176, // protocol-query
+ 118, // proxy-authenticate
+ 119, // proxy-authentication-info
+ 120, // proxy-authorization
+ 121, // proxy-status
+ 122, // public-key-pins
+ 123, // public-key-pins-report-only
+ 124, // range
+ 125, // redirect-ref
+ 126, // referer
+ 127, // refresh
+ 128, // replay-nonce
+ 129, // repr-digest
+ 130, // retry-after
+ 131, // schedule-reply
+ 132, // schedule-tag
+ 133, // sec-purpose
+ 134, // sec-token-binding
+ 135, // sec-websocket-accept
+ 136, // sec-websocket-extensions
+ 137, // sec-websocket-key
+ 138, // sec-websocket-protocol
+ 139, // sec-websocket-version
+ 140, // server
+ 141, // server-timing
+ 142, // set-cookie
+ 143, // signature
+ 144, // signature-input
+ 145, // slug
+ 146, // soapaction
+ 147, // status-uri
+ 148, // strict-transport-security
+ 149, // sunset
+ 150, // surrogate-capability
+ 151, // surrogate-control
+ 152, // tcn
+ 153, // te
+ 154, // timeout
+ 155, // topic
+ 156, // traceparent
+ 157, // tracestate
+ 158, // trailer
+ 159, // transfer-encoding
+ 160, // ttl
+ 161, // upgrade
+ 162, // urgency
+ 163, // user-agent
+ 164, // variant-vary
+ 165, // vary
+ 166, // via
+ 167, // want-content-digest
+ 168, // want-repr-digest
+ 169, // www-authenticate
+ 170, // x-content-type-options
+ 171, // x-frame-options
+};
+static_assert(std::size(orderedHeaderNameIndexes) == size_t(headerNames.count()));
+static_assert(q20::is_sorted(std::begin(orderedHeaderNameIndexes),
+ std::end(orderedHeaderNameIndexes),
+ ByIndirectHeaderName{}));
+
+/*!
+ \enum QHttpHeaders::WellKnownHeader
+
+ List of well known headers as per
+ \l {https://www.iana.org/assignments/http-fields}{IANA registry}.
+
+ \value AIM
+ \value Accept
+ \value AcceptAdditions
+ \value AcceptCH
+ \value AcceptDatetime
+ \value AcceptEncoding
+ \value AcceptFeatures
+ \value AcceptLanguage
+ \value AcceptPatch
+ \value AcceptPost
+ \value AcceptRanges
+ \value AcceptSignature
+ \value AccessControlAllowCredentials
+ \value AccessControlAllowHeaders
+ \value AccessControlAllowMethods
+ \value AccessControlAllowOrigin
+ \value AccessControlExposeHeaders
+ \value AccessControlMaxAge
+ \value AccessControlRequestHeaders
+ \value AccessControlRequestMethod
+ \value Age
+ \value Allow
+ \value ALPN
+ \value AltSvc
+ \value AltUsed
+ \value Alternates
+ \value ApplyToRedirectRef
+ \value AuthenticationControl
+ \value AuthenticationInfo
+ \value Authorization
+ \value CacheControl
+ \value CacheStatus
+ \value CalManagedID
+ \value CalDAVTimezones
+ \value CapsuleProtocol
+ \value CDNCacheControl
+ \value CDNLoop
+ \value CertNotAfter
+ \value CertNotBefore
+ \value ClearSiteData
+ \value ClientCert
+ \value ClientCertChain
+ \value Close
+ \value Connection
+ \value ContentDigest
+ \value ContentDisposition
+ \value ContentEncoding
+ \value ContentID
+ \value ContentLanguage
+ \value ContentLength
+ \value ContentLocation
+ \value ContentRange
+ \value ContentSecurityPolicy
+ \value ContentSecurityPolicyReportOnly
+ \value ContentType
+ \value Cookie
+ \value CrossOriginEmbedderPolicy
+ \value CrossOriginEmbedderPolicyReportOnly
+ \value CrossOriginOpenerPolicy
+ \value CrossOriginOpenerPolicyReportOnly
+ \value CrossOriginResourcePolicy
+ \value DASL
+ \value Date
+ \value DAV
+ \value DeltaBase
+ \value Depth
+ \value Destination
+ \value DifferentialID
+ \value DPoP
+ \value DPoPNonce
+ \value EarlyData
+ \value ETag
+ \value Expect
+ \value ExpectCT
+ \value Expires
+ \value Forwarded
+ \value From
+ \value Hobareg
+ \value Host
+ \value If
+ \value IfMatch
+ \value IfModifiedSince
+ \value IfNoneMatch
+ \value IfRange
+ \value IfScheduleTagMatch
+ \value IfUnmodifiedSince
+ \value IM
+ \value IncludeReferredTokenBindingID
+ \value KeepAlive
+ \value Label
+ \value LastEventID
+ \value LastModified
+ \value Link
+ \value Location
+ \value LockToken
+ \value MaxForwards
+ \value MementoDatetime
+ \value Meter
+ \value MIMEVersion
+ \value Negotiate
+ \value NEL
+ \value ODataEntityId
+ \value ODataIsolation
+ \value ODataMaxVersion
+ \value ODataVersion
+ \value OptionalWWWAuthenticate
+ \value OrderingType
+ \value Origin
+ \value OriginAgentCluster
+ \value OSCORE
+ \value OSLCCoreVersion
+ \value Overwrite
+ \value PingFrom
+ \value PingTo
+ \value Position
+ \value Prefer
+ \value PreferenceApplied
+ \value Priority
+ \value ProxyAuthenticate
+ \value ProxyAuthenticationInfo
+ \value ProxyAuthorization
+ \value ProxyStatus
+ \value PublicKeyPins
+ \value PublicKeyPinsReportOnly
+ \value Range
+ \value RedirectRef
+ \value Referer
+ \value Refresh
+ \value ReplayNonce
+ \value ReprDigest
+ \value RetryAfter
+ \value ScheduleReply
+ \value ScheduleTag
+ \value SecPurpose
+ \value SecTokenBinding
+ \value SecWebSocketAccept
+ \value SecWebSocketExtensions
+ \value SecWebSocketKey
+ \value SecWebSocketProtocol
+ \value SecWebSocketVersion
+ \value Server
+ \value ServerTiming
+ \value SetCookie
+ \value Signature
+ \value SignatureInput
+ \value SLUG
+ \value SoapAction
+ \value StatusURI
+ \value StrictTransportSecurity
+ \value Sunset
+ \value SurrogateCapability
+ \value SurrogateControl
+ \value TCN
+ \value TE
+ \value Timeout
+ \value Topic
+ \value Traceparent
+ \value Tracestate
+ \value Trailer
+ \value TransferEncoding
+ \value TTL
+ \value Upgrade
+ \value Urgency
+ \value UserAgent
+ \value VariantVary
+ \value Vary
+ \value Via
+ \value WantContentDigest
+ \value WantReprDigest
+ \value WWWAuthenticate
+ \value XContentTypeOptions
+ \value XFrameOptions
+ \value AcceptCharset
+ \value CPEPInfo
+ \value Pragma
+ \value ProtocolInfo
+ \value ProtocolQuery
+*/
+
+static QByteArray fieldToByteArray(QLatin1StringView s) noexcept
+{
+ return QByteArray(s.data(), s.size());
+}
+
+static QByteArray fieldToByteArray(QUtf8StringView s) noexcept
+{
+ return QByteArray(s.data(), s.size());
+}
+
+static QByteArray fieldToByteArray(QStringView s)
+{
+ return s.toLatin1();
+}
+
+static QByteArray normalizedName(QAnyStringView name)
+{
+ return name.visit([](auto name){ return fieldToByteArray(name); }).toLower();
+}
+
+struct HeaderName
+{
+ explicit HeaderName(QHttpHeaders::WellKnownHeader name) : data(name)
+ {
+ }
+
+ explicit HeaderName(QAnyStringView name)
+ {
+ auto nname = normalizedName(name);
+ if (auto h = HeaderName::toWellKnownHeader(nname))
+ data = *h;
+ else
+ data = std::move(nname);
+ }
+
+ // Returns an enum corresponding with the 'name' if possible. Uses binary search (O(logN)).
+ // The function doesn't normalize the data; needs to be done by the caller if needed
+ static std::optional<QHttpHeaders::WellKnownHeader> toWellKnownHeader(QByteArrayView name) noexcept
+ {
+ auto indexesBegin = std::cbegin(orderedHeaderNameIndexes);
+ auto indexesEnd = std::cend(orderedHeaderNameIndexes);
+
+ auto result = std::lower_bound(indexesBegin, indexesEnd, name, ByIndirectHeaderName{});
+
+ if (result != indexesEnd && name == headerNames[*result])
+ return static_cast<QHttpHeaders::WellKnownHeader>(*result);
+ return std::nullopt;
+ }
+
+ QByteArrayView asView() const noexcept
+ {
+ return std::visit([](const auto &arg) -> QByteArrayView {
+ using T = decltype(arg);
+ if constexpr (std::is_same_v<T, const QByteArray &>)
+ return arg;
+ else if constexpr (std::is_same_v<T, const QHttpHeaders::WellKnownHeader &>)
+ return headerNames.viewAt(qToUnderlying(arg));
+ else
+ static_assert(QtPrivate::type_dependent_false<T>());
+ }, data);
+ }
+
+ QByteArray asByteArray() const noexcept
+ {
+ return std::visit([](const auto &arg) -> QByteArray {
+ using T = decltype(arg);
+ if constexpr (std::is_same_v<T, const QByteArray &>) {
+ return arg;
+ } else if constexpr (std::is_same_v<T, const QHttpHeaders::WellKnownHeader &>) {
+ const auto view = headerNames.viewAt(qToUnderlying(arg));
+ return QByteArray::fromRawData(view.constData(), view.size());
+ } else {
+ static_assert(QtPrivate::type_dependent_false<T>());
+ }
+ }, data);
+ }
+
+private:
+ // Store the data as 'enum' whenever possible; more performant, and comparison relies on that
+ std::variant<QHttpHeaders::WellKnownHeader, QByteArray> data;
+
+ friend bool comparesEqual(const HeaderName &lhs, const HeaderName &rhs) noexcept
+ {
+ // Here we compare two std::variants, which will return false if the types don't match.
+ // That is beneficial here because we avoid unnecessary comparisons; but it also means
+ // we must always store the data as WellKnownHeader when possible (in other words, if
+ // we get a string that is mappable to a WellKnownHeader). To guard against accidental
+ // misuse, the 'data' is private and the constructors must be used.
+ return lhs.data == rhs.data;
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE(HeaderName)
+};
+
+// A clarification on case-sensitivity:
+// - Header *names* are case-insensitive; Content-Type and content-type are considered equal
+// - Header *values* are case-sensitive
+// (In addition, the HTTP/2 and HTTP/3 standards mandate that all headers must be lower-cased when
+// encoded into transmission)
+struct Header {
+ HeaderName name;
+ QByteArray value;
+};
+
+auto headerNameMatches(const HeaderName &name)
+{
+ return [&name](const Header &header) { return header.name == name; };
+}
+
+class QHttpHeadersPrivate : public QSharedData
+{
+public:
+ QHttpHeadersPrivate() = default;
+
+ // The 'Self' is supplied as parameter to static functions so that
+ // we can define common methods which 'detach()' the private itself.
+ using Self = QExplicitlySharedDataPointer<QHttpHeadersPrivate>;
+ static void removeAll(Self &d, const HeaderName &name);
+ static void replaceOrAppend(Self &d, const HeaderName &name, const QByteArray &value);
+
+ void combinedValue(const HeaderName &name, QByteArray &result) const;
+ void values(const HeaderName &name, QList<QByteArray> &result) const;
+ QByteArrayView value(const HeaderName &name, QByteArrayView defaultValue) const noexcept;
+
+ QList<Header> headers;
+};
+
+QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QHttpHeadersPrivate)
+template <> void QExplicitlySharedDataPointer<QHttpHeadersPrivate>::detach()
+{
+ if (!d) {
+ d = new QHttpHeadersPrivate();
+ d->ref.ref();
+ } else if (d->ref.loadRelaxed() != 1) {
+ detach_helper();
+ }
+}
+
+void QHttpHeadersPrivate::removeAll(Self &d, const HeaderName &name)
+{
+ const auto it = std::find_if(d->headers.cbegin(), d->headers.cend(), headerNameMatches(name));
+
+ if (it != d->headers.cend()) {
+ // Found something to remove, calculate offset so we can proceed from the match-location
+ const auto matchOffset = it - d->headers.cbegin();
+ d.detach();
+ // Rearrange all matches to the end and erase them
+ d->headers.erase(std::remove_if(d->headers.begin() + matchOffset, d->headers.end(),
+ headerNameMatches(name)),
+ d->headers.end());
+ }
+}
+
+void QHttpHeadersPrivate::combinedValue(const HeaderName &name, QByteArray &result) const
+{
+ const char* separator = "";
+ for (const auto &h : std::as_const(headers)) {
+ if (h.name == name) {
+ result.append(separator);
+ result.append(h.value);
+ separator = ", ";
+ }
+ }
+}
+
+void QHttpHeadersPrivate::values(const HeaderName &name, QList<QByteArray> &result) const
+{
+ for (const auto &h : std::as_const(headers)) {
+ if (h.name == name)
+ result.append(h.value);
+ }
+}
+
+QByteArrayView QHttpHeadersPrivate::value(const HeaderName &name, QByteArrayView defaultValue) const noexcept
+{
+ for (const auto &h : std::as_const(headers)) {
+ if (h.name == name)
+ return h.value;
+ }
+ return defaultValue;
+}
+
+void QHttpHeadersPrivate::replaceOrAppend(Self &d, const HeaderName &name, const QByteArray &value)
+{
+ d.detach();
+ auto it = std::find_if(d->headers.begin(), d->headers.end(), headerNameMatches(name));
+ if (it != d->headers.end()) {
+ // Found something to replace => replace, and then rearrange any remaining
+ // matches to the end and erase them
+ it->value = value;
+ d->headers.erase(
+ std::remove_if(it + 1, d->headers.end(), headerNameMatches(name)),
+ d->headers.end());
+ } else {
+ // Found nothing to replace => append
+ d->headers.append(Header{name, value});
+ }
+}
+
+/*!
+ Creates a new QHttpHeaders object.
+*/
+QHttpHeaders::QHttpHeaders() noexcept : d()
+{
+}
+
+/*!
+ Creates a new QHttpHeaders object that is populated with
+ \a headers.
+
+ \sa {Allowed field name and value characters}
+*/
+QHttpHeaders QHttpHeaders::fromListOfPairs(const QList<std::pair<QByteArray, QByteArray>> &headers)
+{
+ QHttpHeaders h;
+ h.reserve(headers.size());
+ for (const auto &header : headers)
+ h.append(header.first, header.second);
+ return h;
+}
+
+/*!
+ Creates a new QHttpHeaders object that is populated with
+ \a headers.
+
+ \sa {Allowed field name and value characters}
+*/
+QHttpHeaders QHttpHeaders::fromMultiMap(const QMultiMap<QByteArray, QByteArray> &headers)
+{
+ QHttpHeaders h;
+ h.reserve(headers.size());
+ for (const auto &[name,value] : headers.asKeyValueRange())
+ h.append(name, value);
+ return h;
+}
+
+/*!
+ Creates a new QHttpHeaders object that is populated with
+ \a headers.
+
+ \sa {Allowed field name and value characters}
+*/
+QHttpHeaders QHttpHeaders::fromMultiHash(const QMultiHash<QByteArray, QByteArray> &headers)
+{
+ QHttpHeaders h;
+ h.reserve(headers.size());
+ for (const auto &[name,value] : headers.asKeyValueRange())
+ h.append(name, value);
+ return h;
+}
+
+/*!
+ Disposes of the headers object.
+*/
+QHttpHeaders::~QHttpHeaders()
+ = default;
+
+/*!
+ Creates a copy of \a other.
+*/
+QHttpHeaders::QHttpHeaders(const QHttpHeaders &other)
+ = default;
+
+/*!
+ Assigns the contents of \a other and returns a reference to this object.
+*/
+QHttpHeaders &QHttpHeaders::operator=(const QHttpHeaders &other)
+ = default;
+
+/*!
+ \fn QHttpHeaders::QHttpHeaders(QHttpHeaders &&other) noexcept
+
+ Move-constructs the object from \a other, which will be left
+ \l{isEmpty()}{empty}.
+*/
+
+/*!
+ \fn QHttpHeaders &QHttpHeaders::operator=(QHttpHeaders &&other) noexcept
+
+ Move-assigns \a other and returns a reference to this object.
+
+ \a other will be left \l{isEmpty()}{empty}.
+*/
+
+/*!
+ \fn void QHttpHeaders::swap(QHttpHeaders &other)
+
+ Swaps this QHttpHeaders with \a other. This function is very fast and
+ never fails.
+*/
+
+#ifndef QT_NO_DEBUG_STREAM
+/*!
+ \fn QDebug QHttpHeaders::operator<<(QDebug debug,
+ const QHttpHeaders &headers)
+
+ Writes \a headers into \a debug stream.
+*/
+QDebug operator<<(QDebug debug, const QHttpHeaders &headers)
+{
+ const QDebugStateSaver saver(debug);
+ debug.resetFormat().nospace();
+
+ debug << "QHttpHeaders(";
+ if (headers.d) {
+ debug << "headers = ";
+ const char *separator = "";
+ for (const auto &h : headers.d->headers) {
+ debug << separator << h.name.asView() << ':' << h.value;
+ separator = " | ";
+ }
+ }
+ debug << ")";
+ return debug;
+}
+#endif
+
+// A clarification on string encoding:
+// Setters and getters only accept names and values that are Latin-1 representable:
+// Either they are directly ASCII/Latin-1, or if they are UTF-X, they only use first 256
+// of the unicode points. For example using a '€' (U+20AC) in value would yield a warning
+// and the call is ignored.
+// Furthermore the 'name' has more strict rules than the 'value'
+
+// TODO FIXME REMOVEME once this is merged:
+// https://codereview.qt-project.org/c/qt/qtbase/+/508829
+static bool isUtf8Latin1Representable(QUtf8StringView s) noexcept
+{
+ // L1 encoded in UTF8 has at most the form
+ // - 0b0XXX'XXXX - US-ASCII
+ // - 0b1100'00XX 0b10XX'XXXX - at most 8 non-zero LSB bits allowed in L1
+ bool inMultibyte = false;
+ for (unsigned char c : s) {
+ if (c < 128) { // US-ASCII
+ if (inMultibyte)
+ return false; // invalid sequence
+ } else {
+ // decode as UTF-8:
+ if ((c & 0b1110'0000) == 0b1100'0000) { // two-octet UTF-8 leader
+ if (inMultibyte)
+ return false; // invalid sequence
+ inMultibyte = true;
+ const auto bits_7_to_11 = c & 0b0001'1111;
+ if (bits_7_to_11 < 0b10)
+ return false; // invalid sequence (US-ASCII encoded in two octets)
+ if (bits_7_to_11 > 0b11) // more than the two LSB
+ return false; // outside L1
+ } else if ((c & 0b1100'0000) == 0b1000'0000) { // trailing UTF-8 octet
+ if (!inMultibyte)
+ return false; // invalid sequence
+ inMultibyte = false; // only one continuation allowed
+ } else {
+ return false; // invalid sequence or outside of L1
+ }
+ }
+ }
+ if (inMultibyte)
+ return false; // invalid sequence: premature end
+ return true;
+}
+
+static constexpr auto isValidHttpHeaderNameChar = [](uchar c) noexcept
+{
+ // RFC 9110 Chapters "5.1 Field Names" and "5.6.2 Tokens"
+ // field-name = token
+ // token = 1*tchar
+ // tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" /
+ // "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
+ // / DIGIT / ALPHA
+ // ; any VCHAR, except delimiters
+ // (for explanation on VCHAR see isValidHttpHeaderValueChar)
+ return (('A' <= c && c <= 'Z')
+ || ('a' <= c && c <= 'z')
+ || ('0' <= c && c <= '9')
+ || ('#' <= c && c <= '\'')
+ || ('^' <= c && c <= '`')
+ || c == '|' || c == '~' || c == '!' || c == '*' || c == '+' || c == '-' || c == '.');
+};
+
+static bool headerNameValidImpl(QLatin1StringView name) noexcept
+{
+ return std::all_of(name.begin(), name.end(), isValidHttpHeaderNameChar);
+}
+
+static bool headerNameValidImpl(QUtf8StringView name) noexcept
+{
+ // Traversing the UTF-8 string char-by-char is fine in this case as
+ // the isValidHttpHeaderNameChar rejects any value above 0x7E. UTF-8
+ // only has bytes <= 0x7F if they truly represent that ASCII character.
+ return headerNameValidImpl(QLatin1StringView(QByteArrayView(name)));
+}
+
+static bool headerNameValidImpl(QStringView name) noexcept
+{
+ return std::all_of(name.begin(), name.end(), [](QChar c) {
+ return isValidHttpHeaderNameChar(c.toLatin1());
+ });
+}
+
+static bool isValidHttpHeaderNameField(QAnyStringView name) noexcept
+{
+ if (name.isEmpty()) {
+ qCWarning(lcQHttpHeaders, "HTTP header name cannot be empty");
+ return false;
+ }
+ const bool valid = name.visit([](auto name){ return headerNameValidImpl(name); });
+ if (!valid)
+ qCWarning(lcQHttpHeaders, "HTTP header name contained illegal character(s)");
+ return valid;
+}
+
+static constexpr auto isValidHttpHeaderValueChar = [](uchar c) noexcept
+{
+ // RFC 9110 Chapter 5.5, Field Values
+ // field-value = *field-content
+ // field-content = field-vchar
+ // [ 1*( SP / HTAB / field-vchar ) field-vchar ]
+ // field-vchar = VCHAR / obs-text
+ // obs-text = %x80-FF
+ // VCHAR is defined as "any visible US-ASCII character", and RFC 5234 B.1.
+ // defines it as %x21-7E
+ // Note: The ABNF above states that field-content and thus field-value cannot
+ // start or end with SP/HTAB. The caller should handle this.
+ return (c >= 0x80 // obs-text (extended ASCII)
+ || (0x20 <= c && c <= 0x7E) // SP (0x20) + VCHAR
+ || (c == 0x09)); // HTAB
+};
+
+static bool headerValueValidImpl(QLatin1StringView value) noexcept
+{
+ return std::all_of(value.begin(), value.end(), isValidHttpHeaderValueChar);
+}
+
+static bool headerValueValidImpl(QUtf8StringView value) noexcept
+{
+ if (!isUtf8Latin1Representable(value)) // TODO FIXME see the function
+ return false;
+ return std::all_of(value.begin(), value.end(), isValidHttpHeaderValueChar);
+}
+
+static bool headerValueValidImpl(QStringView value) noexcept
+{
+ return std::all_of(value.begin(), value.end(), [](QChar c) {
+ return isValidHttpHeaderValueChar(c.toLatin1());
+ });
+}
+
+static bool isValidHttpHeaderValueField(QAnyStringView value) noexcept
+{
+ const bool valid = value.visit([](auto value){ return headerValueValidImpl(value); });
+ if (!valid)
+ qCWarning(lcQHttpHeaders, "HTTP header value contained illegal character(s)");
+ return valid;
+}
+
+static QByteArray normalizedValue(QAnyStringView value)
+{
+ // Note on trimming away any leading or trailing whitespace of 'value':
+ // RFC 9110 (HTTP 1.1, 2022, Chapter 5.5) does not allow leading or trailing whitespace
+ // RFC 7230 (HTTP 1.1, 2014, Chapter 3.2) allows them optionally, but also mandates that
+ // they are ignored during processing
+ // RFC 7540 (HTTP/2) does not seem explicit about it
+ // => for maximum compatibility, trim away any leading or trailing whitespace
+ return value.visit([](auto value){ return fieldToByteArray(value); }).trimmed();
+}
+
+/*!
+ Appends a header entry with \a name and \a value and returns \c true
+ if successful.
+
+ \sa append(QHttpHeaders::WellKnownHeader, QAnyStringView)
+ \sa {Allowed field name and value characters}
+*/
+bool QHttpHeaders::append(QAnyStringView name, QAnyStringView value)
+{
+ if (!isValidHttpHeaderNameField(name) || !isValidHttpHeaderValueField(value))
+ return false;
+
+ d.detach();
+ d->headers.push_back({HeaderName{name}, normalizedValue(value)});
+ return true;
+}
+
+/*!
+ \overload append(QAnyStringView, QAnyStringView)
+*/
+bool QHttpHeaders::append(WellKnownHeader name, QAnyStringView value)
+{
+ if (!isValidHttpHeaderValueField(value))
+ return false;
+
+ d.detach();
+ d->headers.push_back({HeaderName{name}, normalizedValue(value)});
+ return true;
+}
+
+/*!
+ Inserts a header entry at index \a i, with \a name and \a value. The index
+ must be valid (see \l size()). Returns whether the insert succeeded.
+
+ \sa append(),
+ insert(qsizetype, QHttpHeaders::WellKnownHeader, QAnyStringView), size()
+ \sa {Allowed field name and value characters}
+*/
+bool QHttpHeaders::insert(qsizetype i, QAnyStringView name, QAnyStringView value)
+{
+ verify(i, 0);
+ if (!isValidHttpHeaderNameField(name) || !isValidHttpHeaderValueField(value))
+ return false;
+
+ d.detach();
+ d->headers.insert(i, {HeaderName{name}, normalizedValue(value)});
+ return true;
+}
+
+/*!
+ \overload insert(qsizetype, QAnyStringView, QAnyStringView)
+*/
+bool QHttpHeaders::insert(qsizetype i, WellKnownHeader name, QAnyStringView value)
+{
+ verify(i, 0);
+ if (!isValidHttpHeaderValueField(value))
+ return false;
+
+ d.detach();
+ d->headers.insert(i, {HeaderName{name}, normalizedValue(value)});
+ return true;
+}
+
+/*!
+ Replaces the header entry at index \a i, with \a name and \a newValue.
+ The index must be valid (see \l size()). Returns whether the replace
+ succeeded.
+
+ \sa append(),
+ replace(qsizetype, QHttpHeaders::WellKnownHeader, QAnyStringView), size()
+ \sa {Allowed field name and value characters}
+*/
+bool QHttpHeaders::replace(qsizetype i, QAnyStringView name, QAnyStringView newValue)
+{
+ verify(i);
+ if (!isValidHttpHeaderNameField(name) || !isValidHttpHeaderValueField(newValue))
+ return false;
+
+ d.detach();
+ d->headers.replace(i, {HeaderName{name}, normalizedValue(newValue)});
+ return true;
+}
+
+/*!
+ \overload replace(qsizetype, QAnyStringView, QAnyStringView)
+*/
+bool QHttpHeaders::replace(qsizetype i, WellKnownHeader name, QAnyStringView newValue)
+{
+ verify(i);
+ if (!isValidHttpHeaderValueField(newValue))
+ return false;
+
+ d.detach();
+ d->headers.replace(i, {HeaderName{name}, normalizedValue(newValue)});
+ return true;
+}
+
+/*!
+ \since 6.8
+
+ If QHttpHeaders already contains \a name, replaces its value with
+ \a newValue and removes possible additional \a name entries.
+ If \a name didn't exist, appends a new entry. Returns \c true
+ if successful.
+
+ This function is a convenience method for setting a unique
+ \a name : \a newValue header. For most headers the relative order does not
+ matter, which allows reusing an existing entry if one exists.
+
+ \sa replaceOrAppend(QAnyStringView, QAnyStringView)
+*/
+bool QHttpHeaders::replaceOrAppend(WellKnownHeader name, QAnyStringView newValue)
+{
+ if (isEmpty())
+ return append(name, newValue);
+
+ if (!isValidHttpHeaderValueField(newValue))
+ return false;
+
+ QHttpHeadersPrivate::replaceOrAppend(d, HeaderName{name}, normalizedValue(newValue));
+ return true;
+}
+
+/*!
+ \overload replaceOrAppend(WellKnownHeader, QAnyStringView)
+*/
+bool QHttpHeaders::replaceOrAppend(QAnyStringView name, QAnyStringView newValue)
+{
+ if (isEmpty())
+ return append(name, newValue);
+
+ if (!isValidHttpHeaderNameField(name) || !isValidHttpHeaderValueField(newValue))
+ return false;
+
+ QHttpHeadersPrivate::replaceOrAppend(d, HeaderName{name}, normalizedValue(newValue));
+ return true;
+}
+
+/*!
+ Returns whether the headers contain header with \a name.
+
+ \sa contains(QHttpHeaders::WellKnownHeader)
+*/
+bool QHttpHeaders::contains(QAnyStringView name) const
+{
+ if (isEmpty())
+ return false;
+
+ return std::any_of(d->headers.cbegin(), d->headers.cend(), headerNameMatches(HeaderName{name}));
+}
+
+/*!
+ \overload has(QAnyStringView)
+*/
+bool QHttpHeaders::contains(WellKnownHeader name) const
+{
+ if (isEmpty())
+ return false;
+
+ return std::any_of(d->headers.cbegin(), d->headers.cend(), headerNameMatches(HeaderName{name}));
+}
+
+/*!
+ Removes the header \a name.
+
+ \sa removeAt(), removeAll(QHttpHeaders::WellKnownHeader)
+*/
+void QHttpHeaders::removeAll(QAnyStringView name)
+{
+ if (isEmpty())
+ return;
+
+ return QHttpHeadersPrivate::removeAll(d, HeaderName(name));
+}
+
+/*!
+ \overload removeAll(QAnyStringView)
+*/
+void QHttpHeaders::removeAll(WellKnownHeader name)
+{
+ if (isEmpty())
+ return;
+
+ return QHttpHeadersPrivate::removeAll(d, HeaderName(name));
+}
+
+/*!
+ Removes the header at index \a i. The index \a i must be valid
+ (see \l size()).
+
+ \sa removeAll(QHttpHeaders::WellKnownHeader),
+ removeAll(QAnyStringView), size()
+*/
+void QHttpHeaders::removeAt(qsizetype i)
+{
+ verify(i);
+ d.detach();
+ d->headers.removeAt(i);
+}
+
+/*!
+ Returns the value of the (first) header \a name, or \a defaultValue if it
+ doesn't exist.
+
+ \sa value(QHttpHeaders::WellKnownHeader, QByteArrayView)
+*/
+QByteArrayView QHttpHeaders::value(QAnyStringView name, QByteArrayView defaultValue) const noexcept
+{
+ if (isEmpty())
+ return defaultValue;
+
+ return d->value(HeaderName{name}, defaultValue);
+}
+
+/*!
+ \overload value(QAnyStringView, QByteArrayView)
+*/
+QByteArrayView QHttpHeaders::value(WellKnownHeader name, QByteArrayView defaultValue) const noexcept
+{
+ if (isEmpty())
+ return defaultValue;
+
+ return d->value(HeaderName{name}, defaultValue);
+}
+
+/*!
+ Returns the values of header \a name in a list. Returns an empty
+ list if header with \a name doesn't exist.
+
+ \sa values(QHttpHeaders::WellKnownHeader)
+*/
+QList<QByteArray> QHttpHeaders::values(QAnyStringView name) const
+{
+ QList<QByteArray> result;
+ if (isEmpty())
+ return result;
+
+ d->values(HeaderName{name}, result);
+ return result;
+}
+
+/*!
+ \overload values(QAnyStringView)
+*/
+QList<QByteArray> QHttpHeaders::values(WellKnownHeader name) const
+{
+ QList<QByteArray> result;
+ if (isEmpty())
+ return result;
+
+ d->values(HeaderName{name}, result);
+ return result;
+}
+
+/*!
+ Returns the header value at index \a i. The index \a i must be valid
+ (see \l size()).
+
+ \sa size(), value(), values(), combinedValue(), nameAt()
+*/
+QByteArrayView QHttpHeaders::valueAt(qsizetype i) const noexcept
+{
+ verify(i);
+ return d->headers.at(i).value;
+}
+
+/*!
+ Returns the header name at index \a i. The index \a i must be valid
+ (see \l size()).
+
+ Header names are case-insensitive, and the returned names are lower-cased.
+
+ \sa size(), valueAt()
+*/
+QLatin1StringView QHttpHeaders::nameAt(qsizetype i) const noexcept
+{
+ verify(i);
+ return QLatin1StringView{d->headers.at(i).name.asView()};
+}
+
+/*!
+ Returns the values of header \a name in a comma-combined string.
+ Returns a \c null QByteArray if the header with \a name doesn't
+ exist.
+
+ \note Accessing the value(s) of 'Set-Cookie' header this way may not work
+ as intended. It is a notable exception in the
+ \l {https://datatracker.ietf.org/doc/html/rfc9110#name-field-order}{HTTP RFC}
+ in that its values cannot be combined this way. Prefer \l values() instead.
+
+ \sa values(QAnyStringView)
+*/
+QByteArray QHttpHeaders::combinedValue(QAnyStringView name) const
+{
+ QByteArray result;
+ if (isEmpty())
+ return result;
+
+ d->combinedValue(HeaderName{name}, result);
+ return result;
+}
+
+/*!
+ \overload combinedValue(QAnyStringView)
+*/
+QByteArray QHttpHeaders::combinedValue(WellKnownHeader name) const
+{
+ QByteArray result;
+ if (isEmpty())
+ return result;
+
+ d->combinedValue(HeaderName{name}, result);
+ return result;
+}
+
+/*!
+ Returns the number of header entries.
+*/
+qsizetype QHttpHeaders::size() const noexcept
+{
+ if (!d)
+ return 0;
+ return d->headers.size();
+}
+
+/*!
+ Attempts to allocate memory for at least \a size header entries.
+
+ If you know in advance how how many header entries there will
+ be, you may call this function to prevent reallocations
+ and memory fragmentation.
+*/
+void QHttpHeaders::reserve(qsizetype size)
+{
+ d.detach();
+ d->headers.reserve(size);
+}
+
+/*!
+ \fn bool QHttpHeaders::isEmpty() const noexcept
+
+ Returns \c true if the headers have size 0; otherwise returns \c false.
+
+ \sa size()
+*/
+
+/*!
+ Returns a header name corresponding to the provided \a name as a view.
+*/
+QByteArrayView QHttpHeaders::wellKnownHeaderName(WellKnownHeader name) noexcept
+{
+ return headerNames[qToUnderlying(name)];
+}
+
+/*!
+ Returns the header entries as a list of (name, value) pairs.
+ Header names are case-insensitive, and the returned names are lower-cased.
+*/
+QList<std::pair<QByteArray, QByteArray>> QHttpHeaders::toListOfPairs() const
+{
+ QList<std::pair<QByteArray, QByteArray>> list;
+ if (isEmpty())
+ return list;
+ list.reserve(size());
+ for (const auto & h : std::as_const(d->headers))
+ list.append({h.name.asByteArray(), h.value});
+ return list;
+}
+
+/*!
+ Returns the header entries as a map from name to value(s).
+ Header names are case-insensitive, and the returned names are lower-cased.
+*/
+QMultiMap<QByteArray, QByteArray> QHttpHeaders::toMultiMap() const
+{
+ QMultiMap<QByteArray, QByteArray> map;
+ if (isEmpty())
+ return map;
+ for (const auto &h : std::as_const(d->headers))
+ map.insert(h.name.asByteArray(), h.value);
+ return map;
+}
+
+/*!
+ Returns the header entries as a hash from name to value(s).
+ Header names are case-insensitive, and the returned names are lower-cased.
+*/
+QMultiHash<QByteArray, QByteArray> QHttpHeaders::toMultiHash() const
+{
+ QMultiHash<QByteArray, QByteArray> hash;
+ if (isEmpty())
+ return hash;
+ hash.reserve(size());
+ for (const auto &h : std::as_const(d->headers))
+ hash.insert(h.name.asByteArray(), h.value);
+ return hash;
+}
+
+/*!
+ Clears all header entries.
+
+ \sa size()
+*/
+void QHttpHeaders::clear()
+{
+ if (isEmpty())
+ return;
+ d.detach();
+ d->headers.clear();
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qhttpheaders.h b/src/network/access/qhttpheaders.h
new file mode 100644
index 0000000000..97dc415e55
--- /dev/null
+++ b/src/network/access/qhttpheaders.h
@@ -0,0 +1,281 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QHTTPHEADERS_H
+#define QHTTPHEADERS_H
+
+#include <QtNetwork/qnetworkrequest.h>
+#include <QtCore/qshareddata.h>
+#include <QtCore/qcontainerfwd.h>
+
+QT_BEGIN_NAMESPACE
+
+class QDebug;
+
+class QHttpHeadersPrivate;
+QT_DECLARE_QESDP_SPECIALIZATION_DTOR_WITH_EXPORT(QHttpHeadersPrivate, Q_NETWORK_EXPORT)
+class QHttpHeaders
+{
+ Q_GADGET_EXPORT(Q_NETWORK_EXPORT)
+public:
+ enum class WellKnownHeader {
+ // IANA Permanent status:
+ AIM,
+ Accept,
+ AcceptAdditions,
+ AcceptCH,
+ AcceptDatetime,
+ AcceptEncoding,
+ AcceptFeatures,
+ AcceptLanguage,
+ AcceptPatch,
+ AcceptPost,
+ AcceptRanges,
+ AcceptSignature,
+ AccessControlAllowCredentials,
+ AccessControlAllowHeaders,
+ AccessControlAllowMethods,
+ AccessControlAllowOrigin,
+ AccessControlExposeHeaders,
+ AccessControlMaxAge,
+ AccessControlRequestHeaders,
+ AccessControlRequestMethod,
+ Age,
+ Allow,
+ ALPN,
+ AltSvc,
+ AltUsed,
+ Alternates,
+ ApplyToRedirectRef,
+ AuthenticationControl,
+ AuthenticationInfo,
+ Authorization,
+ CacheControl,
+ CacheStatus,
+ CalManagedID,
+ CalDAVTimezones,
+ CapsuleProtocol,
+ CDNCacheControl,
+ CDNLoop,
+ CertNotAfter,
+ CertNotBefore,
+ ClearSiteData,
+ ClientCert,
+ ClientCertChain,
+ Close,
+ Connection,
+ ContentDigest,
+ ContentDisposition,
+ ContentEncoding,
+ ContentID,
+ ContentLanguage,
+ ContentLength,
+ ContentLocation,
+ ContentRange,
+ ContentSecurityPolicy,
+ ContentSecurityPolicyReportOnly,
+ ContentType,
+ Cookie,
+ CrossOriginEmbedderPolicy,
+ CrossOriginEmbedderPolicyReportOnly,
+ CrossOriginOpenerPolicy,
+ CrossOriginOpenerPolicyReportOnly,
+ CrossOriginResourcePolicy,
+ DASL,
+ Date,
+ DAV,
+ DeltaBase,
+ Depth,
+ Destination,
+ DifferentialID,
+ DPoP,
+ DPoPNonce,
+ EarlyData,
+ ETag,
+ Expect,
+ ExpectCT,
+ Expires,
+ Forwarded,
+ From,
+ Hobareg,
+ Host,
+ If,
+ IfMatch,
+ IfModifiedSince,
+ IfNoneMatch,
+ IfRange,
+ IfScheduleTagMatch,
+ IfUnmodifiedSince,
+ IM,
+ IncludeReferredTokenBindingID,
+ KeepAlive,
+ Label,
+ LastEventID,
+ LastModified,
+ Link,
+ Location,
+ LockToken,
+ MaxForwards,
+ MementoDatetime,
+ Meter,
+ MIMEVersion,
+ Negotiate,
+ NEL,
+ ODataEntityId,
+ ODataIsolation,
+ ODataMaxVersion,
+ ODataVersion,
+ OptionalWWWAuthenticate,
+ OrderingType,
+ Origin,
+ OriginAgentCluster,
+ OSCORE,
+ OSLCCoreVersion,
+ Overwrite,
+ PingFrom,
+ PingTo,
+ Position,
+ Prefer,
+ PreferenceApplied,
+ Priority,
+ ProxyAuthenticate,
+ ProxyAuthenticationInfo,
+ ProxyAuthorization,
+ ProxyStatus,
+ PublicKeyPins,
+ PublicKeyPinsReportOnly,
+ Range,
+ RedirectRef,
+ Referer,
+ Refresh,
+ ReplayNonce,
+ ReprDigest,
+ RetryAfter,
+ ScheduleReply,
+ ScheduleTag,
+ SecPurpose,
+ SecTokenBinding,
+ SecWebSocketAccept,
+ SecWebSocketExtensions,
+ SecWebSocketKey,
+ SecWebSocketProtocol,
+ SecWebSocketVersion,
+ Server,
+ ServerTiming,
+ SetCookie,
+ Signature,
+ SignatureInput,
+ SLUG,
+ SoapAction,
+ StatusURI,
+ StrictTransportSecurity,
+ Sunset,
+ SurrogateCapability,
+ SurrogateControl,
+ TCN,
+ TE,
+ Timeout,
+ Topic,
+ Traceparent,
+ Tracestate,
+ Trailer,
+ TransferEncoding,
+ TTL,
+ Upgrade,
+ Urgency,
+ UserAgent,
+ VariantVary,
+ Vary,
+ Via,
+ WantContentDigest,
+ WantReprDigest,
+ WWWAuthenticate,
+ XContentTypeOptions,
+ XFrameOptions,
+ // IANA Deprecated status:
+ AcceptCharset,
+ CPEPInfo,
+ Pragma,
+ ProtocolInfo,
+ ProtocolQuery,
+ };
+ Q_ENUM(WellKnownHeader)
+
+ Q_NETWORK_EXPORT QHttpHeaders() noexcept;
+ Q_NETWORK_EXPORT ~QHttpHeaders();
+
+ Q_NETWORK_EXPORT QHttpHeaders(const QHttpHeaders &other);
+ QHttpHeaders(QHttpHeaders &&other) noexcept = default;
+ Q_NETWORK_EXPORT QHttpHeaders &operator=(const QHttpHeaders &other);
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QHttpHeaders)
+ void swap(QHttpHeaders &other) noexcept { d.swap(other.d); }
+
+ Q_NETWORK_EXPORT bool append(QAnyStringView name, QAnyStringView value);
+ Q_NETWORK_EXPORT bool append(WellKnownHeader name, QAnyStringView value);
+
+ Q_NETWORK_EXPORT bool insert(qsizetype i, QAnyStringView name, QAnyStringView value);
+ Q_NETWORK_EXPORT bool insert(qsizetype i, WellKnownHeader name, QAnyStringView value);
+
+ Q_NETWORK_EXPORT bool replace(qsizetype i, QAnyStringView name, QAnyStringView newValue);
+ Q_NETWORK_EXPORT bool replace(qsizetype i, WellKnownHeader name, QAnyStringView newValue);
+
+ Q_NETWORK_EXPORT bool replaceOrAppend(QAnyStringView name, QAnyStringView newValue);
+ Q_NETWORK_EXPORT bool replaceOrAppend(WellKnownHeader name, QAnyStringView newValue);
+
+ Q_NETWORK_EXPORT bool contains(QAnyStringView name) const;
+ Q_NETWORK_EXPORT bool contains(WellKnownHeader name) const;
+
+ Q_NETWORK_EXPORT void clear();
+ Q_NETWORK_EXPORT void removeAll(QAnyStringView name);
+ Q_NETWORK_EXPORT void removeAll(WellKnownHeader name);
+ Q_NETWORK_EXPORT void removeAt(qsizetype i);
+
+ Q_NETWORK_EXPORT QByteArrayView value(QAnyStringView name, QByteArrayView defaultValue = {}) const noexcept;
+ Q_NETWORK_EXPORT QByteArrayView value(WellKnownHeader name, QByteArrayView defaultValue = {}) const noexcept;
+
+ Q_NETWORK_EXPORT QList<QByteArray> values(QAnyStringView name) const;
+ Q_NETWORK_EXPORT QList<QByteArray> values(WellKnownHeader name) const;
+
+ Q_NETWORK_EXPORT QByteArrayView valueAt(qsizetype i) const noexcept;
+ Q_NETWORK_EXPORT QLatin1StringView nameAt(qsizetype i) const noexcept;
+
+ Q_NETWORK_EXPORT QByteArray combinedValue(QAnyStringView name) const;
+ Q_NETWORK_EXPORT QByteArray combinedValue(WellKnownHeader name) const;
+
+ Q_NETWORK_EXPORT qsizetype size() const noexcept;
+ Q_NETWORK_EXPORT void reserve(qsizetype size);
+ bool isEmpty() const noexcept { return size() == 0; }
+
+ Q_NETWORK_EXPORT static QByteArrayView wellKnownHeaderName(WellKnownHeader name) noexcept;
+
+ Q_NETWORK_EXPORT static QHttpHeaders
+ fromListOfPairs(const QList<std::pair<QByteArray, QByteArray>> &headers);
+ Q_NETWORK_EXPORT static QHttpHeaders
+ fromMultiMap(const QMultiMap<QByteArray, QByteArray> &headers);
+ Q_NETWORK_EXPORT static QHttpHeaders
+ fromMultiHash(const QMultiHash<QByteArray, QByteArray> &headers);
+
+ Q_NETWORK_EXPORT QList<std::pair<QByteArray, QByteArray>> toListOfPairs() const;
+ Q_NETWORK_EXPORT QMultiMap<QByteArray, QByteArray> toMultiMap() const;
+ Q_NETWORK_EXPORT QMultiHash<QByteArray, QByteArray> toMultiHash() const;
+
+private:
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QHttpHeaders &headers);
+#endif
+ Q_ALWAYS_INLINE void verify([[maybe_unused]] qsizetype pos = 0,
+ [[maybe_unused]] qsizetype n = 1) const
+ {
+ Q_ASSERT(pos >= 0);
+ Q_ASSERT(pos <= size());
+ Q_ASSERT(n >= 0);
+ Q_ASSERT(n <= size() - pos);
+ }
+ QExplicitlySharedDataPointer<QHttpHeadersPrivate> d;
+};
+
+Q_DECLARE_SHARED(QHttpHeaders)
+
+QT_END_NAMESPACE
+
+#endif // QHTTPHEADERS_H
diff --git a/src/network/access/qhttpmultipart.cpp b/src/network/access/qhttpmultipart.cpp
index 27e3ae2201..6d81f1b957 100644
--- a/src/network/access/qhttpmultipart.cpp
+++ b/src/network/access/qhttpmultipart.cpp
@@ -317,11 +317,11 @@ void QHttpMultiPart::setBoundary(const QByteArray &boundary)
qint64 QHttpPartPrivate::bytesAvailable() const
{
checkHeaderCreated();
- qint64 bytesAvailable = header.length();
+ qint64 bytesAvailable = header.size();
if (bodyDevice) {
bytesAvailable += bodyDevice->bytesAvailable() - readPointer;
} else {
- bytesAvailable += body.length() - readPointer;
+ bytesAvailable += body.size() - readPointer;
}
// the device might have closed etc., so make sure we do not return a negative value
return qMax(bytesAvailable, (qint64) 0);
@@ -331,7 +331,7 @@ qint64 QHttpPartPrivate::readData(char *data, qint64 maxSize)
{
checkHeaderCreated();
qint64 bytesRead = 0;
- qint64 headerDataCount = header.length();
+ qint64 headerDataCount = header.size();
// read header if it has not been read yet
if (readPointer < headerDataCount) {
@@ -349,7 +349,7 @@ qint64 QHttpPartPrivate::readData(char *data, qint64 maxSize)
bytesRead += dataBytesRead;
readPointer += dataBytesRead;
} else {
- qint64 contentBytesRead = qMin(body.length() - readPointer + headerDataCount, maxSize - bytesRead);
+ qint64 contentBytesRead = qMin(body.size() - readPointer + headerDataCount, maxSize - bytesRead);
const char *contentData = body.constData();
// if this method is called several times, we need to find the
// right offset in the content ourselves:
@@ -364,11 +364,11 @@ qint64 QHttpPartPrivate::readData(char *data, qint64 maxSize)
qint64 QHttpPartPrivate::size() const
{
checkHeaderCreated();
- qint64 size = header.length();
+ qint64 size = header.size();
if (bodyDevice) {
size += bodyDevice->size();
} else {
- size += body.length();
+ size += body.size();
}
return size;
}
@@ -386,10 +386,9 @@ void QHttpPartPrivate::checkHeaderCreated() const
{
if (!headerCreated) {
// copied from QHttpNetworkRequestPrivate::header() and adapted
- QList<QPair<QByteArray, QByteArray> > fields = allRawHeaders();
- QList<QPair<QByteArray, QByteArray> >::const_iterator it = fields.constBegin();
- for (; it != fields.constEnd(); ++it)
- header += it->first + ": " + it->second + "\r\n";
+ const auto fields = allRawHeaders();
+ for (const auto &[name, value] : fields)
+ header += name + ": " + value + "\r\n";
header += "\r\n";
headerCreated = true;
}
@@ -404,7 +403,7 @@ QHttpMultiPartPrivate::QHttpMultiPartPrivate() : contentType(QHttpMultiPart::Mix
+ QByteArray::fromRawData(reinterpret_cast<char *>(random), sizeof(random)).toBase64();
// boundary must not be longer than 70 characters, see RFC 2046, section 5.1.1
- Q_ASSERT(boundary.length() <= 70);
+ Q_ASSERT(boundary.size() <= 70);
}
qint64 QHttpMultiPartIODevice::size() const
@@ -413,8 +412,8 @@ qint64 QHttpMultiPartIODevice::size() const
// including boundary (needed later in readData)
if (deviceSize == -1) {
qint64 currentSize = 0;
- qint64 boundaryCount = multiPart->boundary.length();
- for (int a = 0; a < multiPart->parts.count(); a++) {
+ qint64 boundaryCount = multiPart->boundary.size();
+ for (int a = 0; a < multiPart->parts.size(); a++) {
partOffsets.append(currentSize);
// 4 additional bytes for the "--" before and the "\r\n" after the boundary,
// and 2 bytes for the "\r\n" after the content
@@ -428,7 +427,7 @@ qint64 QHttpMultiPartIODevice::size() const
bool QHttpMultiPartIODevice::isSequential() const
{
- for (int a = 0; a < multiPart->parts.count(); a++) {
+ for (int a = 0; a < multiPart->parts.size(); a++) {
QIODevice *device = multiPart->parts.at(a).d->bodyDevice;
// we are sequential if any of the bodyDevices of our parts are sequential;
// when reading from a byte array, we are not sequential
@@ -442,7 +441,7 @@ bool QHttpMultiPartIODevice::reset()
{
// Reset QIODevice's data
QIODevice::reset();
- for (int a = 0; a < multiPart->parts.count(); a++)
+ for (int a = 0; a < multiPart->parts.size(); a++)
if (!multiPart->parts[a].d->reset())
return false;
readPointer = 0;
@@ -453,17 +452,17 @@ qint64 QHttpMultiPartIODevice::readData(char *data, qint64 maxSize)
qint64 bytesRead = 0, index = 0;
// skip the parts we have already read
- while (index < multiPart->parts.count() &&
+ while (index < multiPart->parts.size() &&
readPointer >= partOffsets.at(index) + multiPart->parts.at(index).d->size()
- + multiPart->boundary.length() + 6) // 6 == 2 boundary dashes, \r\n after boundary, \r\n after multipart
+ + multiPart->boundary.size() + 6) // 6 == 2 boundary dashes, \r\n after boundary, \r\n after multipart
index++;
// read the data
- while (bytesRead < maxSize && index < multiPart->parts.count()) {
+ while (bytesRead < maxSize && index < multiPart->parts.size()) {
// check whether we need to read the boundary of the current part
QByteArray boundaryData = "--" + multiPart->boundary + "\r\n";
- qint64 boundaryCount = boundaryData.length();
+ qint64 boundaryCount = boundaryData.size();
qint64 partIndex = readPointer - partOffsets.at(index);
if (partIndex < boundaryCount) {
qint64 boundaryBytesRead = qMin(boundaryCount - partIndex, maxSize - bytesRead);
@@ -494,10 +493,10 @@ qint64 QHttpMultiPartIODevice::readData(char *data, qint64 maxSize)
}
}
// check whether we need to return the final boundary
- if (bytesRead < maxSize && index == multiPart->parts.count()) {
+ if (bytesRead < maxSize && index == multiPart->parts.size()) {
QByteArray finalBoundary = "--" + multiPart->boundary + "--\r\n";
- qint64 boundaryIndex = readPointer + finalBoundary.length() - size();
- qint64 lastBoundaryBytesRead = qMin(finalBoundary.length() - boundaryIndex, maxSize - bytesRead);
+ qint64 boundaryIndex = readPointer + finalBoundary.size() - size();
+ qint64 lastBoundaryBytesRead = qMin(finalBoundary.size() - boundaryIndex, maxSize - bytesRead);
memcpy(data + bytesRead, finalBoundary.constData() + boundaryIndex, lastBoundaryBytesRead);
bytesRead += lastBoundaryBytesRead;
readPointer += lastBoundaryBytesRead;
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index 3b08a84a03..1897380e0e 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -17,6 +17,8 @@
#include <qbuffer.h>
#include <qpair.h>
#include <qdebug.h>
+#include <qspan.h>
+#include <qvarlengtharray.h>
#ifndef QT_NO_SSL
# include <private/qsslsocket_p.h>
@@ -32,51 +34,39 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-const int QHttpNetworkConnectionPrivate::defaultHttpChannelCount = 6;
-
// The pipeline length. So there will be 4 requests in flight.
const int QHttpNetworkConnectionPrivate::defaultPipelineLength = 3;
// Only re-fill the pipeline if there's defaultRePipelineLength slots free in the pipeline.
// This means that there are 2 requests in flight and 2 slots free that will be re-filled.
const int QHttpNetworkConnectionPrivate::defaultRePipelineLength = 2;
-
-QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &hostName,
- quint16 port, bool encrypt,
- QHttpNetworkConnection::ConnectionType type)
-: state(RunningState),
- networkLayerState(Unknown),
- hostName(hostName), port(port), encrypt(encrypt), delayIpv4(true)
- , activeChannelCount(type == QHttpNetworkConnection::ConnectionTypeHTTP2
- || type == QHttpNetworkConnection::ConnectionTypeHTTP2Direct
- ? 1 : defaultHttpChannelCount)
- , channelCount(defaultHttpChannelCount)
-#ifndef QT_NO_NETWORKPROXY
- , networkProxy(QNetworkProxy::NoProxy)
-#endif
- , preConnectRequests(0)
- , connectionType(type)
+static int getPreferredActiveChannelCount(QHttpNetworkConnection::ConnectionType type,
+ int defaultValue)
{
- // We allocate all 6 channels even if it's HTTP/2 enabled connection:
- // in case the protocol negotiation via NPN/ALPN fails, we will have
- // normally working HTTP/1.1.
- Q_ASSERT(channelCount >= activeChannelCount);
- channels = new QHttpNetworkConnectionChannel[channelCount];
+ return (type == QHttpNetworkConnection::ConnectionTypeHTTP2
+ || type == QHttpNetworkConnection::ConnectionTypeHTTP2Direct)
+ ? 1
+ : defaultValue;
}
-QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(quint16 connectionCount, const QString &hostName,
- quint16 port, bool encrypt,
- QHttpNetworkConnection::ConnectionType type)
-: state(RunningState), networkLayerState(Unknown),
- hostName(hostName), port(port), encrypt(encrypt), delayIpv4(true),
- activeChannelCount(connectionCount), channelCount(connectionCount)
+QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(
+ quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt,
+ QHttpNetworkConnection::ConnectionType type)
+ : hostName(hostName),
+ port(port),
+ encrypt(encrypt),
+ activeChannelCount(getPreferredActiveChannelCount(type, connectionCount)),
+ channelCount(connectionCount),
+ channels(new QHttpNetworkConnectionChannel[channelCount]),
#ifndef QT_NO_NETWORKPROXY
- , networkProxy(QNetworkProxy::NoProxy)
+ networkProxy(QNetworkProxy::NoProxy),
#endif
- , preConnectRequests(0)
- , connectionType(type)
+ connectionType(type)
{
- channels = new QHttpNetworkConnectionChannel[channelCount];
+ // We allocate all 6 channels even if it's an HTTP/2-enabled
+ // connection: in case the protocol negotiation via NPN/ALPN fails,
+ // we will have normally working HTTP/1.1.
+ Q_ASSERT(channelCount >= activeChannelCount);
}
@@ -145,7 +135,7 @@ void QHttpNetworkConnectionPrivate::resumeConnection()
QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
}
-int QHttpNetworkConnectionPrivate::indexOf(QAbstractSocket *socket) const
+int QHttpNetworkConnectionPrivate::indexOf(QIODevice *socket) const
{
for (int i = 0; i < activeChannelCount; ++i)
if (channels[i].socket == socket)
@@ -159,7 +149,7 @@ int QHttpNetworkConnectionPrivate::indexOf(QAbstractSocket *socket) const
// emitted. This function will check the status of the connection channels if we
// have not decided the networkLayerState and will return true if the channel error
// should be emitted by the channel.
-bool QHttpNetworkConnectionPrivate::shouldEmitChannelError(QAbstractSocket *socket)
+bool QHttpNetworkConnectionPrivate::shouldEmitChannelError(QIODevice *socket)
{
Q_Q(QHttpNetworkConnection);
@@ -217,6 +207,17 @@ qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailableNextBlock(const
return reply.d_func()->responseData.sizeNextBlock();
}
+static QByteArray makeAcceptLanguage()
+{
+ QString systemLocale = QLocale::system().name();
+ if (systemLocale == "C"_L1)
+ return "en,*"_ba;
+ systemLocale.replace('_'_L1, '-'_L1);
+ if (systemLocale.startsWith("en-"_L1))
+ return (systemLocale + ",*"_L1).toLatin1();
+ return (systemLocale + ",en,*"_L1).toLatin1();
+}
+
void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
{
QHttpNetworkRequest &request = messagePair.first;
@@ -267,8 +268,8 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
value = request.headerField("accept-encoding");
if (value.isEmpty()) {
#ifndef QT_NO_COMPRESS
- const QByteArrayList &acceptedEncoding = QDecompressHelper::acceptedEncoding();
- request.setHeaderField("Accept-Encoding", acceptedEncoding.join(", "));
+ const static QByteArray acceptedEncoding = QDecompressHelper::acceptedEncoding().join(", ");
+ request.setHeaderField("Accept-Encoding", acceptedEncoding);
request.d->autoDecompress = true;
#else
// if zlib is not available set this to false always
@@ -281,17 +282,8 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
// not with us, but we work around this by setting
// one always.
value = request.headerField("accept-language");
- if (value.isEmpty()) {
- QString systemLocale = QLocale::system().name().replace(QChar::fromLatin1('_'),QChar::fromLatin1('-'));
- QString acceptLanguage;
- if (systemLocale == "C"_L1)
- acceptLanguage = QString::fromLatin1("en,*");
- else if (systemLocale.startsWith("en-"_L1))
- acceptLanguage = systemLocale + ",*"_L1;
- else
- acceptLanguage = systemLocale + ",en,*"_L1;
- request.setHeaderField("Accept-Language", std::move(acceptLanguage).toLatin1());
- }
+ if (value.isEmpty())
+ request.setHeaderField("Accept-Language", makeAcceptLanguage());
// set the User Agent
value = request.headerField("user-agent");
@@ -304,7 +296,7 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
QByteArray host;
if (add.setAddress(hostName)) {
if (add.protocol() == QAbstractSocket::IPv6Protocol)
- host = '[' + hostName.toLatin1() + ']'; //format the ipv6 in the standard way
+ host = (u'[' + hostName + u']').toLatin1(); //format the ipv6 in the standard way
else
host = hostName.toLatin1();
@@ -400,7 +392,7 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket
resend = false;
//create the response header to be used with QAuthenticatorPrivate.
- QList<QPair<QByteArray, QByteArray> > fields = reply->header();
+ const auto headers = reply->header();
// Check that any of the proposed authenticate methods are supported
const QByteArray header = isProxy ? "proxy-authenticate" : "www-authenticate";
@@ -416,14 +408,15 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket
if (auth->isNull())
auth->detach();
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*auth);
- priv->parseHttpResponse(fields, isProxy, reply->url().host());
+ priv->parseHttpResponse(headers, isProxy, reply->url().host());
// Update method in case it changed
if (priv->method == QAuthenticatorPrivate::None)
return false;
if (priv->phase == QAuthenticatorPrivate::Done ||
(priv->phase == QAuthenticatorPrivate::Start
- && priv->method == QAuthenticatorPrivate::Ntlm)) {
+ && (priv->method == QAuthenticatorPrivate::Ntlm
+ || priv->method == QAuthenticatorPrivate::Negotiate))) {
if (priv->phase == QAuthenticatorPrivate::Start)
priv->phase = QAuthenticatorPrivate::Phase1;
@@ -511,12 +504,9 @@ QHttpNetworkConnectionPrivate::parseRedirectResponse(QHttpNetworkReply *reply)
return {{}, QNetworkReply::NoError};
QUrl redirectUrl;
- const QList<QPair<QByteArray, QByteArray> > fields = reply->header();
- for (const QNetworkReply::RawHeaderPair &header : fields) {
- if (header.first.compare("location", Qt::CaseInsensitive) == 0) {
- redirectUrl = QUrl::fromEncoded(header.second);
- break;
- }
+ const QHttpHeaders fields = reply->header();
+ if (const auto h = fields.values(QHttpHeaders::WellKnownHeader::Location); !h.empty()) {
+ redirectUrl = QUrl::fromEncoded(h.first());
}
// If the location url is invalid/empty, we return ProtocolUnknownError
@@ -560,7 +550,7 @@ QHttpNetworkConnectionPrivate::parseRedirectResponse(QHttpNetworkReply *reply)
return {std::move(redirectUrl), QNetworkReply::NoError};
}
-void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request)
+void QHttpNetworkConnectionPrivate::createAuthorization(QIODevice *socket, QHttpNetworkRequest &request)
{
Q_ASSERT(socket);
@@ -696,7 +686,7 @@ void QHttpNetworkConnectionPrivate::requeueRequest(const HttpMessagePair &pair)
QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
}
-bool QHttpNetworkConnectionPrivate::dequeueRequest(QAbstractSocket *socket)
+bool QHttpNetworkConnectionPrivate::dequeueRequest(QIODevice *socket)
{
int i = 0;
if (socket)
@@ -762,7 +752,7 @@ void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket)
if (channels[i].reply == nullptr)
return;
- if (! (defaultPipelineLength - channels[i].alreadyPipelinedRequests.length() >= defaultRePipelineLength)) {
+ if (! (defaultPipelineLength - channels[i].alreadyPipelinedRequests.size() >= defaultRePipelineLength)) {
return;
}
@@ -803,28 +793,28 @@ void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket)
int lengthBefore;
while (!highPriorityQueue.isEmpty()) {
- lengthBefore = channels[i].alreadyPipelinedRequests.length();
+ lengthBefore = channels[i].alreadyPipelinedRequests.size();
fillPipeline(highPriorityQueue, channels[i]);
- if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) {
+ if (channels[i].alreadyPipelinedRequests.size() >= defaultPipelineLength) {
channels[i].pipelineFlush();
return;
}
- if (lengthBefore == channels[i].alreadyPipelinedRequests.length())
+ if (lengthBefore == channels[i].alreadyPipelinedRequests.size())
break; // did not process anything, now do the low prio queue
}
while (!lowPriorityQueue.isEmpty()) {
- lengthBefore = channels[i].alreadyPipelinedRequests.length();
+ lengthBefore = channels[i].alreadyPipelinedRequests.size();
fillPipeline(lowPriorityQueue, channels[i]);
- if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) {
+ if (channels[i].alreadyPipelinedRequests.size() >= defaultPipelineLength) {
channels[i].pipelineFlush();
return;
}
- if (lengthBefore == channels[i].alreadyPipelinedRequests.length())
+ if (lengthBefore == channels[i].alreadyPipelinedRequests.size())
break; // did not process anything
}
@@ -838,7 +828,7 @@ bool QHttpNetworkConnectionPrivate::fillPipeline(QList<HttpMessagePair> &queue,
if (queue.isEmpty())
return true;
- for (int i = queue.count() - 1; i >= 0; --i) {
+ for (int i = queue.size() - 1; i >= 0; --i) {
HttpMessagePair messagePair = queue.at(i);
const QHttpNetworkRequest &request = messagePair.first;
@@ -906,7 +896,7 @@ QString QHttpNetworkConnectionPrivate::errorDetail(QNetworkReply::NetworkError e
case QNetworkReply::SslHandshakeFailedError:
errorString = QCoreApplication::translate("QHttp", "SSL handshake failed");
if (socket)
- errorString += QStringLiteral(": ") + socket->errorString();
+ errorString += ": "_L1 + socket->errorString();
break;
case QNetworkReply::TooManyRedirectsError:
errorString = QCoreApplication::translate("QHttp", "Too many redirects");
@@ -960,7 +950,7 @@ void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply)
}
// is the reply inside the pipeline of this channel already?
- for (int j = 0; j < channels[i].alreadyPipelinedRequests.length(); j++) {
+ for (int j = 0; j < channels[i].alreadyPipelinedRequests.size(); j++) {
if (channels[i].alreadyPipelinedRequests.at(j).second == reply) {
// Remove that HttpMessagePair
channels[i].alreadyPipelinedRequests.removeAt(j);
@@ -978,23 +968,22 @@ void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply)
return;
}
}
-#ifndef QT_NO_SSL
// is the reply inside the H2 pipeline of this channel already?
- QMultiMap<int, HttpMessagePair>::iterator it = channels[i].h2RequestsToSend.begin();
- QMultiMap<int, HttpMessagePair>::iterator end = channels[i].h2RequestsToSend.end();
- for (; it != end; ++it) {
- if (it.value().second == reply) {
- channels[i].h2RequestsToSend.remove(it.key());
-
- QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
- return;
- }
+ const auto foundReply = [reply](const HttpMessagePair &pair) {
+ return pair.second == reply;
+ };
+ auto &seq = channels[i].h2RequestsToSend;
+ const auto end = seq.cend();
+ auto it = std::find_if(seq.cbegin(), end, foundReply);
+ if (it != end) {
+ seq.erase(it);
+ QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
+ return;
}
-#endif
}
// remove from the high priority queue
if (!highPriorityQueue.isEmpty()) {
- for (int j = highPriorityQueue.count() - 1; j >= 0; --j) {
+ for (int j = highPriorityQueue.size() - 1; j >= 0; --j) {
HttpMessagePair messagePair = highPriorityQueue.at(j);
if (messagePair.second == reply) {
highPriorityQueue.removeAt(j);
@@ -1005,7 +994,7 @@ void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply)
}
// remove from the low priority queue
if (!lowPriorityQueue.isEmpty()) {
- for (int j = lowPriorityQueue.count() - 1; j >= 0; --j) {
+ for (int j = lowPriorityQueue.size() - 1; j >= 0; --j) {
HttpMessagePair messagePair = lowPriorityQueue.at(j);
if (messagePair.second == reply) {
lowPriorityQueue.removeAt(j);
@@ -1034,6 +1023,11 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
//resend the necessary ones.
for (int i = 0; i < activeChannelCount; ++i) {
if (channels[i].resendCurrent && (channels[i].state != QHttpNetworkConnectionChannel::ClosingState)) {
+ if (!channels[i].socket
+ || channels[i].socket->state() == QAbstractSocket::UnconnectedState) {
+ if (!channels[i].ensureConnection())
+ continue;
+ }
channels[i].resendCurrent = false;
// if this is not possible, error will be emitted and connection terminated
@@ -1108,7 +1102,7 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
// If there is not already any connected channels we need to connect a new one.
// We do not pair the channel with the request until we know if it is
// connected or not. This is to reuse connected channels before we connect new once.
- int queuedRequests = highPriorityQueue.count() + lowPriorityQueue.count();
+ int queuedRequests = highPriorityQueue.size() + lowPriorityQueue.size();
// in case we have in-flight preconnect requests and normal requests,
// we only need one socket for each (preconnect, normal request) pair
@@ -1121,7 +1115,7 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
if (neededOpenChannels <= 0)
return;
- QQueue<int> channelsToConnect;
+ QVarLengthArray<int> channelsToConnect;
// use previously used channels first
for (int i = 0; i < activeChannelCount && neededOpenChannels > 0; ++i) {
@@ -1137,7 +1131,7 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
if (!channels[i].reply && !channels[i].isSocketBusy()
&& (channels[i].socket->state() == QAbstractSocket::UnconnectedState)) {
- channelsToConnect.enqueue(i);
+ channelsToConnect.push_back(i);
neededOpenChannels--;
}
}
@@ -1147,12 +1141,14 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
if (channels[i].socket)
continue;
- channelsToConnect.enqueue(i);
+ channelsToConnect.push_back(i);
neededOpenChannels--;
}
- while (!channelsToConnect.isEmpty()) {
- const int channel = channelsToConnect.dequeue();
+ auto channelToConnectSpan = QSpan{channelsToConnect};
+ while (!channelToConnectSpan.isEmpty()) {
+ const int channel = channelToConnectSpan.front();
+ channelToConnectSpan = channelToConnectSpan.sliced(1);
if (networkLayerState == IPv4)
channels[channel].networkLayerPreference = QAbstractSocket::IPv4Protocol;
@@ -1256,16 +1252,24 @@ void QHttpNetworkConnectionPrivate::_q_hostLookupFinished(const QHostInfo &info)
networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
} else {
+ auto lookupError = QNetworkReply::HostNotFoundError;
+#ifndef QT_NO_NETWORKPROXY
+ // if the proxy can lookup hostnames, all hostname lookups except for the lookup of the
+ // proxy hostname are delegated to the proxy.
+ auto proxyCapabilities = networkProxy.capabilities() | channels[0].proxy.capabilities();
+ if (proxyCapabilities & QNetworkProxy::HostNameLookupCapability)
+ lookupError = QNetworkReply::ProxyNotFoundError;
+#endif
if (dequeueRequest(channels[0].socket)) {
- emitReplyError(channels[0].socket, channels[0].reply, QNetworkReply::HostNotFoundError);
+ emitReplyError(channels[0].socket, channels[0].reply, lookupError);
networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
} else if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
|| connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
- for (const HttpMessagePair &h2Pair : qAsConst(channels[0].h2RequestsToSend)) {
+ for (const HttpMessagePair &h2Pair : std::as_const(channels[0].h2RequestsToSend)) {
// emit error for all replies
QHttpNetworkReply *currentReply = h2Pair.second;
Q_ASSERT(currentReply);
- emitReplyError(channels[0].socket, currentReply, QNetworkReply::HostNotFoundError);
+ emitReplyError(channels[0].socket, currentReply, lookupError);
}
} else {
// We can end up here if a request has been aborted or otherwise failed (e.g. timeout)
@@ -1324,18 +1328,6 @@ void QHttpNetworkConnectionPrivate::_q_connectDelayedChannel()
channels[1].ensureConnection();
}
-QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt,
- QHttpNetworkConnection::ConnectionType connectionType, QObject *parent)
- : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt , connectionType)), parent)
-{
- Q_D(QHttpNetworkConnection);
- d->init();
- if (QNetworkConnectionMonitor::isEnabled()) {
- connect(&d->connectionMonitor, &QNetworkConnectionMonitor::reachabilityChanged,
- this, &QHttpNetworkConnection::onlineStateChanged, Qt::QueuedConnection);
- }
-}
-
QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName,
quint16 port, bool encrypt, QObject *parent,
QHttpNetworkConnection::ConnectionType connectionType)
@@ -1423,9 +1415,9 @@ QNetworkProxy QHttpNetworkConnection::transparentProxy() const
}
#endif
-QHttpNetworkConnection::ConnectionType QHttpNetworkConnection::connectionType()
+QHttpNetworkConnection::ConnectionType QHttpNetworkConnection::connectionType() const
{
- Q_D(QHttpNetworkConnection);
+ Q_D(const QHttpNetworkConnection);
return d->connectionType;
}
@@ -1460,9 +1452,9 @@ void QHttpNetworkConnection::setSslConfiguration(const QSslConfiguration &config
d->channels[i].setSslConfiguration(config);
}
-std::shared_ptr<QSslContext> QHttpNetworkConnection::sslContext()
+std::shared_ptr<QSslContext> QHttpNetworkConnection::sslContext() const
{
- Q_D(QHttpNetworkConnection);
+ Q_D(const QHttpNetworkConnection);
return d->sslContext;
}
@@ -1558,12 +1550,12 @@ void QHttpNetworkConnectionPrivate::emitProxyAuthenticationRequired(const QHttpN
pauseConnection();
QHttpNetworkReply *reply;
if ((connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
- && (chan->switchedToHttp2 || chan->h2RequestsToSend.count() > 0))
+ && (chan->switchedToHttp2 || chan->h2RequestsToSend.size() > 0))
|| connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
// we choose the reply to emit the proxyAuth signal from somewhat arbitrarily,
// but that does not matter because the signal will ultimately be emitted
// by the QNetworkAccessManager.
- Q_ASSERT(chan->h2RequestsToSend.count() > 0);
+ Q_ASSERT(chan->h2RequestsToSend.size() > 0);
reply = chan->h2RequestsToSend.cbegin().value().second;
} else { // HTTP
reply = chan->reply;
diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h
index c3556f6eff..36234a24ba 100644
--- a/src/network/access/qhttpnetworkconnection_p.h
+++ b/src/network/access/qhttpnetworkconnection_p.h
@@ -52,7 +52,7 @@ class QSslContext;
#endif // !QT_NO_SSL
class QHttpNetworkConnectionPrivate;
-class Q_AUTOTEST_EXPORT QHttpNetworkConnection : public QObject
+class Q_NETWORK_EXPORT QHttpNetworkConnection : public QObject
{
Q_OBJECT
public:
@@ -63,9 +63,6 @@ public:
ConnectionTypeHTTP2Direct
};
- explicit QHttpNetworkConnection(const QString &hostName, quint16 port = 80, bool encrypt = false,
- ConnectionType connectionType = ConnectionTypeHTTP,
- QObject *parent = nullptr);
QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port = 80,
bool encrypt = false, QObject *parent = nullptr,
ConnectionType connectionType = ConnectionTypeHTTP);
@@ -92,7 +89,7 @@ public:
QHttpNetworkConnectionChannel *channels() const;
- ConnectionType connectionType();
+ ConnectionType connectionType() const;
void setConnectionType(ConnectionType type);
QHttp2Configuration http2Parameters() const;
@@ -102,7 +99,7 @@ public:
void setSslConfiguration(const QSslConfiguration &config);
void ignoreSslErrors(int channel = -1);
void ignoreSslErrors(const QList<QSslError> &errors, int channel = -1);
- std::shared_ptr<QSslContext> sslContext();
+ std::shared_ptr<QSslContext> sslContext() const;
void setSslContext(std::shared_ptr<QSslContext> context);
#endif
@@ -137,8 +134,10 @@ typedef QPair<QHttpNetworkRequest, QHttpNetworkReply*> HttpMessagePair;
class QHttpNetworkConnectionPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QHttpNetworkConnection)
+ Q_DISABLE_COPY_MOVE(QHttpNetworkConnectionPrivate)
public:
- static const int defaultHttpChannelCount;
+ // Note: Only used from auto tests, normal usage is via QHttp1Configuration
+ static constexpr int defaultHttpChannelCount = 6;
static const int defaultPipelineLength;
static const int defaultRePipelineLength;
@@ -155,26 +154,24 @@ public:
IPv4or6
};
- QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt,
- QHttpNetworkConnection::ConnectionType type);
- QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt,
- QHttpNetworkConnection::ConnectionType type);
+ QHttpNetworkConnectionPrivate(quint16 connectionCount, const QString &hostName, quint16 port,
+ bool encrypt, QHttpNetworkConnection::ConnectionType type);
~QHttpNetworkConnectionPrivate();
void init();
void pauseConnection();
void resumeConnection();
- ConnectionState state;
- NetworkLayerPreferenceState networkLayerState;
+ ConnectionState state = RunningState;
+ NetworkLayerPreferenceState networkLayerState = Unknown;
enum { ChunkSize = 4096 };
- int indexOf(QAbstractSocket *socket) const;
+ int indexOf(QIODevice *socket) const;
QHttpNetworkReply *queueRequest(const QHttpNetworkRequest &request);
void requeueRequest(const HttpMessagePair &pair); // e.g. after pipeline broke
void fillHttp2Queue();
- bool dequeueRequest(QAbstractSocket *socket);
+ bool dequeueRequest(QIODevice *socket);
void prepareRequest(HttpMessagePair &request);
void updateChannel(int i, const HttpMessagePair &messagePair);
QHttpNetworkRequest predictNextRequest() const;
@@ -198,7 +195,7 @@ public:
void _q_hostLookupFinished(const QHostInfo &info);
void _q_connectDelayedChannel();
- void createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request);
+ void createAuthorization(QIODevice *socket, QHttpNetworkRequest &request);
QString errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket,
const QString &extraDetail = QString());
@@ -208,15 +205,15 @@ public:
QString hostName;
quint16 port;
bool encrypt;
- bool delayIpv4;
+ bool delayIpv4 = true;
// Number of channels we are trying to use at the moment:
int activeChannelCount;
// The total number of channels we reserved:
const int channelCount;
QTimer delayedConnectionTimer;
- QHttpNetworkConnectionChannel *channels; // parallel connections to the server
- bool shouldEmitChannelError(QAbstractSocket *socket);
+ QHttpNetworkConnectionChannel * const channels; // parallel connections to the server
+ bool shouldEmitChannelError(QIODevice *socket);
qint64 uncompressedBytesAvailable(const QHttpNetworkReply &reply) const;
qint64 uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const;
@@ -228,7 +225,7 @@ public:
QUrl redirectUrl;
QNetworkReply::NetworkError errorCode;
};
- ParseRedirectResult parseRedirectResponse(QHttpNetworkReply *reply);
+ static ParseRedirectResult parseRedirectResponse(QHttpNetworkReply *reply);
// Used by the HTTP1 code-path
QUrl parseRedirectResponse(QAbstractSocket *socket, QHttpNetworkReply *reply);
@@ -241,7 +238,7 @@ public:
QList<HttpMessagePair> highPriorityQueue;
QList<HttpMessagePair> lowPriorityQueue;
- int preConnectRequests;
+ int preConnectRequests = 0;
QHttpNetworkConnection::ConnectionType connectionType;
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index ed581eb7d8..6766989690 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -7,7 +7,6 @@
#include "qhttp2configuration.h"
#include "private/qnoncontiguousbytedevice_p.h"
-#include <qpair.h>
#include <qdebug.h>
#include <private/qhttp2protocolhandler_p.h>
@@ -23,6 +22,7 @@
#include "private/qnetconmonitor_p.h"
#include <memory>
+#include <utility>
QT_BEGIN_NAMESPACE
@@ -325,8 +325,8 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
QHttpNetworkReply *potentialReply = connection->d_func()->predictNextRequestsReply();
if (potentialReply) {
QMetaObject::invokeMethod(potentialReply, "socketStartedConnecting", Qt::QueuedConnection);
- } else if (h2RequestsToSend.count() > 0) {
- QMetaObject::invokeMethod(h2RequestsToSend.values().at(0).second, "socketStartedConnecting", Qt::QueuedConnection);
+ } else if (!h2RequestsToSend.isEmpty()) {
+ QMetaObject::invokeMethod(std::as_const(h2RequestsToSend).first().second, "socketStartedConnecting", Qt::QueuedConnection);
}
#ifndef QT_NO_NETWORKPROXY
@@ -344,8 +344,8 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
if (connection->connectionType()
== QHttpNetworkConnection::ConnectionTypeHTTP2Direct
|| (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
- && h2RequestsToSend.count() > 0)) {
- value = h2RequestsToSend.first().first.headerField("user-agent");
+ && !h2RequestsToSend.isEmpty())) {
+ value = std::as_const(h2RequestsToSend).first().first.headerField("user-agent");
} else {
value = connection->d_func()->predictNextRequest().headerField("user-agent");
}
@@ -573,7 +573,7 @@ void QHttpNetworkConnectionChannel::detectPipeliningSupport()
// called when the connection broke and we need to queue some pipelined requests again
void QHttpNetworkConnectionChannel::requeueCurrentlyPipelinedRequests()
{
- for (int i = 0; i < alreadyPipelinedRequests.length(); i++)
+ for (int i = 0; i < alreadyPipelinedRequests.size(); i++)
connection->d_func()->requeueRequest(alreadyPipelinedRequests.at(i));
alreadyPipelinedRequests.clear();
@@ -829,7 +829,7 @@ void QHttpNetworkConnectionChannel::_q_disconnected()
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
}
state = QHttpNetworkConnectionChannel::IdleState;
- if (alreadyPipelinedRequests.length()) {
+ if (alreadyPipelinedRequests.size()) {
// If nothing was in a pipeline, no need in calling
// _q_startNextRequest (which it does):
requeueCurrentlyPipelinedRequests();
@@ -856,6 +856,8 @@ void QHttpNetworkConnectionChannel::_q_connected()
connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
}
connection->d_func()->networkLayerDetected(networkLayerPreference);
+ if (connection->d_func()->activeChannelCount > 1 && !connection->d_func()->encrypt)
+ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
} else {
bool anyProtocol = networkLayerPreference == QAbstractSocket::AnyIPProtocol;
if (((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv4)
@@ -908,7 +910,7 @@ void QHttpNetworkConnectionChannel::_q_connected()
} else if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
state = QHttpNetworkConnectionChannel::IdleState;
protocolHandler.reset(new QHttp2ProtocolHandler(this));
- if (h2RequestsToSend.count() > 0) {
+ if (h2RequestsToSend.size() > 0) {
// In case our peer has sent us its settings (window size, max concurrent streams etc.)
// let's give _q_receiveReply a chance to read them first ('invokeMethod', QueuedConnection).
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
@@ -949,6 +951,10 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
break;
case QAbstractSocket::ConnectionRefusedError:
errorCode = QNetworkReply::ConnectionRefusedError;
+#ifndef QT_NO_NETWORKPROXY
+ if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !ssl)
+ errorCode = QNetworkReply::ProxyConnectionRefusedError;
+#endif
break;
case QAbstractSocket::RemoteHostClosedError:
// This error for SSL comes twice in a row, first from SSL layer ("The TLS/SSL connection has been closed") then from TCP layer.
@@ -1030,6 +1036,9 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
}
errorCode = QNetworkReply::TimeoutError;
break;
+ case QAbstractSocket::ProxyConnectionRefusedError:
+ errorCode = QNetworkReply::ProxyConnectionRefusedError;
+ break;
case QAbstractSocket::ProxyAuthenticationRequiredError:
errorCode = QNetworkReply::ProxyAuthenticationRequiredError;
break;
@@ -1087,16 +1096,15 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
|| connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
- QList<HttpMessagePair> h2Pairs = h2RequestsToSend.values();
- for (int a = 0; a < h2Pairs.count(); ++a) {
+ const auto h2RequestsToSendCopy = std::exchange(h2RequestsToSend, {});
+ for (const auto &httpMessagePair : h2RequestsToSendCopy) {
// emit error for all replies
- QHttpNetworkReply *currentReply = h2Pairs.at(a).second;
+ QHttpNetworkReply *currentReply = httpMessagePair.second;
currentReply->d_func()->errorString = errorString;
currentReply->d_func()->httpErrorCode = errorCode;
Q_ASSERT(currentReply);
emit currentReply->finishedWithError(errorCode, errorString);
}
- h2RequestsToSend.clear();
}
// send the next request
@@ -1120,9 +1128,9 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth)
{
if ((connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
- && (switchedToHttp2 || h2RequestsToSend.count() > 0))
+ && (switchedToHttp2 || h2RequestsToSend.size() > 0))
|| connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
- if (h2RequestsToSend.count() > 0)
+ if (h2RequestsToSend.size() > 0)
connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
} else { // HTTP
// Need to dequeue the request before we can emit the error.
@@ -1145,9 +1153,9 @@ void QHttpNetworkConnectionChannel::emitFinishedWithError(QNetworkReply::Network
{
if (reply)
emit reply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message));
- QList<HttpMessagePair> h2Pairs = h2RequestsToSend.values();
- for (int a = 0; a < h2Pairs.count(); ++a) {
- QHttpNetworkReply *currentReply = h2Pairs.at(a).second;
+ const auto h2RequestsToSendCopy = h2RequestsToSend;
+ for (const auto &httpMessagePair : h2RequestsToSendCopy) {
+ QHttpNetworkReply *currentReply = httpMessagePair.second;
Q_ASSERT(currentReply);
emit currentReply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message));
}
@@ -1228,14 +1236,12 @@ void QHttpNetworkConnectionChannel::_q_encrypted()
if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2 ||
connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
- if (h2RequestsToSend.count() > 0) {
+ if (!h2RequestsToSend.isEmpty()) {
// Similar to HTTP/1.1 counterpart below:
- const auto &h2Pairs = h2RequestsToSend.values(); // (request, reply)
- const auto &pair = h2Pairs.first();
+ const auto &pair = std::as_const(h2RequestsToSend).first();
emit pair.second->encrypted();
// In case our peer has sent us its settings (window size, max concurrent streams etc.)
// let's give _q_receiveReply a chance to read them first ('invokeMethod', QueuedConnection).
- QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
}
} else { // HTTP
if (!reply)
@@ -1248,14 +1254,14 @@ void QHttpNetworkConnectionChannel::_q_encrypted()
if (reply)
sendRequestDelayed();
}
+ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
}
void QHttpNetworkConnectionChannel::requeueHttp2Requests()
{
- QList<HttpMessagePair> h2Pairs = h2RequestsToSend.values();
- for (int a = 0; a < h2Pairs.count(); ++a)
- connection->d_func()->requeueRequest(h2Pairs.at(a));
- h2RequestsToSend.clear();
+ const auto h2RequestsToSendCopy = std::exchange(h2RequestsToSend, {});
+ for (const auto &httpMessagePair : h2RequestsToSendCopy)
+ connection->d_func()->requeueRequest(httpMessagePair);
}
void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
@@ -1274,10 +1280,10 @@ void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
}
#ifndef QT_NO_SSL
else { // HTTP/2
- QList<HttpMessagePair> h2Pairs = h2RequestsToSend.values();
- for (int a = 0; a < h2Pairs.count(); ++a) {
+ const auto h2RequestsToSendCopy = h2RequestsToSend;
+ for (const auto &httpMessagePair : h2RequestsToSendCopy) {
// emit SSL errors for all replies
- QHttpNetworkReply *currentReply = h2Pairs.at(a).second;
+ QHttpNetworkReply *currentReply = httpMessagePair.second;
Q_ASSERT(currentReply);
emit currentReply->sslErrors(errors);
}
@@ -1297,10 +1303,10 @@ void QHttpNetworkConnectionChannel::_q_preSharedKeyAuthenticationRequired(QSslPr
if (reply)
emit reply->preSharedKeyAuthenticationRequired(authenticator);
} else {
- QList<HttpMessagePair> h2Pairs = h2RequestsToSend.values();
- for (int a = 0; a < h2Pairs.count(); ++a) {
+ const auto h2RequestsToSendCopy = h2RequestsToSend;
+ for (const auto &httpMessagePair : h2RequestsToSendCopy) {
// emit SSL errors for all replies
- QHttpNetworkReply *currentReply = h2Pairs.at(a).second;
+ QHttpNetworkReply *currentReply = httpMessagePair.second;
Q_ASSERT(currentReply);
emit currentReply->preSharedKeyAuthenticationRequired(authenticator);
}
diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h
index 0772a4452b..c42290feca 100644
--- a/src/network/access/qhttpnetworkconnectionchannel_p.h
+++ b/src/network/access/qhttpnetworkconnectionchannel_p.h
@@ -40,6 +40,7 @@
# include <QtNetwork/qtcpsocket.h>
#endif
+#include <QtCore/qpointer.h>
#include <QtCore/qscopedpointer.h>
#include <memory>
diff --git a/src/network/access/qhttpnetworkheader.cpp b/src/network/access/qhttpnetworkheader.cpp
index e0a964d253..7f9c94dc9c 100644
--- a/src/network/access/qhttpnetworkheader.cpp
+++ b/src/network/access/qhttpnetworkheader.cpp
@@ -29,7 +29,7 @@ void QHttpNetworkHeaderPrivate::setContentLength(qint64 length)
setHeaderField("Content-Length", QByteArray::number(length));
}
-QByteArray QHttpNetworkHeaderPrivate::headerField(const QByteArray &name, const QByteArray &defaultValue) const
+QByteArray QHttpNetworkHeaderPrivate::headerField(QByteArrayView name, const QByteArray &defaultValue) const
{
QList<QByteArray> allValues = headerFieldValues(name);
if (allValues.isEmpty())
@@ -38,7 +38,7 @@ QByteArray QHttpNetworkHeaderPrivate::headerField(const QByteArray &name, const
return allValues.join(", ");
}
-QList<QByteArray> QHttpNetworkHeaderPrivate::headerFieldValues(const QByteArray &name) const
+QList<QByteArray> QHttpNetworkHeaderPrivate::headerFieldValues(QByteArrayView name) const
{
return parser.headerFieldValues(name);
}
@@ -53,7 +53,7 @@ void QHttpNetworkHeaderPrivate::prependHeaderField(const QByteArray &name, const
parser.prependHeaderField(name, data);
}
-QList<QPair<QByteArray, QByteArray> > QHttpNetworkHeaderPrivate::headers() const
+QHttpHeaders QHttpNetworkHeaderPrivate::headers() const
{
return parser.headers();
}
diff --git a/src/network/access/qhttpnetworkheader_p.h b/src/network/access/qhttpnetworkheader_p.h
index fc5d388ae5..afbc6cb6fe 100644
--- a/src/network/access/qhttpnetworkheader_p.h
+++ b/src/network/access/qhttpnetworkheader_p.h
@@ -17,6 +17,7 @@
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <QtNetwork/private/qhttpheaderparser_p.h>
+#include <QtNetwork/qhttpheaders.h>
#include <qshareddata.h>
#include <qurl.h>
@@ -40,8 +41,8 @@ public:
virtual qint64 contentLength() const = 0;
virtual void setContentLength(qint64 length) = 0;
- virtual QList<QPair<QByteArray, QByteArray> > header() const = 0;
- virtual QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const = 0;
+ virtual QHttpHeaders header() const = 0;
+ virtual QByteArray headerField(QByteArrayView name, const QByteArray &defaultValue = QByteArray()) const = 0;
virtual void setHeaderField(const QByteArray &name, const QByteArray &data) = 0;
};
@@ -56,12 +57,12 @@ public:
qint64 contentLength() const;
void setContentLength(qint64 length);
- QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const;
- QList<QByteArray> headerFieldValues(const QByteArray &name) const;
+ QByteArray headerField(QByteArrayView name, const QByteArray &defaultValue = QByteArray()) const;
+ QList<QByteArray> headerFieldValues(QByteArrayView name) const;
void setHeaderField(const QByteArray &name, const QByteArray &data);
void prependHeaderField(const QByteArray &name, const QByteArray &data);
void clearHeaders();
- QList<QPair<QByteArray, QByteArray> > headers() const;
+ QHttpHeaders headers() const;
bool operator==(const QHttpNetworkHeaderPrivate &other) const;
};
diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp
index cb93d3e410..5711c96b18 100644
--- a/src/network/access/qhttpnetworkreply.cpp
+++ b/src/network/access/qhttpnetworkreply.cpp
@@ -67,12 +67,12 @@ void QHttpNetworkReply::setContentLength(qint64 length)
d->setContentLength(length);
}
-QList<QPair<QByteArray, QByteArray> > QHttpNetworkReply::header() const
+QHttpHeaders QHttpNetworkReply::header() const
{
return d_func()->parser.headers();
}
-QByteArray QHttpNetworkReply::headerField(const QByteArray &name, const QByteArray &defaultValue) const
+QByteArray QHttpNetworkReply::headerField(QByteArrayView name, const QByteArray &defaultValue) const
{
return d_func()->headerField(name, defaultValue);
}
@@ -89,7 +89,7 @@ void QHttpNetworkReply::appendHeaderField(const QByteArray &name, const QByteArr
d->appendHeaderField(name, data);
}
-void QHttpNetworkReply::parseHeader(const QByteArray &header)
+void QHttpNetworkReply::parseHeader(QByteArrayView header)
{
Q_D(QHttpNetworkReply);
d->parseHeader(header);
@@ -368,7 +368,7 @@ void QHttpNetworkReplyPrivate::removeAutoDecompressHeader()
{
// The header "Content-Encoding = gzip" is retained.
// Content-Length is removed since the actual one sent by the server is for compressed data
- QByteArray name("content-length");
+ constexpr auto name = QByteArrayView("content-length");
QByteArray contentLength = parser.firstHeaderField(name);
bool parseOk = false;
qint64 value = contentLength.toLongLong(&parseOk);
@@ -378,23 +378,7 @@ void QHttpNetworkReplyPrivate::removeAutoDecompressHeader()
}
}
-bool QHttpNetworkReplyPrivate::findChallenge(bool forProxy, QByteArray &challenge) const
-{
- challenge.clear();
- // find out the type of authentication protocol requested.
- QByteArray header = forProxy ? "proxy-authenticate" : "www-authenticate";
- // pick the best protocol (has to match parsing in QAuthenticatorPrivate)
- QList<QByteArray> challenges = headerFieldValues(header);
- for (int i = 0; i<challenges.size(); i++) {
- QByteArray line = challenges.at(i);
- // todo use qstrincmp
- if (!line.toLower().startsWith("negotiate"))
- challenge = line;
- }
- return !challenge.isEmpty();
-}
-
-qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
+qint64 QHttpNetworkReplyPrivate::readStatus(QIODevice *socket)
{
if (fragment.isEmpty()) {
// reserve bytes for the status line. This is better than always append() which reallocs the byte array
@@ -420,7 +404,7 @@ qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
if (c == '\n') {
// remove the CR at the end
if (fragment.endsWith('\r')) {
- fragment.truncate(fragment.length()-1);
+ fragment.truncate(fragment.size()-1);
}
bool ok = parseStatus(fragment);
state = ReadingHeaderState;
@@ -434,7 +418,7 @@ qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
}
// is this a valid reply?
- if (fragment.length() == 5 && !fragment.startsWith("HTTP/")) {
+ if (fragment.size() == 5 && !fragment.startsWith("HTTP/")) {
fragment.clear();
return -1;
}
@@ -443,12 +427,12 @@ qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
return bytes;
}
-bool QHttpNetworkReplyPrivate::parseStatus(const QByteArray &status)
+bool QHttpNetworkReplyPrivate::parseStatus(QByteArrayView status)
{
return parser.parseStatus(status);
}
-qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
+qint64 QHttpNetworkReplyPrivate::readHeader(QIODevice *socket)
{
if (fragment.isEmpty()) {
// according to http://dev.opera.com/articles/view/mama-http-headers/ the average size of the header
@@ -482,8 +466,8 @@ qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
allHeaders = true;
// there is another case: We have no headers. Then the fragment equals just the line ending
- if ((fragment.length() == 2 && fragment.endsWith("\r\n"))
- || (fragment.length() == 1 && fragment.endsWith("\n")))
+ if ((fragment.size() == 2 && fragment.endsWith("\r\n"))
+ || (fragment.size() == 1 && fragment.endsWith("\n")))
allHeaders = true;
}
}
@@ -510,7 +494,7 @@ qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
return bytes;
}
-void QHttpNetworkReplyPrivate::parseHeader(const QByteArray &header)
+void QHttpNetworkReplyPrivate::parseHeader(QByteArrayView header)
{
parser.parseHeaders(header);
}
@@ -532,7 +516,7 @@ bool QHttpNetworkReplyPrivate::isConnectionCloseEnabled()
// note this function can only be used for non-chunked, non-compressed with
// known content length
-qint64 QHttpNetworkReplyPrivate::readBodyVeryFast(QAbstractSocket *socket, char *b)
+qint64 QHttpNetworkReplyPrivate::readBodyVeryFast(QIODevice *socket, char *b)
{
// This first read is to flush the buffer inside the socket
qint64 haveRead = 0;
@@ -551,7 +535,7 @@ qint64 QHttpNetworkReplyPrivate::readBodyVeryFast(QAbstractSocket *socket, char
// note this function can only be used for non-chunked, non-compressed with
// known content length
-qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb)
+qint64 QHttpNetworkReplyPrivate::readBodyFast(QIODevice *socket, QByteDataBuffer *rb)
{
qint64 toBeRead = qMin(socket->bytesAvailable(), bodyLength - contentRead);
@@ -581,7 +565,7 @@ qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QByteData
}
-qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuffer *out)
+qint64 QHttpNetworkReplyPrivate::readBody(QIODevice *socket, QByteDataBuffer *out)
{
qint64 bytes = 0;
@@ -601,7 +585,7 @@ qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuff
return bytes;
}
-qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QAbstractSocket *socket, QByteDataBuffer *out, qint64 size)
+qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QIODevice *socket, QByteDataBuffer *out, qint64 size)
{
// FIXME get rid of this function and just use readBodyFast and give it socket->bytesAvailable()
qint64 bytes = 0;
@@ -634,7 +618,7 @@ qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QAbstractSocket *socket, QByte
}
-qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QAbstractSocket *socket, QByteDataBuffer *out)
+qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *socket, QByteDataBuffer *out)
{
qint64 bytes = 0;
while (socket->bytesAvailable()) {
@@ -696,7 +680,7 @@ qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QAbstractSocket *socket, Q
return bytes;
}
-qint64 QHttpNetworkReplyPrivate::getChunkSize(QAbstractSocket *socket, qint64 *chunkSize)
+qint64 QHttpNetworkReplyPrivate::getChunkSize(QIODevice *socket, qint64 *chunkSize)
{
qint64 bytes = 0;
char crlf[2];
@@ -717,8 +701,8 @@ qint64 QHttpNetworkReplyPrivate::getChunkSize(QAbstractSocket *socket, qint64 *c
bytes += socket->read(crlf, 1); // read the \n
bool ok = false;
// ignore the chunk-extension
- fragment = fragment.mid(0, fragment.indexOf(';')).trimmed();
- *chunkSize = fragment.toLong(&ok, 16);
+ const auto fragmentView = QByteArrayView(fragment).mid(0, fragment.indexOf(';')).trimmed();
+ *chunkSize = fragmentView.toLong(&ok, 16);
fragment.clear();
break; // size done
} else {
diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h
index ed76fde1d6..caec82bd7e 100644
--- a/src/network/access/qhttpnetworkreply_p.h
+++ b/src/network/access/qhttpnetworkreply_p.h
@@ -41,6 +41,9 @@ Q_MOC_INCLUDE(<QtNetwork/QNetworkProxy>)
Q_MOC_INCLUDE(<QtNetwork/QAuthenticator>)
#include <private/qdecompresshelper_p.h>
+#include <QtNetwork/qhttpheaders.h>
+
+#include <QtCore/qpointer.h>
QT_REQUIRE_CONFIG(http);
@@ -51,7 +54,7 @@ class QHttpNetworkConnectionChannel;
class QHttpNetworkRequest;
class QHttpNetworkConnectionPrivate;
class QHttpNetworkReplyPrivate;
-class Q_AUTOTEST_EXPORT QHttpNetworkReply : public QObject, public QHttpNetworkHeader
+class Q_NETWORK_EXPORT QHttpNetworkReply : public QObject, public QHttpNetworkHeader
{
Q_OBJECT
public:
@@ -70,11 +73,11 @@ public:
qint64 contentLength() const override;
void setContentLength(qint64 length) override;
- QList<QPair<QByteArray, QByteArray> > header() const override;
- QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const override;
+ QHttpHeaders header() const override;
+ QByteArray headerField(QByteArrayView name, const QByteArray &defaultValue = QByteArray()) const override;
void setHeaderField(const QByteArray &name, const QByteArray &data) override;
void appendHeaderField(const QByteArray &name, const QByteArray &data);
- void parseHeader(const QByteArray &header); // used for testing
+ void parseHeader(QByteArrayView header); // used for testing
QHttpNetworkRequest request() const;
void setRequest(const QHttpNetworkRequest &request);
@@ -169,21 +172,20 @@ class Q_AUTOTEST_EXPORT QHttpNetworkReplyPrivate : public QObjectPrivate, public
public:
QHttpNetworkReplyPrivate(const QUrl &newUrl = QUrl());
~QHttpNetworkReplyPrivate();
- qint64 readStatus(QAbstractSocket *socket);
- bool parseStatus(const QByteArray &status);
- qint64 readHeader(QAbstractSocket *socket);
- void parseHeader(const QByteArray &header);
+ qint64 readStatus(QIODevice *socket);
+ bool parseStatus(QByteArrayView status);
+ qint64 readHeader(QIODevice *socket);
+ void parseHeader(QByteArrayView header);
void appendHeaderField(const QByteArray &name, const QByteArray &data);
- qint64 readBody(QAbstractSocket *socket, QByteDataBuffer *out);
- qint64 readBodyVeryFast(QAbstractSocket *socket, char *b);
- qint64 readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb);
- bool findChallenge(bool forProxy, QByteArray &challenge) const;
+ qint64 readBody(QIODevice *socket, QByteDataBuffer *out);
+ qint64 readBodyVeryFast(QIODevice *socket, char *b);
+ qint64 readBodyFast(QIODevice *socket, QByteDataBuffer *rb);
void clear();
void clearHttpLayerInformation();
- qint64 readReplyBodyRaw(QAbstractSocket *in, QByteDataBuffer *out, qint64 size);
- qint64 readReplyBodyChunked(QAbstractSocket *in, QByteDataBuffer *out);
- qint64 getChunkSize(QAbstractSocket *in, qint64 *chunkSize);
+ qint64 readReplyBodyRaw(QIODevice *in, QByteDataBuffer *out, qint64 size);
+ qint64 readReplyBodyChunked(QIODevice *in, QByteDataBuffer *out);
+ qint64 getChunkSize(QIODevice *in, qint64 *chunkSize);
bool isRedirecting() const;
bool shouldEmitSignals();
diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp
index 8c90c92db9..7a4ffb1684 100644
--- a/src/network/access/qhttpnetworkrequest.cpp
+++ b/src/network/access/qhttpnetworkrequest.cpp
@@ -112,9 +112,9 @@ QByteArray QHttpNetworkRequest::uri(bool throughProxy) const
QByteArray QHttpNetworkRequestPrivate::header(const QHttpNetworkRequest &request, bool throughProxy)
{
- QList<QPair<QByteArray, QByteArray> > fields = request.header();
+ const QHttpHeaders headers = request.header();
QByteArray ba;
- ba.reserve(40 + fields.length()*25); // very rough lower bound estimation
+ ba.reserve(40 + headers.size() * 25); // very rough lower bound estimation
ba += request.methodName();
ba += ' ';
@@ -126,12 +126,10 @@ QByteArray QHttpNetworkRequestPrivate::header(const QHttpNetworkRequest &request
ba += QByteArray::number(request.minorVersion());
ba += "\r\n";
- QList<QPair<QByteArray, QByteArray> >::const_iterator it = fields.constBegin();
- QList<QPair<QByteArray, QByteArray> >::const_iterator endIt = fields.constEnd();
- for (; it != endIt; ++it) {
- ba += it->first;
+ for (qsizetype i = 0; i < headers.size(); ++i) {
+ ba += headers.nameAt(i);
ba += ": ";
- ba += it->second;
+ ba += headers.valueAt(i);
ba += "\r\n";
}
if (request.d->operation == QHttpNetworkRequest::Post) {
@@ -237,12 +235,12 @@ void QHttpNetworkRequest::setContentLength(qint64 length)
d->setContentLength(length);
}
-QList<QPair<QByteArray, QByteArray> > QHttpNetworkRequest::header() const
+QHttpHeaders QHttpNetworkRequest::header() const
{
return d->parser.headers();
}
-QByteArray QHttpNetworkRequest::headerField(const QByteArray &name, const QByteArray &defaultValue) const
+QByteArray QHttpNetworkRequest::headerField(QByteArrayView name, const QByteArray &defaultValue) const
{
return d->headerField(name, defaultValue);
}
diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h
index e2014c4221..131885f6d2 100644
--- a/src/network/access/qhttpnetworkrequest_p.h
+++ b/src/network/access/qhttpnetworkrequest_p.h
@@ -65,8 +65,8 @@ public:
qint64 contentLength() const override;
void setContentLength(qint64 length) override;
- QList<QPair<QByteArray, QByteArray> > header() const override;
- QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const override;
+ QHttpHeaders header() const override;
+ QByteArray headerField(QByteArrayView name, const QByteArray &defaultValue = QByteArray()) const override;
void setHeaderField(const QByteArray &name, const QByteArray &data) override;
void prependHeaderField(const QByteArray &name, const QByteArray &data);
void clearHeaders();
diff --git a/src/network/access/qhttpprotocolhandler.cpp b/src/network/access/qhttpprotocolhandler.cpp
index 36102909df..28eab03890 100644
--- a/src/network/access/qhttpprotocolhandler.cpp
+++ b/src/network/access/qhttpprotocolhandler.cpp
@@ -93,7 +93,8 @@ void QHttpProtocolHandler::_q_receiveReply()
} else {
replyPrivate->autoDecompress = false;
}
- if (m_reply->statusCode() == 100) {
+ const int statusCode = m_reply->statusCode();
+ if (statusCode == 100 || (102 <= statusCode && statusCode <= 199)) {
replyPrivate->clearHttpLayerInformation();
replyPrivate->state = QHttpNetworkReplyPrivate::ReadingStatusState;
break; // ignore
@@ -238,8 +239,7 @@ bool QHttpProtocolHandler::sendRequest()
// _q_connected or _q_encrypted
return false;
}
- QString scheme = m_channel->request.url().scheme();
- if (scheme == "preconnect-http"_L1 || scheme == "preconnect-https"_L1) {
+ if (m_channel->request.isPreConnect()) {
m_channel->state = QHttpNetworkConnectionChannel::IdleState;
m_reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
m_channel->allDone();
@@ -298,7 +298,7 @@ bool QHttpProtocolHandler::sendRequest()
sendRequest(); //recurse
} else {
// no data to send: just send the HTTP headers
- m_socket->write(qExchange(m_header, {}));
+ m_socket->write(std::exchange(m_header, {}));
QMetaObject::invokeMethod(m_reply, "requestSent", Qt::QueuedConnection);
m_channel->state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
sendRequest(); //recurse
@@ -314,7 +314,7 @@ bool QHttpProtocolHandler::sendRequest()
// the upload device might have no data to send, but we still have to send the headers,
// do it now.
if (!m_header.isEmpty())
- m_socket->write(qExchange(m_header, {}));
+ m_socket->write(std::exchange(m_header, {}));
if (uploadByteDevice)
emit m_reply->dataSendProgress(m_channel->written, m_channel->bytesTotal);
m_channel->state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
@@ -371,7 +371,7 @@ bool QHttpProtocolHandler::sendRequest()
// assemble header and data and send them together
const qint64 headerSize = m_header.size();
m_header.append(readPointer, currentReadSize);
- currentWriteSize = m_socket->write(qExchange(m_header, {}));
+ currentWriteSize = m_socket->write(std::exchange(m_header, {}));
if (currentWriteSize != -1)
currentWriteSize -= headerSize;
QMetaObject::invokeMethod(m_reply, "requestSent", Qt::QueuedConnection);
diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp
index 4a11b828a8..b0ae0dcf44 100644
--- a/src/network/access/qhttpthreaddelegate.cpp
+++ b/src/network/access/qhttpthreaddelegate.cpp
@@ -145,9 +145,9 @@ class QNetworkAccessCachedHttpConnection: public QHttpNetworkConnection,
{
// Q_OBJECT
public:
- QNetworkAccessCachedHttpConnection(const QString &hostName, quint16 port, bool encrypt,
+ QNetworkAccessCachedHttpConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt,
QHttpNetworkConnection::ConnectionType connectionType)
- : QHttpNetworkConnection(hostName, port, encrypt, connectionType)
+ : QHttpNetworkConnection(connectionCount, hostName, port, encrypt, /*parent=*/nullptr, connectionType)
{
setExpires(true);
setShareable(true);
@@ -297,7 +297,7 @@ void QHttpThreadDelegate::startRequest()
if (!httpConnection) {
// no entry in cache; create an object
// the http object is actually a QHttpNetworkConnection
- httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl,
+ httpConnection = new QNetworkAccessCachedHttpConnection(http1Parameters.numberOfConnectionsPerHost(), urlCopy.host(), urlCopy.port(), ssl,
connectionType);
if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
|| connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
@@ -567,11 +567,6 @@ void QHttpThreadDelegate::synchronousFinishedWithErrorSlot(QNetworkReply::Networ
httpReply = nullptr;
}
-static void downloadBufferDeleter(char *ptr)
-{
- delete[] ptr;
-}
-
void QHttpThreadDelegate::headerChangedSlot()
{
if (!httpReply)
@@ -589,14 +584,11 @@ void QHttpThreadDelegate::headerChangedSlot()
// Is using a zerocopy buffer allowed by user and possible with this reply?
if (httpReply->supportsUserProvidedDownloadBuffer()
&& (downloadBufferMaximumSize > 0) && (httpReply->contentLength() <= downloadBufferMaximumSize)) {
- QT_TRY {
- char *buf = new char[httpReply->contentLength()]; // throws if allocation fails
- if (buf) {
- downloadBuffer = QSharedPointer<char>(buf, downloadBufferDeleter);
- httpReply->setUserProvidedDownloadBuffer(buf);
- }
- } QT_CATCH(const std::bad_alloc &) {
- // in out of memory situations, don't use downloadbuffer.
+ char *buf = new (std::nothrow) char[httpReply->contentLength()];
+ // in out of memory situations, don't use downloadBuffer.
+ if (buf) {
+ downloadBuffer = QSharedPointer<char>(buf, [](auto p) { delete[] p; });
+ httpReply->setUserProvidedDownloadBuffer(buf);
}
}
diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h
index d16edf3b0f..38e9fb4d78 100644
--- a/src/network/access/qhttpthreaddelegate_p.h
+++ b/src/network/access/qhttpthreaddelegate_p.h
@@ -26,12 +26,14 @@
#include <QNetworkReply>
#include "qhttpnetworkrequest_p.h"
#include "qhttpnetworkconnection_p.h"
+#include "qhttp1configuration.h"
#include "qhttp2configuration.h"
#include <QSharedPointer>
#include <QScopedPointer>
#include "private/qnoncontiguousbytedevice_p.h"
#include "qnetworkaccessauthenticationmanager_p.h"
#include <QtNetwork/private/http2protocol_p.h>
+#include <QtNetwork/qhttpheaders.h>
QT_REQUIRE_CONFIG(http);
@@ -73,7 +75,7 @@ public:
// outgoing, Retrieved in the synchronous HTTP case
QByteArray synchronousDownloadData;
- QList<QPair<QByteArray,QByteArray> > incomingHeaders;
+ QHttpHeaders incomingHeaders;
int incomingStatusCode;
QString incomingReasonPhrase;
bool isPipeliningUsed;
@@ -82,6 +84,7 @@ public:
qint64 removedContentLength;
QNetworkReply::NetworkError incomingErrorCode;
QString incomingErrorDetail;
+ QHttp1Configuration http1Parameters;
QHttp2Configuration http2Parameters;
bool isCompressed;
@@ -110,7 +113,7 @@ signals:
#endif
void socketStartedConnecting();
void requestSent();
- void downloadMetaData(const QList<QPair<QByteArray,QByteArray> > &, int, const QString &, bool,
+ void downloadMetaData(const QHttpHeaders &, int, const QString &, bool,
QSharedPointer<char>, qint64, qint64, bool, bool);
void downloadProgress(qint64, qint64);
void downloadData(const QByteArray &);
@@ -162,22 +165,18 @@ class QNonContiguousByteDeviceThreadForwardImpl : public QNonContiguousByteDevic
{
Q_OBJECT
protected:
- bool wantDataPending;
- qint64 m_amount;
- char *m_data;
+ bool wantDataPending = false;
+ qint64 m_amount = 0;
+ char *m_data = nullptr;
QByteArray m_dataArray;
- bool m_atEnd;
- qint64 m_size;
- qint64 m_pos; // to match calls of haveDataSlot with the expected position
+ bool m_atEnd = false;
+ qint64 m_size = 0;
+ qint64 m_pos = 0; // to match calls of haveDataSlot with the expected position
public:
QNonContiguousByteDeviceThreadForwardImpl(bool aE, qint64 s)
: QNonContiguousByteDevice(),
- wantDataPending(false),
- m_amount(0),
- m_data(nullptr),
m_atEnd(aE),
- m_size(s),
- m_pos(0)
+ m_size(s)
{
}
@@ -250,6 +249,7 @@ public:
if (b) {
// the reset succeeded, we're at pos 0 again
m_pos = 0;
+ m_atEnd = false;
// the HTTP code will anyway abort the request if !b.
}
return b;
diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp
index 7b3b112825..5dbcef4bbe 100644
--- a/src/network/access/qnetworkaccessbackend.cpp
+++ b/src/network/access/qnetworkaccessbackend.cpp
@@ -36,7 +36,7 @@ public:
static QBasicAtomicInt valid;
};
Q_GLOBAL_STATIC(QNetworkAccessBackendFactoryData, factoryData)
-QBasicAtomicInt QNetworkAccessBackendFactoryData::valid = Q_BASIC_ATOMIC_INITIALIZER(0);
+Q_CONSTINIT QBasicAtomicInt QNetworkAccessBackendFactoryData::valid = Q_BASIC_ATOMIC_INITIALIZER(0);
class QNetworkAccessBackendPrivate : public QObjectPrivate
{
diff --git a/src/network/access/qnetworkaccesscache.cpp b/src/network/access/qnetworkaccesscache.cpp
index 9a1a3d3806..2bc0e8fb70 100644
--- a/src/network/access/qnetworkaccesscache.cpp
+++ b/src/network/access/qnetworkaccesscache.cpp
@@ -14,9 +14,6 @@
QT_BEGIN_NAMESPACE
-QT_IMPL_METATYPE_EXTERN_TAGGED(QNetworkAccessCache::CacheableObject*,
- QNetworkAccessCache__CacheableObject_ptr)
-
enum ExpiryTimeEnum {
ExpiryTime = 120
};
diff --git a/src/network/access/qnetworkaccesscache_p.h b/src/network/access/qnetworkaccesscache_p.h
index d0c032de6f..3be7967ca1 100644
--- a/src/network/access/qnetworkaccesscache_p.h
+++ b/src/network/access/qnetworkaccesscache_p.h
@@ -87,7 +87,4 @@ private:
QT_END_NAMESPACE
-QT_DECL_METATYPE_EXTERN_TAGGED(QNetworkAccessCache::CacheableObject*,
- QNetworkAccessCache__CacheableObject_ptr, /* not exported */)
-
#endif
diff --git a/src/network/access/qnetworkaccesscachebackend.cpp b/src/network/access/qnetworkaccesscachebackend.cpp
index 99bef10488..fd8174c143 100644
--- a/src/network/access/qnetworkaccesscachebackend.cpp
+++ b/src/network/access/qnetworkaccesscachebackend.cpp
@@ -12,6 +12,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
QNetworkAccessCacheBackend::QNetworkAccessCacheBackend()
: QNetworkAccessBackend(QNetworkAccessBackend::TargetType::Local)
{
@@ -51,10 +53,10 @@ bool QNetworkAccessCacheBackend::sendCacheContents()
// set the raw headers
const QNetworkCacheMetaData::RawHeaderList rawHeaders = item.rawHeaders();
for (const auto &header : rawHeaders) {
- if (header.first.toLower() == "cache-control") {
- const QByteArray cacheControlValue = header.second.toLower();
- if (cacheControlValue.contains("must-revalidate")
- || cacheControlValue.contains("no-cache")) {
+ if (header.first.compare("cache-control", Qt::CaseInsensitive) == 0) {
+ const QLatin1StringView cacheControlValue(header.second);
+ if (cacheControlValue.contains("must-revalidate"_L1, Qt::CaseInsensitive)
+ || cacheControlValue.contains("no-cache"_L1, Qt::CaseInsensitive)) {
return false;
}
}
diff --git a/src/network/access/qnetworkaccessdebugpipebackend.cpp b/src/network/access/qnetworkaccessdebugpipebackend.cpp
index 2df14289a4..4b12f9fe31 100644
--- a/src/network/access/qnetworkaccessdebugpipebackend.cpp
+++ b/src/network/access/qnetworkaccessdebugpipebackend.cpp
@@ -184,7 +184,7 @@ void QNetworkAccessDebugPipeBackend::possiblyFinish()
void QNetworkAccessDebugPipeBackend::close()
{
- qWarning("QNetworkAccessDebugPipeBackend::closeDownstreamChannel() %d",operation());;
+ qWarning("QNetworkAccessDebugPipeBackend::closeDownstreamChannel() %d",operation());
//if (operation() == QNetworkAccessManager::GetOperation)
// socket.disconnectFromHost();
}
diff --git a/src/network/access/qnetworkaccessfilebackend.cpp b/src/network/access/qnetworkaccessfilebackend.cpp
index 4b56870d15..2100c188a5 100644
--- a/src/network/access/qnetworkaccessfilebackend.cpp
+++ b/src/network/access/qnetworkaccessfilebackend.cpp
@@ -46,7 +46,7 @@ QNetworkAccessFileBackendFactory::create(QNetworkAccessManager::Operation op,
#endif
|| url.isLocalFile()) {
return new QNetworkAccessFileBackend;
- } else if (!url.scheme().isEmpty() && url.authority().isEmpty() && (url.scheme().length() > 1)) {
+ } else if (!url.scheme().isEmpty() && url.authority().isEmpty() && (url.scheme().size() > 1)) {
// check if QFile could, in theory, open this URL via the file engines
// it has to be in the format:
// prefix:path/to/file
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index 4804046ea3..4e13c9924b 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -45,12 +45,15 @@
#include <QHostInfo>
#include "QtCore/qapplicationstatic.h"
+#include "QtCore/qloggingcategory.h"
#include <QtCore/private/qfactoryloader_p.h>
#if defined(Q_OS_MACOS)
+#include <QtCore/private/qcore_mac_p.h>
+
#include <CoreServices/CoreServices.h>
#include <SystemConfiguration/SystemConfiguration.h>
-#include <Security/SecKeychain.h>
+#include <Security/Security.h>
#endif
#ifdef Q_OS_WASM
#include "qnetworkreplywasmimpl_p.h"
@@ -65,73 +68,93 @@
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
+using namespace std::chrono_literals;
+
+Q_LOGGING_CATEGORY(lcQnam, "qt.network.access.manager")
Q_APPLICATION_STATIC(QNetworkAccessFileBackendFactory, fileBackend)
-#ifdef QT_BUILD_INTERNAL
+#if QT_CONFIG(private_tests)
Q_GLOBAL_STATIC(QNetworkAccessDebugPipeBackendFactory, debugpipeBackend)
#endif
-Q_APPLICATION_STATIC(QFactoryLoader, loader, QNetworkAccessBackendFactory_iid, "/networkaccess"_L1)
+Q_APPLICATION_STATIC(QFactoryLoader, qnabfLoader, QNetworkAccessBackendFactory_iid, "/networkaccess"_L1)
#if defined(Q_OS_MACOS)
bool getProxyAuth(const QString& proxyHostname, const QString &scheme, QString& username, QString& password)
{
- OSStatus err;
- SecKeychainItemRef itemRef;
- bool retValue = false;
- SecProtocolType protocolType = kSecProtocolTypeAny;
+ CFStringRef protocolType = nullptr;
if (scheme.compare("ftp"_L1, Qt::CaseInsensitive) == 0) {
- protocolType = kSecProtocolTypeFTPProxy;
+ protocolType = kSecAttrProtocolFTPProxy;
} else if (scheme.compare("http"_L1, Qt::CaseInsensitive) == 0
|| scheme.compare("preconnect-http"_L1, Qt::CaseInsensitive) == 0) {
- protocolType = kSecProtocolTypeHTTPProxy;
+ protocolType = kSecAttrProtocolHTTPProxy;
} else if (scheme.compare("https"_L1,Qt::CaseInsensitive)==0
|| scheme.compare("preconnect-https"_L1, Qt::CaseInsensitive) == 0) {
- protocolType = kSecProtocolTypeHTTPSProxy;
+ protocolType = kSecAttrProtocolHTTPSProxy;
+ } else {
+ qCWarning(lcQnam) << "Cannot query user name and password for a proxy, unnknown protocol:"
+ << scheme;
+ return false;
}
- QByteArray proxyHostnameUtf8(proxyHostname.toUtf8());
- err = SecKeychainFindInternetPassword(NULL,
- proxyHostnameUtf8.length(), proxyHostnameUtf8.constData(),
- 0,NULL,
- 0, NULL,
- 0, NULL,
- 0,
- protocolType,
- kSecAuthenticationTypeAny,
- 0, NULL,
- &itemRef);
- if (err == noErr) {
-
- SecKeychainAttribute attr;
- SecKeychainAttributeList attrList;
- UInt32 length;
- void *outData;
-
- attr.tag = kSecAccountItemAttr;
- attr.length = 0;
- attr.data = NULL;
-
- attrList.count = 1;
- attrList.attr = &attr;
-
- if (SecKeychainItemCopyContent(itemRef, NULL, &attrList, &length, &outData) == noErr) {
- username = QString::fromUtf8((const char*)attr.data, attr.length);
- password = QString::fromUtf8((const char*)outData, length);
- SecKeychainItemFreeContent(&attrList,outData);
- retValue = true;
- }
- CFRelease(itemRef);
+
+ QCFType<CFMutableDictionaryRef> query(CFDictionaryCreateMutable(kCFAllocatorDefault,
+ 0, nullptr, nullptr));
+ Q_ASSERT(query);
+
+ CFDictionaryAddValue(query, kSecClass, kSecClassInternetPassword);
+ CFDictionaryAddValue(query, kSecAttrProtocol, protocolType);
+
+ QCFType<CFStringRef> serverName; // Note the scope.
+ if (proxyHostname.size()) {
+ serverName = proxyHostname.toCFString();
+ CFDictionaryAddValue(query, kSecAttrServer, serverName);
+ }
+
+ // This is to get the user name in the result:
+ CFDictionaryAddValue(query, kSecReturnAttributes, kCFBooleanTrue);
+ // This one to get the password:
+ CFDictionaryAddValue(query, kSecReturnData, kCFBooleanTrue);
+
+ // The default for kSecMatchLimit key is 1 (the first match only), which is fine,
+ // so don't set this value explicitly.
+
+ QCFType<CFTypeRef> replyData;
+ if (SecItemCopyMatching(query, &replyData) != errSecSuccess) {
+ qCWarning(lcQnam, "Failed to extract user name and password from the keychain.");
+ return false;
+ }
+
+ if (!replyData || CFDictionaryGetTypeID() != CFGetTypeID(replyData)) {
+ qCWarning(lcQnam, "Query returned data in unexpected format.");
+ return false;
+ }
+
+ CFDictionaryRef accountData = replyData.as<CFDictionaryRef>();
+ const void *value = CFDictionaryGetValue(accountData, kSecAttrAccount);
+ if (!value || CFGetTypeID(value) != CFStringGetTypeID()) {
+ qCWarning(lcQnam, "Cannot find user name or its format is unknown.");
+ return false;
}
- return retValue;
+ username = QString::fromCFString(static_cast<CFStringRef>(value));
+
+ value = CFDictionaryGetValue(accountData, kSecValueData);
+ if (!value || CFGetTypeID(value) != CFDataGetTypeID()) {
+ qCWarning(lcQnam, "Cannot find password or its format is unknown.");
+ return false;
+ }
+ const CFDataRef passData = static_cast<const CFDataRef>(value);
+ password = QString::fromLocal8Bit(reinterpret_cast<const char *>(CFDataGetBytePtr(passData)),
+ qsizetype(CFDataGetLength(passData)));
+ return true;
}
-#endif
+#endif // Q_OS_MACOS
static void ensureInitialized()
{
-#ifdef QT_BUILD_INTERNAL
+#if QT_CONFIG(private_tests)
(void) debugpipeBackend();
#endif
@@ -758,6 +781,46 @@ QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request)
}
/*!
+ \since 6.7
+
+ \overload
+
+ \note A GET request with a message body is not cached.
+
+ \note If the request is redirected, the message body will be kept only if the status code is
+ 307 or 308.
+*/
+
+QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request, QIODevice *data)
+{
+ QNetworkRequest newRequest(request);
+ return d_func()->postProcess(
+ createRequest(QNetworkAccessManager::GetOperation, newRequest, data));
+}
+
+/*!
+ \since 6.7
+
+ \overload
+
+ \note A GET request with a message body is not cached.
+
+ \note If the request is redirected, the message body will be kept only if the status code is
+ 307 or 308.
+*/
+
+QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request, const QByteArray &data)
+{
+ QBuffer *buffer = new QBuffer;
+ buffer->setData(data);
+ buffer->open(QIODevice::ReadOnly);
+
+ QNetworkReply *reply = get(request, buffer);
+ buffer->setParent(reply);
+ return reply;
+}
+
+/*!
Sends an HTTP POST request to the destination specified by \a request
and returns a new QNetworkReply object opened for reading that will
contain the reply sent by the server. The contents of the \a data
@@ -793,6 +856,24 @@ QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, const
return reply;
}
+/*!
+ \overload
+
+ \since 6.8
+
+ Sends the POST request specified by \a request without a body and returns
+ a new QNetworkReply object.
+*/
+QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, std::nullptr_t nptr)
+{
+ Q_UNUSED(nptr);
+ QIODevice *dev = nullptr;
+
+ return d_func()->postProcess(createRequest(QNetworkAccessManager::PostOperation,
+ request,
+ dev));
+}
+
#if QT_CONFIG(http) || defined(Q_OS_WASM)
/*!
\since 4.8
@@ -877,6 +958,23 @@ QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, const
}
/*!
+ \overload
+
+ \since 6.8
+
+ Sends the PUT request specified by \a request without a body and returns
+ a new QNetworkReply object.
+*/
+
+QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, std::nullptr_t nptr)
+{
+ Q_UNUSED(nptr);
+ QIODevice *dev = nullptr;
+
+ return d_func()->postProcess(createRequest(QNetworkAccessManager::PutOperation, request, dev));
+}
+
+/*!
\since 4.6
Sends a request to delete the resource identified by the URL of \a request.
@@ -1110,8 +1208,8 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
}
#if QT_CONFIG(http) || defined (Q_OS_WASM)
- if (!req.transferTimeout())
- req.setTransferTimeout(transferTimeout());
+ if (req.transferTimeoutAsDuration() == 0ms)
+ req.setTransferTimeout(transferTimeoutAsDuration());
#endif
if (autoDeleteReplies()
@@ -1362,38 +1460,59 @@ void QNetworkAccessManager::setAutoDeleteReplies(bool shouldAutoDelete)
}
/*!
+ \fn int QNetworkAccessManager::transferTimeout() const
\since 5.15
Returns the timeout used for transfers, in milliseconds.
- This timeout is zero if setTransferTimeout() hasn't been
- called, which means that the timeout is not used.
+ \sa setTransferTimeout()
*/
-int QNetworkAccessManager::transferTimeout() const
+
+/*!
+ \fn void QNetworkAccessManager::setTransferTimeout(int timeout)
+ \since 5.15
+
+ Sets \a timeout as the transfer timeout in milliseconds.
+
+ \sa setTransferTimeout(std::chrono::milliseconds),
+ transferTimeout(), transferTimeoutAsDuration()
+*/
+
+/*!
+ \since 6.7
+
+ Returns the timeout duration after which the transfer is aborted if no
+ data is exchanged.
+
+ The default duration is zero, which means that the timeout is not used.
+
+ \sa setTransferTimeout(std::chrono::milliseconds)
+ */
+std::chrono::milliseconds QNetworkAccessManager::transferTimeoutAsDuration() const
{
return d_func()->transferTimeout;
}
/*!
- \since 5.15
+ \since 6.7
- Sets \a timeout as the transfer timeout in milliseconds.
+ Sets the timeout \a duration to abort the transfer if no data is exchanged.
Transfers are aborted if no bytes are transferred before
the timeout expires. Zero means no timer is set. If no
argument is provided, the timeout is
- QNetworkRequest::DefaultTransferTimeoutConstant. If this function
+ QNetworkRequest::DefaultTransferTimeout. If this function
is not called, the timeout is disabled and has the
value zero. The request-specific non-zero timeouts set for
the requests that are executed override this value. This means
that if QNetworkAccessManager has an enabled timeout, it needs
to be disabled to execute a request without a timeout.
- \sa transferTimeout()
-*/
-void QNetworkAccessManager::setTransferTimeout(int timeout)
+ \sa transferTimeoutAsDuration()
+ */
+void QNetworkAccessManager::setTransferTimeout(std::chrono::milliseconds duration)
{
- d_func()->transferTimeout = timeout;
+ d_func()->transferTimeout = duration;
}
void QNetworkAccessManagerPrivate::_q_replyFinished(QNetworkReply *reply)
@@ -1631,7 +1750,7 @@ QNetworkRequest QNetworkAccessManagerPrivate::prepareMultipart(const QNetworkReq
// add Content-Type header if not there already
if (!request.header(QNetworkRequest::ContentTypeHeader).isValid()) {
QByteArray contentType;
- contentType.reserve(34 + multiPart->d_func()->boundary.length());
+ contentType.reserve(34 + multiPart->d_func()->boundary.size());
contentType += "multipart/";
switch (multiPart->d_func()->contentType) {
case QHttpMultiPart::RelatedType:
@@ -1654,9 +1773,9 @@ QNetworkRequest QNetworkAccessManagerPrivate::prepareMultipart(const QNetworkReq
// add MIME-Version header if not there already (we must include the header
// if the message conforms to RFC 2045, see section 4 of that RFC)
- QByteArray mimeHeader("MIME-Version");
+ auto mimeHeader = "MIME-Version"_ba;
if (!request.hasRawHeader(mimeHeader))
- newRequest.setRawHeader(mimeHeader, QByteArray("1.0"));
+ newRequest.setRawHeader(mimeHeader, "1.0"_ba);
QIODevice *device = multiPart->d_func()->device;
if (!device->isReadable()) {
@@ -1681,13 +1800,13 @@ void QNetworkAccessManagerPrivate::ensureBackendPluginsLoaded()
{
Q_CONSTINIT static QBasicMutex mutex;
std::unique_lock locker(mutex);
- if (!loader())
+ if (!qnabfLoader())
return;
#if QT_CONFIG(library)
- loader->update();
+ qnabfLoader->update();
#endif
int index = 0;
- while (loader->instance(index))
+ while (qnabfLoader->instance(index))
++index;
}
diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h
index f0c99c03b8..0d069b2a9b 100644
--- a/src/network/access/qnetworkaccessmanager.h
+++ b/src/network/access/qnetworkaccessmanager.h
@@ -80,10 +80,14 @@ public:
QNetworkReply *head(const QNetworkRequest &request);
QNetworkReply *get(const QNetworkRequest &request);
+ QNetworkReply *get(const QNetworkRequest &request, QIODevice *data);
+ QNetworkReply *get(const QNetworkRequest &request, const QByteArray &data);
QNetworkReply *post(const QNetworkRequest &request, QIODevice *data);
QNetworkReply *post(const QNetworkRequest &request, const QByteArray &data);
+ QNetworkReply *post(const QNetworkRequest &request, std::nullptr_t nptr);
QNetworkReply *put(const QNetworkRequest &request, QIODevice *data);
QNetworkReply *put(const QNetworkRequest &request, const QByteArray &data);
+ QNetworkReply *put(const QNetworkRequest &request, std::nullptr_t nptr);
QNetworkReply *deleteResource(const QNetworkRequest &request);
QNetworkReply *sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QIODevice *data = nullptr);
QNetworkReply *sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, const QByteArray &data);
@@ -109,8 +113,14 @@ public:
bool autoDeleteReplies() const;
void setAutoDeleteReplies(bool autoDelete);
+ QT_NETWORK_INLINE_SINCE(6, 8)
int transferTimeout() const;
- void setTransferTimeout(int timeout = QNetworkRequest::DefaultTransferTimeoutConstant);
+ QT_NETWORK_INLINE_SINCE(6, 8)
+ void setTransferTimeout(int timeout);
+
+ std::chrono::milliseconds transferTimeoutAsDuration() const;
+ void setTransferTimeout(std::chrono::milliseconds duration =
+ QNetworkRequest::DefaultTransferTimeout);
Q_SIGNALS:
#ifndef QT_NO_NETWORKPROXY
@@ -147,6 +157,18 @@ private:
#endif
};
+#if QT_NETWORK_INLINE_IMPL_SINCE(6, 8)
+int QNetworkAccessManager::transferTimeout() const
+{
+ return int(transferTimeoutAsDuration().count());
+}
+
+void QNetworkAccessManager::setTransferTimeout(int timeout)
+{
+ setTransferTimeout(std::chrono::milliseconds(timeout));
+}
+#endif // INLINE_SINCE 6.8
+
QT_END_NAMESPACE
#endif
diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h
index 050da2060f..491a5acaa4 100644
--- a/src/network/access/qnetworkaccessmanager_p.h
+++ b/src/network/access/qnetworkaccessmanager_p.h
@@ -131,7 +131,7 @@ public:
bool autoDeleteReplies = false;
- int transferTimeout = 0;
+ std::chrono::milliseconds transferTimeout{0};
Q_DECLARE_PUBLIC(QNetworkAccessManager)
};
diff --git a/src/network/access/qnetworkcookie.cpp b/src/network/access/qnetworkcookie.cpp
index 7ddce06360..8ea5fdbe57 100644
--- a/src/network/access/qnetworkcookie.cpp
+++ b/src/network/access/qnetworkcookie.cpp
@@ -7,12 +7,14 @@
#include "qnetworkrequest.h"
#include "qnetworkreply.h"
#include "QtCore/qbytearray.h"
+#include "QtCore/qdatetime.h"
#include "QtCore/qdebug.h"
#include "QtCore/qlist.h"
#include "QtCore/qlocale.h"
#include <QtCore/qregularexpression.h>
#include "QtCore/qstring.h"
#include "QtCore/qstringlist.h"
+#include "QtCore/qtimezone.h"
#include "QtCore/qurl.h"
#include "QtNetwork/qhostaddress.h"
#include "private/qobject_p.h"
@@ -374,13 +376,13 @@ void QNetworkCookie::setValue(const QByteArray &value)
}
// ### move this to qnetworkcookie_p.h and share with qnetworkaccesshttpbackend
-static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &position, bool isNameValue)
+static QPair<QByteArray, QByteArray> nextField(QByteArrayView text, int &position, bool isNameValue)
{
// format is one of:
// (1) token
// (2) token = token
// (3) token = quoted-string
- const int length = text.length();
+ const int length = text.size();
position = nextNonWhitespace(text, position);
int semiColonPosition = text.indexOf(';', position);
@@ -394,11 +396,11 @@ static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &posi
equalsPosition = semiColonPosition; //no '=' means there is an attribute-name but no attribute-value
}
- QByteArray first = text.mid(position, equalsPosition - position).trimmed();
+ QByteArray first = text.mid(position, equalsPosition - position).trimmed().toByteArray();
QByteArray second;
int secondLength = semiColonPosition - equalsPosition - 1;
if (secondLength > 0)
- second = text.mid(equalsPosition + 1, secondLength).trimmed();
+ second = text.mid(equalsPosition + 1, secondLength).trimmed().toByteArray();
position = semiColonPosition;
return qMakePair(first, second);
@@ -441,29 +443,33 @@ static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &posi
*/
namespace {
-QByteArray sameSiteToRawString(QNetworkCookie::SameSite samesite)
+
+constexpr QByteArrayView sameSiteNone() noexcept { return "None"; }
+constexpr QByteArrayView sameSiteLax() noexcept { return "Lax"; }
+constexpr QByteArrayView sameSiteStrict() noexcept { return "Strict"; }
+
+QByteArrayView sameSiteToRawString(QNetworkCookie::SameSite samesite) noexcept
{
switch (samesite) {
case QNetworkCookie::SameSite::None:
- return QByteArrayLiteral("None");
+ return sameSiteNone();
case QNetworkCookie::SameSite::Lax:
- return QByteArrayLiteral("Lax");
+ return sameSiteLax();
case QNetworkCookie::SameSite::Strict:
- return QByteArrayLiteral("Strict");
+ return sameSiteStrict();
case QNetworkCookie::SameSite::Default:
break;
}
- return QByteArray();
+ return QByteArrayView();
}
-QNetworkCookie::SameSite sameSiteFromRawString(QByteArray str)
+QNetworkCookie::SameSite sameSiteFromRawString(QByteArrayView str) noexcept
{
- str = str.toLower();
- if (str == QByteArrayLiteral("none"))
+ if (str.compare(sameSiteNone(), Qt::CaseInsensitive) == 0)
return QNetworkCookie::SameSite::None;
- if (str == QByteArrayLiteral("lax"))
+ if (str.compare(sameSiteLax(), Qt::CaseInsensitive) == 0)
return QNetworkCookie::SameSite::Lax;
- if (str == QByteArrayLiteral("strict"))
+ if (str.compare(sameSiteStrict(), Qt::CaseInsensitive) == 0)
return QNetworkCookie::SameSite::Strict;
return QNetworkCookie::SameSite::Default;
}
@@ -574,11 +580,11 @@ static inline bool isValueSeparator(char c)
static inline bool isWhitespace(char c)
{ return c == ' ' || c == '\t'; }
-static bool checkStaticArray(int &val, const QByteArray &dateString, int at, const char *array, int size)
+static bool checkStaticArray(int &val, QByteArrayView dateString, int at, const char *array, int size)
{
if (dateString[at] < 'a' || dateString[at] > 'z')
return false;
- if (val == -1 && dateString.length() >= at + 3) {
+ if (val == -1 && dateString.size() >= at + 3) {
int j = 0;
int i = 0;
while (i <= size) {
@@ -621,7 +627,7 @@ static bool checkStaticArray(int &val, const QByteArray &dateString, int at, con
Or in their own words:
"} // else what the hell is this."
*/
-static QDateTime parseDateString(const QByteArray &dateString)
+static QDateTime parseDateString(QByteArrayView dateString)
{
QTime time;
// placeholders for values when we are not sure it is a year, month or day
@@ -636,7 +642,7 @@ static QDateTime parseDateString(const QByteArray &dateString)
u"(\\d\\d?):(\\d\\d?)(?::(\\d\\d?)(?:\\.(\\d{1,3}))?)?(?:\\s*(am|pm))?"_s);
int at = 0;
- while (at < dateString.length()) {
+ while (at < dateString.size()) {
#ifdef PARSEDATESTRINGDEBUG
qDebug() << dateString.mid(at);
#endif
@@ -677,20 +683,20 @@ static QDateTime parseDateString(const QByteArray &dateString)
&& (dateString[at - 1] == 't')))) {
int end = 1;
- while (end < 5 && dateString.length() > at+end
+ while (end < 5 && dateString.size() > at+end
&& dateString[at + end] >= '0' && dateString[at + end] <= '9')
++end;
int minutes = 0;
int hours = 0;
switch (end - 1) {
case 4:
- minutes = atoi(dateString.mid(at + 3, 2).constData());
+ minutes = dateString.mid(at + 3, 2).toInt();
Q_FALLTHROUGH();
case 2:
- hours = atoi(dateString.mid(at + 1, 2).constData());
+ hours = dateString.mid(at + 1, 2).toInt();
break;
case 1:
- hours = atoi(dateString.mid(at + 1, 1).constData());
+ hours = dateString.mid(at + 1, 1).toInt();
break;
default:
at += end;
@@ -709,7 +715,7 @@ static QDateTime parseDateString(const QByteArray &dateString)
// Time
if (isNum && time.isNull()
- && dateString.length() >= at + 3
+ && dateString.size() >= at + 3
&& (dateString[at + 2] == ':' || dateString[at + 1] == ':')) {
// While the date can be found all over the string the format
// for the time is set and a nice regexp can be used.
@@ -737,11 +743,11 @@ static QDateTime parseDateString(const QByteArray &dateString)
// 4 digit Year
if (isNum
&& year == -1
- && dateString.length() > at + 3) {
+ && dateString.size() > at + 3) {
if (isNumber(dateString[at + 1])
&& isNumber(dateString[at + 2])
&& isNumber(dateString[at + 3])) {
- year = atoi(dateString.mid(at, 4).constData());
+ year = dateString.mid(at, 4).toInt();
at += 4;
#ifdef PARSEDATESTRINGDEBUG
qDebug() << "Year:" << year;
@@ -754,10 +760,10 @@ static QDateTime parseDateString(const QByteArray &dateString)
// Could be month, day or year
if (isNum) {
int length = 1;
- if (dateString.length() > at + 1
+ if (dateString.size() > at + 1
&& isNumber(dateString[at + 1]))
++length;
- int x = atoi(dateString.mid(at, length).constData());
+ int x = dateString.mid(at, length).toInt();
if (year == -1 && (x > 31 || x == 0)) {
year = x;
} else {
@@ -906,11 +912,11 @@ static QDateTime parseDateString(const QByteArray &dateString)
if (!date.isValid())
date = QDate(day + y2k, month, year);
- QDateTime dateTime(date, time, Qt::UTC);
+ QDateTime dateTime(date, time, QTimeZone::UTC);
- if (zoneOffset != -1) {
+ if (zoneOffset != -1)
dateTime = dateTime.addSecs(zoneOffset);
- }
+
if (!dateTime.isValid())
return QDateTime();
return dateTime;
@@ -926,19 +932,19 @@ static QDateTime parseDateString(const QByteArray &dateString)
cookie that is parsed.
\sa toRawForm()
+ \note In Qt versions prior to 6.7, this function took QByteArray only.
*/
-QList<QNetworkCookie> QNetworkCookie::parseCookies(const QByteArray &cookieString)
+QList<QNetworkCookie> QNetworkCookie::parseCookies(QByteArrayView cookieString)
{
// cookieString can be a number of set-cookie header strings joined together
// by \n, parse each line separately.
QList<QNetworkCookie> cookies;
- QList<QByteArray> list = cookieString.split('\n');
- for (int a = 0; a < list.size(); a++)
- cookies += QNetworkCookiePrivate::parseSetCookieHeaderLine(list.at(a));
+ for (auto s : QLatin1StringView(cookieString).tokenize('\n'_L1))
+ cookies += QNetworkCookiePrivate::parseSetCookieHeaderLine(s);
return cookies;
}
-QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByteArray &cookieString)
+QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(QByteArrayView cookieString)
{
// According to http://wp.netscape.com/newsref/std/cookie_spec.html,<
// the Set-Cookie response header is of the format:
@@ -953,7 +959,7 @@ QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByt
const QDateTime now = QDateTime::currentDateTimeUtc();
int position = 0;
- const int length = cookieString.length();
+ const int length = cookieString.size();
while (position < length) {
QNetworkCookie cookie;
@@ -971,28 +977,27 @@ QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByt
case ';':
// new field in the cookie
field = nextField(cookieString, position, false);
- field.first = field.first.toLower(); // everything but the NAME=VALUE is case-insensitive
- if (field.first == "expires") {
- position -= field.second.length();
+ if (field.first.compare("expires", Qt::CaseInsensitive) == 0) {
+ position -= field.second.size();
int end;
for (end = position; end < length; ++end)
if (isValueSeparator(cookieString.at(end)))
break;
- QByteArray dateString = cookieString.mid(position, end - position).trimmed();
+ QByteArray dateString = cookieString.mid(position, end - position).trimmed().toByteArray().toLower();
position = end;
- QDateTime dt = parseDateString(dateString.toLower());
+ QDateTime dt = parseDateString(dateString);
if (dt.isValid())
cookie.setExpirationDate(dt);
//if unparsed, ignore the attribute but not the whole cookie (RFC6265 section 5.2.1)
- } else if (field.first == "domain") {
- QByteArray rawDomain = field.second;
+ } else if (field.first.compare("domain", Qt::CaseInsensitive) == 0) {
+ QByteArrayView rawDomain = field.second;
//empty domain should be ignored (RFC6265 section 5.2.3)
if (!rawDomain.isEmpty()) {
- QString maybeLeadingDot;
+ QLatin1StringView maybeLeadingDot;
if (rawDomain.startsWith('.')) {
- maybeLeadingDot = u'.';
+ maybeLeadingDot = "."_L1;
rawDomain = rawDomain.mid(1);
}
@@ -1007,7 +1012,7 @@ QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByt
return result;
}
}
- } else if (field.first == "max-age") {
+ } else if (field.first.compare("max-age", Qt::CaseInsensitive) == 0) {
bool ok = false;
int secs = field.second.toInt(&ok);
if (ok) {
@@ -1019,7 +1024,7 @@ QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByt
}
}
//if unparsed, ignore the attribute but not the whole cookie (RFC6265 section 5.2.2)
- } else if (field.first == "path") {
+ } else if (field.first.compare("path", Qt::CaseInsensitive) == 0) {
if (field.second.startsWith('/')) {
// ### we should treat cookie paths as an octet sequence internally
// However RFC6265 says we should assume UTF-8 for presentation as a string
@@ -1029,11 +1034,11 @@ QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByt
// and also IETF test case path0030 which has valid and empty path in the same cookie
cookie.setPath(QString());
}
- } else if (field.first == "secure") {
+ } else if (field.first.compare("secure", Qt::CaseInsensitive) == 0) {
cookie.setSecure(true);
- } else if (field.first == "httponly") {
+ } else if (field.first.compare("httponly", Qt::CaseInsensitive) == 0) {
cookie.setHttpOnly(true);
- } else if (field.first == "samesite") {
+ } else if (field.first.compare("samesite", Qt::CaseInsensitive) == 0) {
cookie.setSameSitePolicy(sameSiteFromRawString(field.second));
} else {
// ignore unknown fields in the cookie (RFC6265 section 5.2, rule 6)
diff --git a/src/network/access/qnetworkcookie.h b/src/network/access/qnetworkcookie.h
index d4f4942288..aed9c8af12 100644
--- a/src/network/access/qnetworkcookie.h
+++ b/src/network/access/qnetworkcookie.h
@@ -75,7 +75,10 @@ public:
bool hasSameIdentifier(const QNetworkCookie &other) const;
void normalize(const QUrl &url);
+#if QT_NETWORK_REMOVED_SINCE(6, 7)
static QList<QNetworkCookie> parseCookies(const QByteArray &cookieString);
+#endif
+ static QList<QNetworkCookie> parseCookies(QByteArrayView cookieString);
private:
QSharedDataPointer<QNetworkCookiePrivate> d;
diff --git a/src/network/access/qnetworkcookie_p.h b/src/network/access/qnetworkcookie_p.h
index cfbd846212..ce4378fd64 100644
--- a/src/network/access/qnetworkcookie_p.h
+++ b/src/network/access/qnetworkcookie_p.h
@@ -25,7 +25,7 @@ class QNetworkCookiePrivate: public QSharedData
{
public:
QNetworkCookiePrivate() = default;
- static QList<QNetworkCookie> parseSetCookieHeaderLine(const QByteArray &cookieString);
+ static QList<QNetworkCookie> parseSetCookieHeaderLine(QByteArrayView cookieString);
QDateTime expirationDate;
QString domain;
@@ -43,13 +43,13 @@ static inline bool isLWS(char c)
return c == ' ' || c == '\t' || c == '\r' || c == '\n';
}
-static int nextNonWhitespace(const QByteArray &text, int from)
+static int nextNonWhitespace(QByteArrayView text, int from)
{
// RFC 2616 defines linear whitespace as:
// LWS = [CRLF] 1*( SP | HT )
// We ignore the fact that CRLF must come as a pair at this point
// It's an invalid HTTP header if that happens.
- while (from < text.length()) {
+ while (from < text.size()) {
if (isLWS(text.at(from)))
++from;
else
@@ -57,7 +57,7 @@ static int nextNonWhitespace(const QByteArray &text, int from)
}
// reached the end
- return text.length();
+ return text.size();
}
QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkcookiejar.cpp b/src/network/access/qnetworkcookiejar.cpp
index d63185fdd0..82746f91b1 100644
--- a/src/network/access/qnetworkcookiejar.cpp
+++ b/src/network/access/qnetworkcookiejar.cpp
@@ -112,11 +112,11 @@ void QNetworkCookieJar::setAllCookies(const QList<QNetworkCookie> &cookieList)
d->allCookies = cookieList;
}
-static inline bool isParentPath(const QString &path, const QString &reference)
+static inline bool isParentPath(QStringView path, QStringView reference)
{
if ((path.isEmpty() && reference == "/"_L1) || path.startsWith(reference)) {
//The cookie-path and the request-path are identical.
- if (path.length() == reference.length())
+ if (path.size() == reference.size())
return true;
//The cookie-path is a prefix of the request-path, and the last
//character of the cookie-path is %x2F ("/").
@@ -125,18 +125,18 @@ static inline bool isParentPath(const QString &path, const QString &reference)
//The cookie-path is a prefix of the request-path, and the first
//character of the request-path that is not included in the cookie-
//path is a %x2F ("/") character.
- if (path.at(reference.length()) == u'/')
+ if (path.at(reference.size()) == u'/')
return true;
}
return false;
}
-static inline bool isParentDomain(const QString &domain, const QString &reference)
+static inline bool isParentDomain(QStringView domain, QStringView reference)
{
if (!reference.startsWith(u'.'))
return domain == reference;
- return domain.endsWith(reference) || domain == QStringView{reference}.mid(1);
+ return domain.endsWith(reference) || domain == reference.mid(1);
}
/*!
@@ -200,49 +200,38 @@ QList<QNetworkCookie> QNetworkCookieJar::cookiesForUrl(const QUrl &url) const
Q_D(const QNetworkCookieJar);
const QDateTime now = QDateTime::currentDateTimeUtc();
QList<QNetworkCookie> result;
- bool isEncrypted = url.scheme() == "https"_L1;
+ const bool isEncrypted = url.scheme() == "https"_L1;
// scan our cookies for something that matches
- QList<QNetworkCookie>::ConstIterator it = d->allCookies.constBegin(),
- end = d->allCookies.constEnd();
- for ( ; it != end; ++it) {
- if (!isParentDomain(url.host(), it->domain()))
+ for (const auto &cookie : std::as_const(d->allCookies)) {
+ if (!isEncrypted && cookie.isSecure())
continue;
- if (!isParentPath(url.path(), it->path()))
+ if (!cookie.isSessionCookie() && cookie.expirationDate() < now)
continue;
- if (!(*it).isSessionCookie() && (*it).expirationDate() < now)
+ const QString urlHost = url.host();
+ const QString cookieDomain = cookie.domain();
+ if (!isParentDomain(urlHost, cookieDomain))
continue;
- if ((*it).isSecure() && !isEncrypted)
+ if (!isParentPath(url.path(), cookie.path()))
continue;
- QString domain = it->domain();
+ QStringView domain = cookieDomain;
if (domain.startsWith(u'.')) /// Qt6?: remove when compliant with RFC6265
- domain = domain.mid(1);
+ domain = domain.sliced(1);
#if QT_CONFIG(topleveldomain)
- if (qIsEffectiveTLD(domain) && url.host() != domain)
+ if (urlHost != domain && qIsEffectiveTLD(domain))
continue;
#else
- if (!domain.contains(u'.') && url.host() != domain)
+ if (!domain.contains(u'.') && urlHost != domain)
continue;
#endif // topleveldomain
- // insert this cookie into result, sorted by path
- QList<QNetworkCookie>::Iterator insertIt = result.begin();
- while (insertIt != result.end()) {
- if (insertIt->path().length() < it->path().length()) {
- // insert here
- insertIt = result.insert(insertIt, *it);
- break;
- } else {
- ++insertIt;
- }
- }
-
- // this is the shortest path yet, just append
- if (insertIt == result.end())
- result += *it;
+ result += cookie;
}
+ auto longerPath = [](const auto &c1, const auto &c2)
+ { return c1.path().size() > c2.path().size(); };
+ std::sort(result.begin(), result.end(), longerPath);
return result;
}
@@ -299,12 +288,11 @@ bool QNetworkCookieJar::updateCookie(const QNetworkCookie &cookie)
bool QNetworkCookieJar::deleteCookie(const QNetworkCookie &cookie)
{
Q_D(QNetworkCookieJar);
- QList<QNetworkCookie>::Iterator it;
- for (it = d->allCookies.begin(); it != d->allCookies.end(); ++it) {
- if (it->hasSameIdentifier(cookie)) {
- d->allCookies.erase(it);
- return true;
- }
+ const auto it = std::find_if(d->allCookies.cbegin(), d->allCookies.cend(),
+ [&cookie](const auto &c) { return c.hasSameIdentifier(cookie); });
+ if (it != d->allCookies.cend()) {
+ d->allCookies.erase(it);
+ return true;
}
return false;
}
@@ -317,13 +305,14 @@ bool QNetworkCookieJar::deleteCookie(const QNetworkCookie &cookie)
*/
bool QNetworkCookieJar::validateCookie(const QNetworkCookie &cookie, const QUrl &url) const
{
- QString domain = cookie.domain();
+ const QString cookieDomain = cookie.domain();
+ QStringView domain = cookieDomain;
const QString host = url.host();
if (!isParentDomain(domain, host) && !isParentDomain(host, domain))
return false; // not accepted
if (domain.startsWith(u'.'))
- domain = domain.mid(1);
+ domain = domain.sliced(1);
// We shouldn't reject if:
// "[...] the domain-attribute is identical to the canonicalized request-host"
diff --git a/src/network/access/qnetworkdiskcache.cpp b/src/network/access/qnetworkdiskcache.cpp
index cf07f51d4c..c883a61886 100644
--- a/src/network/access/qnetworkdiskcache.cpp
+++ b/src/network/access/qnetworkdiskcache.cpp
@@ -12,16 +12,14 @@
#include <qdir.h>
#include <qdatastream.h>
#include <qdatetime.h>
-#include <qdiriterator.h>
+#include <qdirlisting.h>
#include <qurl.h>
#include <qcryptographichash.h>
#include <qdebug.h>
-#include <QMultiMap>
#include <memory>
#define CACHE_POSTFIX ".d"_L1
-#define PREPARED_SLASH "prepared/"_L1
#define CACHE_VERSION 8
#define DATA_DIR "data"_L1
@@ -173,13 +171,9 @@ QIODevice *QNetworkDiskCache::prepare(const QNetworkCacheMetaData &metaData)
cacheItem->data.open(QBuffer::ReadWrite);
device = &(cacheItem->data);
} else {
- QString templateName = d->tmpCacheFileName();
- QT_TRY {
- cacheItem->file = new QTemporaryFile(templateName, &cacheItem->data);
- } QT_CATCH(...) {
- cacheItem->file = nullptr;
- }
- if (!cacheItem->file || !cacheItem->file->open()) {
+ QString fileName = d->cacheFileName(cacheItem->metaData.url());
+ cacheItem->file = new(std::nothrow) QSaveFile(fileName, &cacheItem->data);
+ if (!cacheItem->file || !cacheItem->file->open(QFileDevice::WriteOnly)) {
qWarning("QNetworkDiskCache::prepare() unable to open temporary file");
cacheItem.reset();
return nullptr;
@@ -219,7 +213,6 @@ void QNetworkDiskCache::insert(QIODevice *device)
void QNetworkDiskCachePrivate::prepareLayout()
{
QDir helper;
- helper.mkpath(cacheDirectory + PREPARED_SLASH);
//Create directory and subdirectories 0-F
helper.mkpath(dataDirectory);
@@ -248,9 +241,8 @@ void QNetworkDiskCachePrivate::storeItem(QCacheItem *cacheItem)
currentCacheSize = q->expire();
if (!cacheItem->file) {
- QString templateName = tmpCacheFileName();
- cacheItem->file = new QTemporaryFile(templateName, &cacheItem->data);
- if (cacheItem->file->open()) {
+ cacheItem->file = new QSaveFile(fileName, &cacheItem->data);
+ if (cacheItem->file->open(QFileDevice::WriteOnly)) {
cacheItem->writeHeader(cacheItem->file);
cacheItem->writeCompressedData(cacheItem->file);
}
@@ -258,13 +250,15 @@ void QNetworkDiskCachePrivate::storeItem(QCacheItem *cacheItem)
if (cacheItem->file
&& cacheItem->file->isOpen()
- && cacheItem->file->error() == QFile::NoError) {
- cacheItem->file->setAutoRemove(false);
- // ### use atomic rename rather then remove & rename
- if (cacheItem->file->rename(fileName))
- currentCacheSize += cacheItem->file->size();
- else
- cacheItem->file->setAutoRemove(true);
+ && cacheItem->file->error() == QFileDevice::NoError) {
+ // We have to call size() here instead of inside the if-body because
+ // commit() invalidates the file-engine, and size() will create a new
+ // one, pointing at an empty filename.
+ qint64 size = cacheItem->file->size();
+ if (cacheItem->file->commit())
+ currentCacheSize += size;
+ // Delete and unset the QSaveFile, it's invalid now.
+ delete std::exchange(cacheItem->file, nullptr);
}
if (cacheItem->metaData.url() == lastItem.metaData.url())
lastItem.reset();
@@ -483,47 +477,45 @@ qint64 QNetworkDiskCache::expire()
// close file handle to prevent "in use" error when QFile::remove() is called
d->lastItem.reset();
- QDir::Filters filters = QDir::AllDirs | QDir:: Files | QDir::NoDotAndDotDot;
- QDirIterator it(cacheDirectory(), filters, QDirIterator::Subdirectories);
+ const QDir::Filters filters = QDir::AllDirs | QDir:: Files | QDir::NoDotAndDotDot;
- QMultiMap<QDateTime, QString> cacheItems;
+ struct CacheItem
+ {
+ std::chrono::milliseconds msecs;
+ QString path;
+ qint64 size = 0;
+ };
+ std::vector<CacheItem> cacheItems;
qint64 totalSize = 0;
- while (it.hasNext()) {
- QFileInfo info = it.nextFileInfo();
- QString path = info.filePath();
- QString fileName = info.fileName();
- if (fileName.endsWith(CACHE_POSTFIX)) {
- const QDateTime birthTime = info.fileTime(QFile::FileBirthTime);
- cacheItems.insert(birthTime.isValid() ? birthTime
- : info.fileTime(QFile::FileMetadataChangeTime), path);
- totalSize += info.size();
- }
+ using F = QDirListing::IteratorFlag;
+ for (const auto &dirEntry : QDirListing(cacheDirectory(), filters, F::Recursive)) {
+ if (!dirEntry.fileName().endsWith(CACHE_POSTFIX))
+ continue;
+
+ const QFileInfo &info = dirEntry.fileInfo();
+ QDateTime fileTime = info.birthTime(QTimeZone::UTC);
+ if (!fileTime.isValid())
+ fileTime = info.metadataChangeTime(QTimeZone::UTC);
+ const std::chrono::milliseconds msecs{fileTime.toMSecsSinceEpoch()};
+ const qint64 size = info.size();
+ cacheItems.push_back(CacheItem{msecs, info.filePath(), size});
+ totalSize += size;
}
+ const qint64 goal = (maximumCacheSize() * 9) / 10;
+ if (totalSize < goal)
+ return totalSize; // Nothing to do
+
+ auto byFileTime = [&](const auto &a, const auto &b) { return a.msecs < b.msecs; };
+ std::sort(cacheItems.begin(), cacheItems.end(), byFileTime);
+
[[maybe_unused]] int removedFiles = 0; // used under QNETWORKDISKCACHE_DEBUG
- qint64 goal = (maximumCacheSize() * 9) / 10;
- QMultiMap<QDateTime, QString>::const_iterator i = cacheItems.constBegin();
- while (i != cacheItems.constEnd()) {
+ for (const CacheItem &cached : cacheItems) {
+ QFile::remove(cached.path);
+ ++removedFiles;
+ totalSize -= cached.size;
if (totalSize < goal)
break;
- QString name = i.value();
- QFile file(name);
-
- if (name.contains(PREPARED_SLASH)) {
- for (QCacheItem *item : qAsConst(d->inserting)) {
- if (item && item->file && item->file->fileName() == name) {
- delete item->file;
- item->file = nullptr;
- break;
- }
- }
- }
-
- qint64 size = file.size();
- file.remove();
- totalSize -= size;
- ++removedFiles;
- ++i;
}
#if defined(QNETWORKDISKCACHE_DEBUG)
if (removedFiles > 0) {
@@ -563,18 +555,12 @@ QString QNetworkDiskCachePrivate::uniqueFileName(const QUrl &url)
// convert sha1 to base36 form and return first 8 bytes for use as string
const QByteArray id = QByteArray::number(*(qlonglong*)hash.data(), 36).left(8);
// generates <one-char subdir>/<8-char filename.d>
- uint code = (uint)id.at(id.length()-1) % 16;
+ uint code = (uint)id.at(id.size()-1) % 16;
QString pathFragment = QString::number(code, 16) + u'/' + QLatin1StringView(id) + CACHE_POSTFIX;
return pathFragment;
}
-QString QNetworkDiskCachePrivate::tmpCacheFileName() const
-{
- //The subdirectory is presumed to be already read for use.
- return cacheDirectory + PREPARED_SLASH + "XXXXXX"_L1 + CACHE_POSTFIX;
-}
-
/*!
Generates fully qualified path of cached resource from a URL.
*/
@@ -625,7 +611,7 @@ enum
CurrentCacheVersion = CACHE_VERSION
};
-void QCacheItem::writeHeader(QFile *device) const
+void QCacheItem::writeHeader(QFileDevice *device) const
{
QDataStream out(device);
@@ -637,7 +623,7 @@ void QCacheItem::writeHeader(QFile *device) const
out << compressed;
}
-void QCacheItem::writeCompressedData(QFile *device) const
+void QCacheItem::writeCompressedData(QFileDevice *device) const
{
QDataStream out(device);
@@ -648,7 +634,7 @@ void QCacheItem::writeCompressedData(QFile *device) const
Returns \c false if the file is a cache file,
but is an older version and should be removed otherwise true.
*/
-bool QCacheItem::read(QFile *device, bool readData)
+bool QCacheItem::read(QFileDevice *device, bool readData)
{
reset();
@@ -687,7 +673,7 @@ bool QCacheItem::read(QFile *device, bool readData)
if (!device->fileName().endsWith(expectedFilename))
return false;
- return metaData.isValid();
+ return metaData.isValid() && !metaData.rawHeaders().isEmpty();
}
QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkdiskcache_p.h b/src/network/access/qnetworkdiskcache_p.h
index 05c97dff08..826f0a1d7d 100644
--- a/src/network/access/qnetworkdiskcache_p.h
+++ b/src/network/access/qnetworkdiskcache_p.h
@@ -20,20 +20,16 @@
#include <qbuffer.h>
#include <qhash.h>
-#include <qtemporaryfile.h>
+#include <qsavefile.h>
QT_REQUIRE_CONFIG(networkdiskcache);
QT_BEGIN_NAMESPACE
-class QFile;
-
class QCacheItem
{
public:
- QCacheItem() : file(nullptr)
- {
- }
+ QCacheItem() = default;
~QCacheItem()
{
reset();
@@ -41,7 +37,7 @@ public:
QNetworkCacheMetaData metaData;
QBuffer data;
- QTemporaryFile *file;
+ QSaveFile *file = nullptr;
inline qint64 size() const
{ return file ? file->size() : data.size(); }
@@ -51,9 +47,9 @@ public:
delete file;
file = nullptr;
}
- void writeHeader(QFile *device) const;
- void writeCompressedData(QFile *device) const;
- bool read(QFile *device, bool readData);
+ void writeHeader(QFileDevice *device) const;
+ void writeCompressedData(QFileDevice *device) const;
+ bool read(QFileDevice *device, bool readData);
bool canCompress() const;
};
@@ -69,7 +65,6 @@ public:
static QString uniqueFileName(const QUrl &url);
QString cacheFileName(const QUrl &url) const;
- QString tmpCacheFileName() const;
bool removeFile(const QString &file);
void storeItem(QCacheItem *item);
void prepareLayout();
diff --git a/src/network/access/qnetworkreply.cpp b/src/network/access/qnetworkreply.cpp
index 8095a4591d..9334b01de6 100644
--- a/src/network/access/qnetworkreply.cpp
+++ b/src/network/access/qnetworkreply.cpp
@@ -42,7 +42,7 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
itself.
QNetworkReply is a sequential-access QIODevice, which means that
- once data is read from the object, it no longer kept by the
+ once data is read from the object, it is no longer kept by the
device. It is therefore the application's responsibility to keep
this data if it needs to. Whenever more data is received from the
network and processed, the readyRead() signal is emitted.
@@ -331,7 +331,7 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
processing. After this signal is emitted, there will be no more
updates to the reply's data or metadata.
- Unless close() or abort() have been called, the reply will be still be opened
+ Unless close() or abort() have been called, the reply will still be opened
for reading, so the data can be retrieved by calls to read() or
readAll(). In particular, if no calls to read() were made as a
result of readyRead(), a call to readAll() will retrieve the full
@@ -612,8 +612,9 @@ QVariant QNetworkReply::header(QNetworkRequest::KnownHeaders header) const
the remote server
\sa rawHeader()
+ \note In Qt versions prior to 6.7, this function took QByteArray only.
*/
-bool QNetworkReply::hasRawHeader(const QByteArray &headerName) const
+bool QNetworkReply::hasRawHeader(QAnyStringView headerName) const
{
Q_D(const QNetworkReply);
return d->findRawHeader(headerName) != d->rawHeaders.constEnd();
@@ -627,13 +628,12 @@ bool QNetworkReply::hasRawHeader(const QByteArray &headerName) const
header field.
\sa setRawHeader(), hasRawHeader(), header()
+ \note In Qt versions prior to 6.7, this function took QByteArray only.
*/
-QByteArray QNetworkReply::rawHeader(const QByteArray &headerName) const
+QByteArray QNetworkReply::rawHeader(QAnyStringView headerName) const
{
Q_D(const QNetworkReply);
- QNetworkHeadersPrivate::RawHeadersList::ConstIterator it =
- d->findRawHeader(headerName);
- if (it != d->rawHeaders.constEnd())
+ if (const auto it = d->findRawHeader(headerName); it != d->rawHeaders.constEnd())
return it->second;
return QByteArray();
}
diff --git a/src/network/access/qnetworkreply.h b/src/network/access/qnetworkreply.h
index 2a505b07c0..390a6f2f51 100644
--- a/src/network/access/qnetworkreply.h
+++ b/src/network/access/qnetworkreply.h
@@ -97,9 +97,15 @@ public:
QVariant header(QNetworkRequest::KnownHeaders header) const;
// raw headers:
+#if QT_NETWORK_REMOVED_SINCE(6, 7)
bool hasRawHeader(const QByteArray &headerName) const;
+#endif
+ bool hasRawHeader(QAnyStringView headerName) const;
QList<QByteArray> rawHeaderList() const;
+#if QT_NETWORK_REMOVED_SINCE(6, 7)
QByteArray rawHeader(const QByteArray &headerName) const;
+#endif
+ QByteArray rawHeader(QAnyStringView headerName) const;
typedef QPair<QByteArray, QByteArray> RawHeaderPair;
const QList<RawHeaderPair>& rawHeaderPairs() const;
diff --git a/src/network/access/qnetworkreplyfileimpl.cpp b/src/network/access/qnetworkreplyfileimpl.cpp
index e1022afcfa..e6208a5c85 100644
--- a/src/network/access/qnetworkreplyfileimpl.cpp
+++ b/src/network/access/qnetworkreplyfileimpl.cpp
@@ -57,9 +57,10 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QNetworkAccessManager *manager, con
// we handle only local files
QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Request for opening non-local file %1").arg(url.toString());
setError(QNetworkReply::ProtocolInvalidOperationError, msg);
+ setFinished(true); // We're finished, will emit finished() after ctor is done.
QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection,
Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProtocolInvalidOperationError));
- fileOpenFinished(false);
+ QMetaObject::invokeMethod(this, &QNetworkReplyFileImpl::fileOpenFinished, Qt::QueuedConnection, false);
return;
}
#endif
diff --git a/src/network/access/qnetworkreplyfileimpl_p.h b/src/network/access/qnetworkreplyfileimpl_p.h
index 20a09983ac..6413903d8f 100644
--- a/src/network/access/qnetworkreplyfileimpl_p.h
+++ b/src/network/access/qnetworkreplyfileimpl_p.h
@@ -1,8 +1,8 @@
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#ifndef QNETWORKREPLYFILEIMPL_H
-#define QNETWORKREPLYFILEIMPL_H
+#ifndef QNETWORKREPLYFILEIMPL_P_H
+#define QNETWORKREPLYFILEIMPL_P_H
//
// W A R N I N G
@@ -19,7 +19,9 @@
#include "qnetworkreply.h"
#include "qnetworkreply_p.h"
#include "qnetworkaccessmanager.h"
+
#include <QFile>
+#include <QtCore/qpointer.h>
#include <private/qabstractfileengine_p.h>
QT_BEGIN_NAMESPACE
@@ -65,4 +67,4 @@ QT_END_NAMESPACE
QT_DECL_METATYPE_EXTERN_TAGGED(QNetworkRequest::KnownHeaders,
QNetworkRequest__KnownHeaders, Q_NETWORK_EXPORT)
-#endif // QNETWORKREPLYFILEIMPL_H
+#endif // QNETWORKREPLYFILEIMPL_P_H
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
index 08ef1866bd..1eee98f834 100644
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
@@ -21,6 +21,7 @@
#include "QtCore/qcoreapplication.h"
#include <QtCore/private/qthread_p.h>
+#include <QtCore/private/qtools_p.h>
#include "qnetworkcookiejar.h"
#include "qnetconmonitor_p.h"
@@ -32,17 +33,17 @@
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
+using namespace QtMiscUtils;
+using namespace std::chrono_literals;
class QNetworkProxy;
-static inline bool isSeparator(char c)
-{
- static const char separators[] = "()<>@,;:\\\"/[]?={}";
- return isLWS(c) || strchr(separators, c) != nullptr;
-}
+static inline QByteArray rangeName() { return "Range"_ba; }
+static inline QByteArray cacheControlName() { return "Cache-Control"_ba; }
+static constexpr QByteArrayView bytesEqualPrefix() noexcept { return "bytes="; }
// ### merge with nextField in cookiejar.cpp
-static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &header)
+static QHash<QByteArray, QByteArray> parseHttpOptionHeader(QByteArrayView header)
{
// The HTTP header is of the form:
// header = #1(directives)
@@ -54,7 +55,7 @@ static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &hea
while (true) {
// skip spaces
pos = nextNonWhitespace(header, pos);
- if (pos == header.length())
+ if (pos == header.size())
return result; // end of parsing
// pos points to a non-whitespace
@@ -68,36 +69,36 @@ static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &hea
// of the header, whichever comes first
int end = comma;
if (end == -1)
- end = header.length();
+ end = header.size();
if (equal != -1 && end > equal)
end = equal; // equal sign comes before comma/end
- QByteArray key = QByteArray(header.constData() + pos, end - pos).trimmed().toLower();
+ const auto key = header.sliced(pos, end - pos).trimmed();
pos = end + 1;
if (uint(equal) < uint(comma)) {
// case: token "=" (token | quoted-string)
// skip spaces
pos = nextNonWhitespace(header, pos);
- if (pos == header.length())
+ if (pos == header.size())
// huh? Broken header
return result;
QByteArray value;
- value.reserve(header.length() - pos);
+ value.reserve(header.size() - pos);
if (header.at(pos) == '"') {
// case: quoted-string
// quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
// qdtext = <any TEXT except <">>
// quoted-pair = "\" CHAR
++pos;
- while (pos < header.length()) {
+ while (pos < header.size()) {
char c = header.at(pos);
if (c == '"') {
// end of quoted text
break;
} else if (c == '\\') {
++pos;
- if (pos >= header.length())
+ if (pos >= header.size())
// broken header
return result;
c = header.at(pos);
@@ -107,8 +108,13 @@ static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &hea
++pos;
}
} else {
+ const auto isSeparator = [](char c) {
+ static const char separators[] = "()<>@,;:\\\"/[]?={}";
+ return isLWS(c) || strchr(separators, c) != nullptr;
+ };
+
// case: token
- while (pos < header.length()) {
+ while (pos < header.size()) {
char c = header.at(pos);
if (isSeparator(c))
break;
@@ -117,7 +123,7 @@ static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &hea
}
}
- result.insert(key, value);
+ result.insert(key.toByteArray().toLower(), value);
// find the comma now:
comma = header.indexOf(',', pos);
@@ -127,7 +133,7 @@ static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &hea
} else {
// case: token
// key is already set
- result.insert(key, QByteArray());
+ result.insert(key.toByteArray().toLower(), QByteArray());
}
}
}
@@ -152,6 +158,9 @@ QNetworkReplyHttpImpl::QNetworkReplyHttpImpl(QNetworkAccessManager* const manage
d->sslConfiguration.reset(new QSslConfiguration(request.sslConfiguration()));
#endif
+ QObjectPrivate::connect(this, &QNetworkReplyHttpImpl::redirectAllowed, d,
+ &QNetworkReplyHttpImplPrivate::followRedirect, Qt::QueuedConnection);
+
// FIXME Later maybe set to Unbuffered, especially if it is zerocopy or from cache?
QIODevice::open(QIODevice::ReadOnly);
@@ -326,6 +335,7 @@ qint64 QNetworkReplyHttpImpl::readData(char* data, qint64 maxlen)
d->error(QNetworkReplyImpl::NetworkError::UnknownContentError,
QCoreApplication::translate("QHttp", "Decompression failed: %1")
.arg(d->decompressHelper.errorString()));
+ d->decompressHelper.clear();
return -1;
}
if (d->cacheSaveDevice) {
@@ -471,16 +481,17 @@ bool QNetworkReplyHttpImplPrivate::loadFromCacheIfAllowed(QHttpNetworkRequest &h
if (CacheLoadControlAttribute == QNetworkRequest::AlwaysNetwork) {
// If the request does not already specify preferred cache-control
// force reload from the network and tell any caching proxy servers to reload too
- if (!request.rawHeaderList().contains("Cache-Control")) {
- httpRequest.setHeaderField("Cache-Control", "no-cache");
- httpRequest.setHeaderField("Pragma", "no-cache");
+ if (!request.rawHeaderList().contains(cacheControlName())) {
+ const auto noCache = "no-cache"_ba;
+ httpRequest.setHeaderField(cacheControlName(), noCache);
+ httpRequest.setHeaderField("Pragma"_ba, noCache);
}
return false;
}
// The disk cache API does not currently support partial content retrieval.
// That is why we don't use the disk cache for any such requests.
- if (request.hasRawHeader("Range"))
+ if (request.hasRawHeader(rangeName()))
return false;
QAbstractNetworkCache *nc = managerPrivate->networkCache;
@@ -498,20 +509,27 @@ bool QNetworkReplyHttpImplPrivate::loadFromCacheIfAllowed(QHttpNetworkRequest &h
QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
cacheHeaders.setAllRawHeaders(metaData.rawHeaders());
+ it = cacheHeaders.findRawHeader("content-length");
+ if (it != cacheHeaders.rawHeaders.constEnd()) {
+ std::unique_ptr<QIODevice> data(nc->data(httpRequest.url()));
+ if (!data || data->size() < it->second.toLongLong())
+ return false; // The data is smaller than the content-length specified
+ }
+
it = cacheHeaders.findRawHeader("etag");
if (it != cacheHeaders.rawHeaders.constEnd())
- httpRequest.setHeaderField("If-None-Match", it->second);
+ httpRequest.setHeaderField("If-None-Match"_ba, it->second);
QDateTime lastModified = metaData.lastModified();
if (lastModified.isValid())
- httpRequest.setHeaderField("If-Modified-Since", QNetworkHeadersPrivate::toHttpDate(lastModified));
+ httpRequest.setHeaderField("If-Modified-Since"_ba, QNetworkHeadersPrivate::toHttpDate(lastModified));
- it = cacheHeaders.findRawHeader("Cache-Control");
+ it = cacheHeaders.findRawHeader(cacheControlName());
if (it != cacheHeaders.rawHeaders.constEnd()) {
QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(it->second);
- if (cacheControl.contains("must-revalidate"))
+ if (cacheControl.contains("must-revalidate"_ba))
return false;
- if (cacheControl.contains("no-cache"))
+ if (cacheControl.contains("no-cache"_ba))
return false;
}
@@ -566,10 +584,11 @@ bool QNetworkReplyHttpImplPrivate::loadFromCacheIfAllowed(QHttpNetworkRequest &h
if (lastModified.isValid() && dateHeader.isValid()) {
qint64 diff = lastModified.secsTo(dateHeader);
freshness_lifetime = diff / 10;
- if (httpRequest.headerField("Warning").isEmpty()) {
+ const auto warningHeader = "Warning"_ba;
+ if (httpRequest.headerField(warningHeader).isEmpty()) {
QDateTime dt = currentDateTime.addSecs(current_age);
if (currentDateTime.daysTo(dt) > 1)
- httpRequest.setHeaderField("Warning", "113");
+ httpRequest.setHeaderField(warningHeader, "113"_ba);
}
}
@@ -681,8 +700,13 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
switch (operation) {
case QNetworkAccessManager::GetOperation:
httpRequest.setOperation(QHttpNetworkRequest::Get);
- if (loadFromCacheIfAllowed(httpRequest))
+ // If the request has a body, createUploadByteDevice() and don't use caching
+ if (outgoingData) {
+ invalidateCache();
+ createUploadByteDevice();
+ } else if (loadFromCacheIfAllowed(httpRequest)) {
return; // no need to send the request! :)
+ }
break;
case QNetworkAccessManager::HeadOperation:
@@ -722,14 +746,15 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
QList<QByteArray> headers = newHttpRequest.rawHeaderList();
if (resumeOffset != 0) {
- const int rangeIndex = headers.indexOf("Range");
+ const int rangeIndex = headers.indexOf(rangeName());
if (rangeIndex != -1) {
// Need to adjust resume offset for user specified range
headers.removeAt(rangeIndex);
// We've already verified that requestRange starts with "bytes=", see canResume.
- QByteArray requestRange = newHttpRequest.rawHeader("Range").mid(6);
+ const auto rangeHeader = newHttpRequest.rawHeader(rangeName());
+ const auto requestRange = QByteArrayView(rangeHeader).mid(bytesEqualPrefix().size());
int index = requestRange.indexOf('-');
@@ -737,16 +762,16 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
quint64 requestEndOffset = requestRange.mid(index + 1).toULongLong();
// In case an end offset is not given it is skipped from the request range
- requestRange = "bytes=" + QByteArray::number(resumeOffset + requestStartOffset) +
+ QByteArray newRange = bytesEqualPrefix() + QByteArray::number(resumeOffset + requestStartOffset) +
'-' + (requestEndOffset ? QByteArray::number(requestEndOffset) : QByteArray());
- httpRequest.setHeaderField("Range", requestRange);
+ httpRequest.setHeaderField(rangeName(), newRange);
} else {
- httpRequest.setHeaderField("Range", "bytes=" + QByteArray::number(resumeOffset) + '-');
+ httpRequest.setHeaderField(rangeName(), bytesEqualPrefix() + QByteArray::number(resumeOffset) + '-');
}
}
- for (const QByteArray &header : qAsConst(headers))
+ for (const QByteArray &header : std::as_const(headers))
httpRequest.setHeaderField(header, newHttpRequest.rawHeader(header));
if (newHttpRequest.attribute(QNetworkRequest::HttpPipeliningAllowedAttribute).toBool())
@@ -783,6 +808,7 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
QHttpThreadDelegate *delegate = new QHttpThreadDelegate;
// Propagate Http/2 settings:
delegate->http2Parameters = request.http2Configuration();
+ delegate->http1Parameters = request.http1Configuration();
if (request.attribute(QNetworkRequest::ConnectionCacheExpiryTimeoutSecondsAttribute).isValid())
delegate->connectionCacheExpiryTimeoutSeconds = request.attribute(QNetworkRequest::ConnectionCacheExpiryTimeoutSecondsAttribute).toInt();
@@ -860,9 +886,6 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
q, SLOT(onRedirected(QUrl,int,int)),
Qt::QueuedConnection);
- QObject::connect(q, SIGNAL(redirectAllowed()), q, SLOT(followRedirect()),
- Qt::QueuedConnection);
-
#ifndef QT_NO_SSL
QObject::connect(delegate, SIGNAL(sslConfigurationChanged(QSslConfiguration)),
q, SLOT(replySslConfigurationChanged(QSslConfiguration)),
@@ -1035,9 +1058,6 @@ void QNetworkReplyHttpImplPrivate::replyDownloadData(QByteArray d)
// cache this, we need it later and it's invalidated when dealing with compressed data
auto dataSize = d.size();
- // Grab this to compare later (only relevant for compressed data) in case none of the data
- // will be propagated to the user
- const qint64 previousBytesDownloaded = bytesDownloaded;
if (cacheEnabled && isCachingAllowed() && !cacheSaveDevice)
initCacheSaveDevice();
@@ -1053,6 +1073,7 @@ void QNetworkReplyHttpImplPrivate::replyDownloadData(QByteArray d)
error(QNetworkReplyImpl::NetworkError::UnknownContentError,
QCoreApplication::translate("QHttp", "Decompression failed: %1")
.arg(decompressHelper.errorString()));
+ decompressHelper.clear();
return;
}
@@ -1072,6 +1093,7 @@ void QNetworkReplyHttpImplPrivate::replyDownloadData(QByteArray d)
error(QNetworkReplyImpl::NetworkError::UnknownContentError,
QCoreApplication::translate("QHttp",
"Data downloaded is too large to store"));
+ decompressHelper.clear();
return;
}
d.resize(nextSize);
@@ -1080,6 +1102,7 @@ void QNetworkReplyHttpImplPrivate::replyDownloadData(QByteArray d)
error(QNetworkReplyImpl::NetworkError::UnknownContentError,
QCoreApplication::translate("QHttp", "Decompression failed: %1")
.arg(decompressHelper.errorString()));
+ decompressHelper.clear();
return;
}
}
@@ -1121,11 +1144,12 @@ void QNetworkReplyHttpImplPrivate::replyDownloadData(QByteArray d)
// This can occur when downloading compressed data as some of the data may be the content
// encoding's header. Don't emit anything for this.
- if (previousBytesDownloaded == bytesDownloaded) {
+ if (lastReadyReadEmittedSize == bytesDownloaded) {
if (readBufferMaxSize)
emit q->readBufferFreed(dataSize);
return;
}
+ lastReadyReadEmittedSize = bytesDownloaded;
QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
@@ -1221,13 +1245,18 @@ void QNetworkReplyHttpImplPrivate::onRedirected(const QUrl &redirectUrl, int htt
return;
}
+ // If the original operation was a GET with a body and the status code is either
+ // 307 or 308 then keep the message body
+ const bool getOperationKeepsBody = (operation == QNetworkAccessManager::GetOperation)
+ && (httpStatus == 307 || httpStatus == 308);
+
redirectRequest = createRedirectRequest(originalRequest, url, maxRedirectsRemaining);
operation = getRedirectOperation(operation, httpStatus);
// Clear stale headers, the relevant ones get set again later
httpRequest.clearHeaders();
- if (operation == QNetworkAccessManager::GetOperation
- || operation == QNetworkAccessManager::HeadOperation) {
+ if ((operation == QNetworkAccessManager::GetOperation
+ || operation == QNetworkAccessManager::HeadOperation) && !getOperationKeepsBody) {
// possibly changed from not-GET/HEAD to GET/HEAD, make sure to get rid of upload device
uploadByteDevice.reset();
uploadByteDevicePosition = 0;
@@ -1274,6 +1303,8 @@ void QNetworkReplyHttpImplPrivate::followRedirect()
q, [this]() { postRequest(redirectRequest); }, Qt::QueuedConnection);
}
+static constexpr QLatin1StringView locationHeader() noexcept { return "location"_L1; }
+
void QNetworkReplyHttpImplPrivate::checkForRedirect(const int statusCode)
{
Q_Q(QNetworkReplyHttpImpl);
@@ -1286,7 +1317,7 @@ void QNetworkReplyHttpImplPrivate::checkForRedirect(const int statusCode)
// What do we do about the caching of the HTML note?
// The response to a 303 MUST NOT be cached, while the response to
// all of the others is cacheable if the headers indicate it to be
- QByteArray header = q->rawHeader("location");
+ QByteArray header = q->rawHeader(locationHeader());
QUrl url = QUrl(QString::fromUtf8(header));
if (!url.isValid())
url = QUrl(QLatin1StringView(header));
@@ -1294,7 +1325,7 @@ void QNetworkReplyHttpImplPrivate::checkForRedirect(const int statusCode)
}
}
-void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QList<QPair<QByteArray,QByteArray> > &hm,
+void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QHttpHeaders &hm,
int sc, const QString &rp, bool pu,
QSharedPointer<char> db,
qint64 contentLength,
@@ -1327,26 +1358,29 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QList<QPair<QByte
q->setAttribute(QNetworkRequest::HttpPipeliningWasUsedAttribute, pu);
q->setAttribute(QNetworkRequest::Http2WasUsedAttribute, h2Used);
+ // A user having manually defined which encodings they accept is, for
+ // somwehat unknown (presumed legacy compatibility) reasons treated as
+ // disabling our decompression:
+ const bool autoDecompress = request.rawHeader("accept-encoding").isEmpty();
+ const bool shouldDecompress = isCompressed && autoDecompress;
// reconstruct the HTTP header
- QList<QPair<QByteArray, QByteArray> > headerMap = hm;
- QList<QPair<QByteArray, QByteArray> >::ConstIterator it = headerMap.constBegin(),
- end = headerMap.constEnd();
- for (; it != end; ++it) {
- QByteArray value = q->rawHeader(it->first);
+ for (qsizetype i = 0; i < hm.size(); ++i) {
+ const auto key = hm.nameAt(i);
+ const auto originValue = hm.valueAt(i);
+
+ QByteArray value = q->rawHeader(key);
// Reset any previous "location" header set in the reply. In case of
// redirects, we don't want to 'append' multiple location header values,
// rather we keep only the latest one
- if (it->first.toLower() == "location")
+ if (key == locationHeader())
value.clear();
- if (isCompressed && !decompressHelper.isValid()
- && it->first.compare("content-encoding", Qt::CaseInsensitive) == 0) {
-
+ if (shouldDecompress && !decompressHelper.isValid() && key == "content-encoding"_L1) {
if (!synchronous) // with synchronous all the data is expected to be handled at once
decompressHelper.setCountingBytesEnabled(true);
- if (!decompressHelper.setEncoding(it->second)) {
+ if (!decompressHelper.setEncoding(originValue)) {
error(QNetworkReplyImpl::NetworkError::UnknownContentError,
QCoreApplication::translate("QHttp", "Failed to initialize decompression: %1")
.arg(decompressHelper.errorString()));
@@ -1359,13 +1393,13 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QList<QPair<QByte
if (!value.isEmpty()) {
// Why are we appending values for headers which are already
// present?
- if (it->first.compare("set-cookie", Qt::CaseInsensitive) == 0)
+ if (key == "set-cookie"_L1)
value += '\n';
else
value += ", ";
}
- value += it->second;
- q->setRawHeader(it->first, value);
+ value += originValue;
+ q->setRawHeader({key.data(), key.size()}, value);
}
q->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, statusCode);
@@ -1384,11 +1418,11 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QList<QPair<QByte
QNetworkHeadersPrivate cacheHeaders;
cacheHeaders.setAllRawHeaders(metaData.rawHeaders());
QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
- it = cacheHeaders.findRawHeader("Cache-Control");
+ it = cacheHeaders.findRawHeader(cacheControlName());
bool mustReValidate = false;
if (it != cacheHeaders.rawHeaders.constEnd()) {
QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(it->second);
- if (cacheControl.contains("must-revalidate"))
+ if (cacheControl.contains("must-revalidate"_ba))
mustReValidate = true;
}
if (!mustReValidate && sendCacheContents(metaData))
@@ -1632,7 +1666,7 @@ bool QNetworkReplyHttpImplPrivate::sendCacheContents(const QNetworkCacheMetaData
QUrl redirectUrl;
for ( ; it != end; ++it) {
if (httpRequest.isFollowRedirects() &&
- !it->first.compare("location", Qt::CaseInsensitive))
+ !it->first.compare(locationHeader(), Qt::CaseInsensitive))
redirectUrl = QUrl::fromEncoded(it->second);
setRawHeader(it->first, it->second);
}
@@ -1669,6 +1703,27 @@ bool QNetworkReplyHttpImplPrivate::sendCacheContents(const QNetworkCacheMetaData
return true;
}
+static auto caseInsensitiveCompare(QByteArrayView value)
+{
+ return [value](QByteArrayView element)
+ {
+ return value.compare(element, Qt::CaseInsensitive) == 0;
+ };
+}
+
+static bool isHopByHop(QByteArrayView header)
+{
+ constexpr QByteArrayView headers[] = { "connection",
+ "keep-alive",
+ "proxy-authenticate",
+ "proxy-authorization",
+ "te",
+ "trailers",
+ "transfer-encoding",
+ "upgrade"};
+ return std::any_of(std::begin(headers), std::end(headers), caseInsensitiveCompare(header));
+}
+
QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNetworkCacheMetaData &oldMetaData) const
{
Q_Q(const QNetworkReplyHttpImpl);
@@ -1680,22 +1735,11 @@ QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNe
QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
const QList<QByteArray> newHeaders = q->rawHeaderList();
- for (QByteArray header : newHeaders) {
- QByteArray originalHeader = header;
- header = header.toLower();
- bool hop_by_hop =
- (header == "connection"
- || header == "keep-alive"
- || header == "proxy-authenticate"
- || header == "proxy-authorization"
- || header == "te"
- || header == "trailers"
- || header == "transfer-encoding"
- || header == "upgrade");
- if (hop_by_hop)
+ for (const QByteArray& header : newHeaders) {
+ if (isHopByHop(header))
continue;
- if (header == "set-cookie")
+ if (header.compare("set-cookie", Qt::CaseInsensitive) == 0)
continue;
// for 4.6.0, we were planning to not store the date header in the
@@ -1708,27 +1752,27 @@ QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNe
//continue;
// Don't store Warning 1xx headers
- if (header == "warning") {
- QByteArray v = q->rawHeader(header);
- if (v.length() == 3
+ if (header.compare("warning", Qt::CaseInsensitive) == 0) {
+ const QByteArray v = q->rawHeader(header);
+ if (v.size() == 3
&& v[0] == '1'
- && v[1] >= '0' && v[1] <= '9'
- && v[2] >= '0' && v[2] <= '9')
+ && isAsciiDigit(v[1])
+ && isAsciiDigit(v[2]))
continue;
}
it = cacheHeaders.findRawHeader(header);
if (it != cacheHeaders.rawHeaders.constEnd()) {
// Match the behavior of Firefox and assume Cache-Control: "no-transform"
- if (header == "content-encoding"
- || header == "content-range"
- || header == "content-type")
+ constexpr QByteArrayView headers[]=
+ {"content-encoding", "content-range", "content-type"};
+ if (std::any_of(std::begin(headers), std::end(headers), caseInsensitiveCompare(header)))
continue;
}
// IIS has been known to send "Content-Length: 0" on 304 responses, so
// ignore this too
- if (header == "content-length" && statusCode == 304)
+ if (statusCode == 304 && header.compare("content-length", Qt::CaseInsensitive) == 0)
continue;
#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
@@ -1736,23 +1780,23 @@ QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNe
QByteArray o;
if (it != cacheHeaders.rawHeaders.constEnd())
o = (*it).second;
- if (n != o && header != "date") {
+ if (n != o && headerheader.compare("date", Qt::CaseInsensitive) != 0) {
qDebug() << "replacing" << header;
qDebug() << "new" << n;
qDebug() << "old" << o;
}
#endif
- cacheHeaders.setRawHeader(originalHeader, q->rawHeader(header));
+ cacheHeaders.setRawHeader(header, q->rawHeader(header));
}
metaData.setRawHeaders(cacheHeaders.rawHeaders);
bool checkExpired = true;
QHash<QByteArray, QByteArray> cacheControl;
- it = cacheHeaders.findRawHeader("Cache-Control");
+ it = cacheHeaders.findRawHeader(cacheControlName());
if (it != cacheHeaders.rawHeaders.constEnd()) {
cacheControl = parseHttpOptionHeader(it->second);
- QByteArray maxAge = cacheControl.value("max-age");
+ QByteArray maxAge = cacheControl.value("max-age"_ba);
if (!maxAge.isEmpty()) {
checkExpired = false;
QDateTime dt = QDateTime::currentDateTimeUtc();
@@ -1779,7 +1823,7 @@ QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNe
canDiskCache = true;
// HTTP/1.1. Check the Cache-Control header
- if (cacheControl.contains("no-store"))
+ if (cacheControl.contains("no-store"_ba))
canDiskCache = false;
// responses to POST might be cacheable
@@ -1788,7 +1832,7 @@ QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNe
canDiskCache = false;
// some pages contain "expires:" and "cache-control: no-cache" field,
// so we only might cache POST requests if we get "cache-control: max-age ..."
- if (cacheControl.contains("max-age"))
+ if (cacheControl.contains("max-age"_ba))
canDiskCache = true;
// responses to PUT and DELETE are not cacheable
@@ -1819,14 +1863,14 @@ bool QNetworkReplyHttpImplPrivate::canResume() const
return false;
// Can only resume if server/resource supports Range header.
- QByteArray acceptRangesheaderName("Accept-Ranges");
+ constexpr auto acceptRangesheaderName = QByteArrayView("Accept-Ranges");
if (!q->hasRawHeader(acceptRangesheaderName) || q->rawHeader(acceptRangesheaderName) == "none")
return false;
// We only support resuming for byte ranges.
- if (request.hasRawHeader("Range")) {
- QByteArray range = request.rawHeader("Range");
- if (!range.startsWith("bytes="))
+ if (request.hasRawHeader(rangeName())) {
+ QByteArray range = request.rawHeader(rangeName());
+ if (!range.startsWith(bytesEqualPrefix()))
return false;
}
@@ -1845,7 +1889,9 @@ void QNetworkReplyHttpImplPrivate::setResumeOffset(quint64 offset)
void QNetworkReplyHttpImplPrivate::_q_startOperation()
{
- if (state == Working) // ensure this function is only being called once
+ // Ensure this function is only being called once, and not at all if we were
+ // cancelled
+ if (state >= Working)
return;
state = Working;
@@ -2005,9 +2051,9 @@ void QNetworkReplyHttpImplPrivate::setupTransferTimeout()
Qt::QueuedConnection);
}
transferTimeout->stop();
- if (request.transferTimeout()) {
+ if (request.transferTimeoutAsDuration() > 0ms) {
transferTimeout->setSingleShot(true);
- transferTimeout->setInterval(request.transferTimeout());
+ transferTimeout->setInterval(request.transferTimeoutAsDuration());
QMetaObject::invokeMethod(transferTimeout, "start",
Qt::QueuedConnection);
@@ -2113,13 +2159,12 @@ void QNetworkReplyHttpImplPrivate::error(QNetworkReplyImpl::NetworkError code, c
Q_Q(QNetworkReplyHttpImpl);
// Can't set and emit multiple errors.
if (errorCode != QNetworkReply::NoError) {
- qWarning("QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.");
+ // But somewhat unavoidable if we have cancelled the request:
+ if (errorCode != QNetworkReply::OperationCanceledError)
+ qWarning("QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.");
return;
}
- if (decompressHelper.isValid())
- decompressHelper.clear(); // Just get rid of any data that might be stored
-
errorCode = code;
q->setErrorString(errorMessage);
diff --git a/src/network/access/qnetworkreplyhttpimpl_p.h b/src/network/access/qnetworkreplyhttpimpl_p.h
index 55953ae878..e00c43bdb3 100644
--- a/src/network/access/qnetworkreplyhttpimpl_p.h
+++ b/src/network/access/qnetworkreplyhttpimpl_p.h
@@ -200,6 +200,10 @@ public:
qint64 bytesDownloaded;
qint64 bytesBuffered;
+ // We use this to keep track of whether or not we need to emit readyRead
+ // when we deal with signal compression (delaying emission) + decompressing
+ // data (potentially receiving bytes that don't end up in the final output):
+ qint64 lastReadyReadEmittedSize = 0;
QTimer *transferTimeout;
@@ -240,7 +244,7 @@ public:
// From HTTP thread:
void replyDownloadData(QByteArray);
void replyFinished();
- void replyDownloadMetaData(const QList<QPair<QByteArray,QByteArray> > &, int, const QString &,
+ void replyDownloadMetaData(const QHttpHeaders &, int, const QString &,
bool, QSharedPointer<char>, qint64, qint64, bool, bool);
void replyDownloadProgressSlot(qint64,qint64);
void httpAuthenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *auth);
diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp
index a1fedb4cd3..8b2acfdb4e 100644
--- a/src/network/access/qnetworkreplyimpl.cpp
+++ b/src/network/access/qnetworkreplyimpl.cpp
@@ -281,7 +281,7 @@ void QNetworkReplyImplPrivate::handleNotifications()
if (notificationHandlingPaused)
return;
- for (InternalNotifications notification : qExchange(pendingNotifications, {})) {
+ for (InternalNotifications notification : std::exchange(pendingNotifications, {})) {
if (state != Working)
return;
switch (notification) {
@@ -519,11 +519,6 @@ void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data)
_q_copyReadyRead();
}
-static void downloadBufferDeleter(char *ptr)
-{
- delete[] ptr;
-}
-
char* QNetworkReplyImplPrivate::getDownloadBuffer(qint64 size)
{
Q_Q(QNetworkReplyImpl);
@@ -536,7 +531,7 @@ char* QNetworkReplyImplPrivate::getDownloadBuffer(qint64 size)
downloadBufferCurrentSize = 0;
downloadBufferMaximumSize = size;
downloadBuffer = new char[downloadBufferMaximumSize]; // throws if allocation fails
- downloadBufferPointer = QSharedPointer<char>(downloadBuffer, downloadBufferDeleter);
+ downloadBufferPointer = QSharedPointer<char>(downloadBuffer, [](auto p) { delete[] p; });
q->setAttribute(QNetworkRequest::DownloadBufferAttribute, QVariant::fromValue<QSharedPointer<char> > (downloadBufferPointer));
}
diff --git a/src/network/access/qnetworkreplyimpl_p.h b/src/network/access/qnetworkreplyimpl_p.h
index d24edfb567..9648b8b57a 100644
--- a/src/network/access/qnetworkreplyimpl_p.h
+++ b/src/network/access/qnetworkreplyimpl_p.h
@@ -157,7 +157,4 @@ Q_DECLARE_TYPEINFO(QNetworkReplyImplPrivate::InternalNotifications, Q_PRIMITIVE_
QT_END_NAMESPACE
-// ### move to qsharedpointer_impl.h
-QT_DECL_METATYPE_EXTERN_TAGGED(QSharedPointer<char>, QSharedPointer_char, Q_NETWORK_EXPORT)
-
#endif
diff --git a/src/network/access/qnetworkreplywasmimpl.cpp b/src/network/access/qnetworkreplywasmimpl.cpp
index 2e0f865ccb..c02f0b4e61 100644
--- a/src/network/access/qnetworkreplywasmimpl.cpp
+++ b/src/network/access/qnetworkreplywasmimpl.cpp
@@ -9,6 +9,8 @@
#include <QtCore/qcoreapplication.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qthread.h>
+#include <QtCore/private/qoffsetstringarray_p.h>
+#include <QtCore/private/qtools_p.h>
#include <private/qnetworkaccessmanager_p.h>
#include <private/qnetworkfile_p.h>
@@ -18,6 +20,41 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
+namespace {
+
+static constexpr auto BannedHeaders = qOffsetStringArray(
+ "accept-charset",
+ "accept-encoding",
+ "access-control-request-headers",
+ "access-control-request-method",
+ "connection",
+ "content-length",
+ "cookie",
+ "cookie2",
+ "date",
+ "dnt",
+ "expect",
+ "host",
+ "keep-alive",
+ "origin",
+ "referer",
+ "te",
+ "trailer",
+ "transfer-encoding",
+ "upgrade",
+ "via"
+);
+
+bool isUnsafeHeader(QLatin1StringView header) noexcept
+{
+ return header.startsWith("proxy-"_L1, Qt::CaseInsensitive)
+ || header.startsWith("sec-"_L1, Qt::CaseInsensitive)
+ || BannedHeaders.contains(header, Qt::CaseInsensitive);
+}
+} // namespace
+
QNetworkReplyWasmImplPrivate::QNetworkReplyWasmImplPrivate()
: QNetworkReplyPrivate()
, managerPrivate(0)
@@ -25,7 +62,7 @@ QNetworkReplyWasmImplPrivate::QNetworkReplyWasmImplPrivate()
, downloadBufferCurrentSize(0)
, totalDownloadSize(0)
, percentFinished(0)
- , m_fetch(0)
+ , m_fetch(nullptr)
{
}
@@ -42,6 +79,9 @@ QNetworkReplyWasmImpl::QNetworkReplyWasmImpl(QObject *parent)
QNetworkReplyWasmImpl::~QNetworkReplyWasmImpl()
{
+ if (isRunning())
+ abort();
+ close();
}
QByteArray QNetworkReplyWasmImpl::methodName() const
@@ -68,22 +108,38 @@ QByteArray QNetworkReplyWasmImpl::methodName() const
void QNetworkReplyWasmImpl::close()
{
+ Q_D(QNetworkReplyWasmImpl);
+
+ if (d->state != QNetworkReplyPrivate::Aborted &&
+ d->state != QNetworkReplyPrivate::Finished &&
+ d->state != QNetworkReplyPrivate::Idle) {
+ d->state = QNetworkReplyPrivate::Finished;
+ d->setCanceled();
+ }
+
QNetworkReply::close();
- setFinished(true);
- emit finished();
}
void QNetworkReplyWasmImpl::abort()
{
Q_D(QNetworkReplyWasmImpl);
+
if (d->state == QNetworkReplyPrivate::Finished || d->state == QNetworkReplyPrivate::Aborted)
return;
d->state = QNetworkReplyPrivate::Aborted;
- d->m_fetch->userData = nullptr;
+ d->setCanceled();
+}
- d->emitReplyError(QNetworkReply::OperationCanceledError, QStringLiteral("Operation canceled"));
- close();
+void QNetworkReplyWasmImplPrivate::setCanceled()
+{
+ Q_Q(QNetworkReplyWasmImpl);
+ if (m_fetch)
+ m_fetch->userData = nullptr;
+
+ emitReplyError(QNetworkReply::OperationCanceledError, QStringLiteral("Operation canceled"));
+ q->setFinished(true);
+ emit q->finished();
}
qint64 QNetworkReplyWasmImpl::bytesAvailable() const
@@ -175,15 +231,22 @@ void QNetworkReplyWasmImplPrivate::doSendRequest()
QList<QByteArray> headersData = request.rawHeaderList();
int arrayLength = getArraySize(headersData.count());
- const char* customHeaders[arrayLength];
+ const char *customHeaders[arrayLength];
+ QStringList trimmedHeaders;
if (headersData.count() > 0) {
int i = 0;
- for (int j = 0; j < headersData.count(); j++) {
- customHeaders[i] = headersData[j].constData();
- i += 1;
- customHeaders[i] = request.rawHeader(headersData[j]).constData();
- i += 1;
+ for (const auto &headerName : headersData) {
+ if (isUnsafeHeader(QLatin1StringView(headerName.constData()))) {
+ trimmedHeaders.push_back(QString::fromLatin1(headerName));
+ } else {
+ customHeaders[i++] = headerName.constData();
+ customHeaders[i++] = request.rawHeader(headerName).constData();
+ }
+ }
+ if (!trimmedHeaders.isEmpty()) {
+ qWarning() << "Qt has trimmed the following forbidden headers from the request:"
+ << trimmedHeaders.join(QLatin1StringView(", "));
}
customHeaders[i] = nullptr;
attr.requestHeaders = customHeaders;
@@ -224,6 +287,7 @@ void QNetworkReplyWasmImplPrivate::doSendRequest()
attr.attributes -= EMSCRIPTEN_FETCH_PERSIST_FILE;
}
+ attr.withCredentials = request.attribute(QNetworkRequest::UseCredentialsAttribute, false).toBool();
attr.onsuccess = QNetworkReplyWasmImplPrivate::downloadSucceeded;
attr.onerror = QNetworkReplyWasmImplPrivate::downloadFailed;
attr.onprogress = QNetworkReplyWasmImplPrivate::downloadProgress;
@@ -231,11 +295,12 @@ void QNetworkReplyWasmImplPrivate::doSendRequest()
attr.timeoutMSecs = request.transferTimeout();
attr.userData = reinterpret_cast<void *>(this);
- QString dPath = QStringLiteral("/home/web_user/") + request.url().fileName();
+ QString dPath = "/home/web_user/"_L1 + request.url().fileName();
QByteArray destinationPath = dPath.toUtf8();
attr.destinationPath = destinationPath.constData();
- m_fetch = emscripten_fetch(&attr, request.url().toString().toUtf8());
+ m_fetch = emscripten_fetch(&attr, request.url().toString().toUtf8().constData());
+ state = Working;
}
void QNetworkReplyWasmImplPrivate::emitReplyError(QNetworkReply::NetworkError errorCode, const QString &errorString)
@@ -252,15 +317,16 @@ void QNetworkReplyWasmImplPrivate::emitDataReadProgress(qint64 bytesReceived, qi
totalDownloadSize = bytesTotal;
- percentFinished = (bytesReceived / bytesTotal) * 100;
+ percentFinished = bytesTotal ? (bytesReceived / bytesTotal) * 100 : 100;
emit q->downloadProgress(bytesReceived, bytesTotal);
}
-void QNetworkReplyWasmImplPrivate::dataReceived(const QByteArray &buffer, int bufferSize)
+void QNetworkReplyWasmImplPrivate::dataReceived(const QByteArray &buffer)
{
Q_Q(QNetworkReplyWasmImpl);
+ const qsizetype bufferSize = buffer.size();
if (bufferSize > 0)
q->setReadBufferSize(bufferSize);
@@ -273,7 +339,7 @@ void QNetworkReplyWasmImplPrivate::dataReceived(const QByteArray &buffer, int bu
totalDownloadSize = downloadBufferCurrentSize;
- downloadBuffer.append(buffer, bufferSize);
+ downloadBuffer.append(buffer);
emit q->readyRead();
}
@@ -284,32 +350,36 @@ static int parseHeaderName(const QByteArray &headerName)
if (headerName.isEmpty())
return -1;
- switch (tolower(headerName.at(0))) {
+ auto is = [&](const char *what) {
+ return qstrnicmp(headerName.data(), headerName.size(), what) == 0;
+ };
+
+ switch (QtMiscUtils::toAsciiLower(headerName.front())) {
case 'c':
- if (qstricmp(headerName.constData(), "content-type") == 0)
+ if (is("content-type"))
return QNetworkRequest::ContentTypeHeader;
- else if (qstricmp(headerName.constData(), "content-length") == 0)
+ else if (is("content-length"))
return QNetworkRequest::ContentLengthHeader;
- else if (qstricmp(headerName.constData(), "cookie") == 0)
+ else if (is("cookie"))
return QNetworkRequest::CookieHeader;
break;
case 'l':
- if (qstricmp(headerName.constData(), "location") == 0)
+ if (is("location"))
return QNetworkRequest::LocationHeader;
- else if (qstricmp(headerName.constData(), "last-modified") == 0)
+ else if (is("last-modified"))
return QNetworkRequest::LastModifiedHeader;
break;
case 's':
- if (qstricmp(headerName.constData(), "set-cookie") == 0)
+ if (is("set-cookie"))
return QNetworkRequest::SetCookieHeader;
- else if (qstricmp(headerName.constData(), "server") == 0)
+ else if (is("server"))
return QNetworkRequest::ServerHeader;
break;
case 'u':
- if (qstricmp(headerName.constData(), "user-agent") == 0)
+ if (is("user-agent"))
return QNetworkRequest::UserAgentHeader;
break;
}
@@ -411,7 +481,7 @@ void QNetworkReplyWasmImplPrivate::downloadSucceeded(emscripten_fetch_t *fetch)
if (reply) {
if (reply->state != QNetworkReplyPrivate::Aborted) {
QByteArray buffer(fetch->data, fetch->numBytes);
- reply->dataReceived(buffer, buffer.size());
+ reply->dataReceived(buffer);
QByteArray statusText(fetch->statusText);
reply->setStatusCode(fetch->status, statusText);
reply->setReplyFinished();
@@ -424,6 +494,7 @@ void QNetworkReplyWasmImplPrivate::downloadSucceeded(emscripten_fetch_t *fetch)
void QNetworkReplyWasmImplPrivate::setReplyFinished()
{
Q_Q(QNetworkReplyWasmImpl);
+ state = QNetworkReplyPrivate::Finished;
q->setFinished(true);
emit q->readChannelFinished();
emit q->finished();
@@ -473,6 +544,8 @@ void QNetworkReplyWasmImplPrivate::downloadFailed(emscripten_fetch_t *fetch)
reasonStr = QStringLiteral("Operation canceled");
else
reasonStr = QString::fromUtf8(fetch->statusText);
+ QByteArray buffer(fetch->data, fetch->numBytes);
+ reply->dataReceived(buffer);
QByteArray statusText(fetch->statusText);
reply->setStatusCode(fetch->status, statusText);
reply->emitReplyError(reply->statusCodeFromHttp(fetch->status, reply->request.url()), reasonStr);
diff --git a/src/network/access/qnetworkreplywasmimpl_p.h b/src/network/access/qnetworkreplywasmimpl_p.h
index ff933b49f8..ae167799d7 100644
--- a/src/network/access/qnetworkreplywasmimpl_p.h
+++ b/src/network/access/qnetworkreplywasmimpl_p.h
@@ -57,7 +57,7 @@ public:
Q_PRIVATE_SLOT(d_func(), void emitReplyError(QNetworkReply::NetworkError errorCode, const QString &errorString))
Q_PRIVATE_SLOT(d_func(), void emitDataReadProgress(qint64 done, qint64 total))
- Q_PRIVATE_SLOT(d_func(), void dataReceived(char *buffer, int bufferSize))
+ Q_PRIVATE_SLOT(d_func(), void dataReceived(const QByteArray &buffer))
private:
QByteArray methodName() const;
@@ -75,7 +75,7 @@ public:
void emitReplyError(QNetworkReply::NetworkError errorCode, const QString &);
void emitDataReadProgress(qint64 done, qint64 total);
- void dataReceived(const QByteArray &buffer, int bufferSize);
+ void dataReceived(const QByteArray &buffer);
void headersReceived(const QByteArray &buffer);
void setStatusCode(int status, const QByteArray &statusText);
@@ -112,6 +112,7 @@ public:
emscripten_fetch_t *m_fetch;
void setReplyFinished();
+ void setCanceled();
Q_DECLARE_PUBLIC(QNetworkReplyWasmImpl)
};
diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp
index 07eaf2f484..6f5a7ff19a 100644
--- a/src/network/access/qnetworkrequest.cpp
+++ b/src/network/access/qnetworkrequest.cpp
@@ -6,13 +6,17 @@
#include "qplatformdefs.h"
#include "qnetworkcookie.h"
#include "qsslconfiguration.h"
-#if QT_CONFIG(http) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(http)
+#include "qhttp1configuration.h"
#include "qhttp2configuration.h"
#include "private/http2protocol_p.h"
#endif
-#include "QtCore/qshareddata.h"
-#include "QtCore/qlocale.h"
+
#include "QtCore/qdatetime.h"
+#include "QtCore/qlocale.h"
+#include "QtCore/qshareddata.h"
+#include "QtCore/qtimezone.h"
+#include "QtCore/private/qtools_p.h"
#include <ctype.h>
#if QT_CONFIG(datestring)
@@ -24,6 +28,9 @@
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
+using namespace std::chrono_literals;
+
+constexpr std::chrono::milliseconds QNetworkRequest::DefaultTransferTimeout;
QT_IMPL_METATYPE_EXTERN(QNetworkRequest)
QT_IMPL_METATYPE_EXTERN_TAGGED(QNetworkRequest::RedirectPolicy, QNetworkRequest__RedirectPolicy)
@@ -217,7 +224,7 @@ QT_IMPL_METATYPE_EXTERN_TAGGED(QNetworkRequest::RedirectPolicy, QNetworkRequest_
Requests only, type: QMetaType::Int (default: QNetworkRequest::Automatic)
Indicates whether to use cached authorization credentials in the request,
if available. If this is set to QNetworkRequest::Manual and the authentication
- mechanism is 'Basic' or 'Digest', Qt will not send an an 'Authorization' HTTP
+ mechanism is 'Basic' or 'Digest', Qt will not send an 'Authorization' HTTP
header with any cached credentials it may have for the request's URL.
This attribute is set to QNetworkRequest::Manual by Qt WebKit when creating a cross-origin
XMLHttpRequest where withCredentials has not been set explicitly to true by the
@@ -305,6 +312,13 @@ QT_IMPL_METATYPE_EXTERN_TAGGED(QNetworkRequest::RedirectPolicy, QNetworkRequest_
This attribute is ignored if the Http2AllowedAttribute is not set.
(This value was introduced in 6.3.)
+ \value UseCredentialsAttribute
+ Requests only, type: QMetaType::Bool (default: false)
+ Indicates if the underlying XMLHttpRequest cross-site Access-Control
+ requests should be made using credentials. Has no effect on
+ same-origin requests. This only affects the WebAssembly platform.
+ (This value was introduced in 6.5.)
+
\value User
Special type. Additional information can be passed in
QVariants with types ranging from User to UserMax. The default
@@ -396,6 +410,16 @@ QT_IMPL_METATYPE_EXTERN_TAGGED(QNetworkRequest::RedirectPolicy, QNetworkRequest_
\value DefaultTransferTimeoutConstant The transfer timeout in milliseconds.
Used if setTimeout() is called
without an argument.
+
+ \sa QNetworkRequest::DefaultTransferTimeout
+ */
+
+/*!
+ \variable QNetworkRequest::DefaultTransferTimeout
+
+ The transfer timeout with \l {QNetworkRequest::TransferTimeoutConstant}
+ milliseconds. Used if setTransferTimeout() is called without an
+ argument.
*/
class QNetworkRequestPrivate: public QSharedData, public QNetworkHeadersPrivate
@@ -408,7 +432,6 @@ public:
, sslConfiguration(nullptr)
#endif
, maxRedirectsAllowed(maxRedirectCount)
- , transferTimeout(0)
{ qRegisterMetaType<QNetworkRequest>(); }
~QNetworkRequestPrivate()
{
@@ -431,6 +454,7 @@ public:
#endif
peerVerifyName = other.peerVerifyName;
#if QT_CONFIG(http)
+ h1Configuration = other.h1Configuration;
h2Configuration = other.h2Configuration;
decompressedSafetyCheckThreshold = other.decompressedSafetyCheckThreshold;
#endif
@@ -446,6 +470,7 @@ public:
maxRedirectsAllowed == other.maxRedirectsAllowed &&
peerVerifyName == other.peerVerifyName
#if QT_CONFIG(http)
+ && h1Configuration == other.h1Configuration
&& h2Configuration == other.h2Configuration
&& decompressedSafetyCheckThreshold == other.decompressedSafetyCheckThreshold
#endif
@@ -462,10 +487,11 @@ public:
int maxRedirectsAllowed;
QString peerVerifyName;
#if QT_CONFIG(http)
+ QHttp1Configuration h1Configuration;
QHttp2Configuration h2Configuration;
qint64 decompressedSafetyCheckThreshold = 10ll * 1024ll * 1024ll;
#endif
- int transferTimeout;
+ std::chrono::milliseconds transferTimeout = 0ms;
};
/*!
@@ -481,10 +507,9 @@ QNetworkRequest::QNetworkRequest()
// Initial values proposed by RFC 7540 are quite draconian, but we
// know about servers configured with this value as maximum possible,
// rejecting our SETTINGS frame and sending us a GOAWAY frame with the
- // flow control error set. Unless an application sets its own parameters,
- // we don't send SETTINGS_INITIAL_WINDOW_SIZE, but increase
- // (via WINDOW_UPDATE) the session window size. These are our 'defaults':
- d->h2Configuration.setStreamReceiveWindowSize(Http2::defaultSessionWindowSize);
+ // flow control error set. If this causes a problem - the app should
+ // set a proper configuration. We'll use our defaults, as documented.
+ d->h2Configuration.setStreamReceiveWindowSize(Http2::qtDefaultStreamReceiveWindowSize);
d->h2Configuration.setSessionReceiveWindowSize(Http2::maxSessionReceiveWindowSize);
d->h2Configuration.setServerPushEnabled(false);
#endif // QT_CONFIG(http)
@@ -604,8 +629,9 @@ void QNetworkRequest::setHeader(KnownHeaders header, const QVariant &value)
network request.
\sa rawHeader(), setRawHeader()
+ \note In Qt versions prior to 6.7, this function took QByteArray only.
*/
-bool QNetworkRequest::hasRawHeader(const QByteArray &headerName) const
+bool QNetworkRequest::hasRawHeader(QAnyStringView headerName) const
{
return d->findRawHeader(headerName) != d->rawHeaders.constEnd();
}
@@ -619,12 +645,11 @@ bool QNetworkRequest::hasRawHeader(const QByteArray &headerName) const
Raw headers can be set with setRawHeader() or with setHeader().
\sa header(), setRawHeader()
+ \note In Qt versions prior to 6.7, this function took QByteArray only.
*/
-QByteArray QNetworkRequest::rawHeader(const QByteArray &headerName) const
+QByteArray QNetworkRequest::rawHeader(QAnyStringView headerName) const
{
- QNetworkHeadersPrivate::RawHeadersList::ConstIterator it =
- d->findRawHeader(headerName);
- if (it != d->rawHeaders.constEnd())
+ if (const auto it = d->findRawHeader(headerName); it != d->rawHeaders.constEnd())
return it->second;
return QByteArray();
}
@@ -846,7 +871,31 @@ void QNetworkRequest::setPeerVerifyName(const QString &peerName)
d->peerVerifyName = peerName;
}
-#if QT_CONFIG(http) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(http)
+/*!
+ \since 6.5
+
+ Returns the current parameters that QNetworkAccessManager is
+ using for the underlying HTTP/1 connection of this request.
+
+ \sa setHttp1Configuration
+*/
+QHttp1Configuration QNetworkRequest::http1Configuration() const
+{
+ return d->h1Configuration;
+}
+/*!
+ \since 6.5
+
+ Sets request's HTTP/1 parameters from \a configuration.
+
+ \sa http1Configuration, QNetworkAccessManager, QHttp1Configuration
+*/
+void QNetworkRequest::setHttp1Configuration(const QHttp1Configuration &configuration)
+{
+ d->h1Configuration = configuration;
+}
+
/*!
\since 5.14
@@ -859,7 +908,7 @@ void QNetworkRequest::setPeerVerifyName(const QString &peerName)
\list
\li Window size for connection-level flowcontrol is 2147483647 octets
- \li Window size for stream-level flowcontrol is 21474836 octets
+ \li Window size for stream-level flowcontrol is 214748364 octets
\li Max frame size is 16384
\endlist
@@ -932,85 +981,104 @@ void QNetworkRequest::setDecompressedSafetyCheckThreshold(qint64 threshold)
{
d->decompressedSafetyCheckThreshold = threshold;
}
-#endif // QT_CONFIG(http) || defined(Q_CLANG_QDOC)
+#endif // QT_CONFIG(http)
-#if QT_CONFIG(http) || defined(Q_CLANG_QDOC) || defined (Q_OS_WASM)
+#if QT_CONFIG(http) || defined (Q_OS_WASM)
/*!
+ \fn int QNetworkRequest::transferTimeout() const
\since 5.15
Returns the timeout used for transfers, in milliseconds.
- This timeout is zero if setTransferTimeout hasn't been
- called, which means that the timeout is not used.
+ \sa setTransferTimeout()
+*/
- \sa setTransferTimeout
+/*!
+ \fn void QNetworkRequest::setTransferTimeout(int timeout)
+ \since 5.15
+
+ Sets \a timeout as the transfer timeout in milliseconds.
+
+ \sa setTransferTimeout(std::chrono::milliseconds),
+ transferTimeout(), transferTimeoutAsDuration()
*/
-int QNetworkRequest::transferTimeout() const
+
+/*!
+ \since 6.7
+
+ Returns the timeout duration after which the transfer is aborted if no
+ data is exchanged.
+
+ The default duration is zero, which means that the timeout is not used.
+
+ \sa setTransferTimeout(std::chrono::milliseconds)
+*/
+std::chrono::milliseconds QNetworkRequest::transferTimeoutAsDuration() const
{
return d->transferTimeout;
}
/*!
- \since 5.15
+ \since 6.7
- Sets \a timeout as the transfer timeout in milliseconds.
+ Sets the timeout \a duration to abort the transfer if no data is exchanged.
Transfers are aborted if no bytes are transferred before
the timeout expires. Zero means no timer is set. If no
argument is provided, the timeout is
- QNetworkRequest::DefaultTransferTimeoutConstant. If this function
+ QNetworkRequest::DefaultTransferTimeout. If this function
is not called, the timeout is disabled and has the
value zero.
- \sa transferTimeout
+ \sa transferTimeoutAsDuration()
*/
-void QNetworkRequest::setTransferTimeout(int timeout)
+void QNetworkRequest::setTransferTimeout(std::chrono::milliseconds duration)
{
- d->transferTimeout = timeout;
+ d->transferTimeout = duration;
}
-#endif // QT_CONFIG(http) || defined(Q_CLANG_QDOC) || defined (Q_OS_WASM)
+#endif // QT_CONFIG(http) || defined (Q_OS_WASM)
static QByteArray headerName(QNetworkRequest::KnownHeaders header)
{
switch (header) {
case QNetworkRequest::ContentTypeHeader:
- return "Content-Type";
+ return "Content-Type"_ba;
case QNetworkRequest::ContentLengthHeader:
- return "Content-Length";
+ return "Content-Length"_ba;
case QNetworkRequest::LocationHeader:
- return "Location";
+ return "Location"_ba;
case QNetworkRequest::LastModifiedHeader:
- return "Last-Modified";
+ return "Last-Modified"_ba;
case QNetworkRequest::IfModifiedSinceHeader:
- return "If-Modified-Since";
+ return "If-Modified-Since"_ba;
case QNetworkRequest::ETagHeader:
- return "ETag";
+ return "ETag"_ba;
case QNetworkRequest::IfMatchHeader:
- return "If-Match";
+ return "If-Match"_ba;
case QNetworkRequest::IfNoneMatchHeader:
- return "If-None-Match";
+ return "If-None-Match"_ba;
case QNetworkRequest::CookieHeader:
- return "Cookie";
+ return "Cookie"_ba;
case QNetworkRequest::SetCookieHeader:
- return "Set-Cookie";
+ return "Set-Cookie"_ba;
case QNetworkRequest::ContentDispositionHeader:
- return "Content-Disposition";
+ return "Content-Disposition"_ba;
case QNetworkRequest::UserAgentHeader:
- return "User-Agent";
+ return "User-Agent"_ba;
case QNetworkRequest::ServerHeader:
- return "Server";
+ return "Server"_ba;
// no default:
// if new values are added, this will generate a compiler warning
@@ -1019,6 +1087,22 @@ static QByteArray headerName(QNetworkRequest::KnownHeaders header)
return QByteArray();
}
+static QByteArray makeCookieHeader(const QVariant &value, QNetworkCookie::RawForm type, QByteArrayView separator)
+{
+ QList<QNetworkCookie> cookies = qvariant_cast<QList<QNetworkCookie> >(value);
+ if (cookies.isEmpty() && value.userType() == qMetaTypeId<QNetworkCookie>())
+ cookies << qvariant_cast<QNetworkCookie>(value);
+
+ QByteArray result;
+ for (const QNetworkCookie &cookie : std::as_const(cookies)) {
+ result += cookie.toRawForm(type);
+ result += separator;
+ }
+ if (!result.isEmpty())
+ result.chop(separator.size());
+ return result;
+}
+
static QByteArray headerValue(QNetworkRequest::KnownHeaders header, const QVariant &value)
{
switch (header) {
@@ -1046,7 +1130,7 @@ static QByteArray headerValue(QNetworkRequest::KnownHeaders header, const QVaria
switch (value.userType()) {
// Generate RFC 1123/822 dates:
case QMetaType::QDate:
- return QNetworkHeadersPrivate::toHttpDate(value.toDate().startOfDay(Qt::UTC));
+ return QNetworkHeadersPrivate::toHttpDate(value.toDate().startOfDay(QTimeZone::UTC));
case QMetaType::QDateTime:
return QNetworkHeadersPrivate::toHttpDate(value.toDateTime());
@@ -1054,89 +1138,67 @@ static QByteArray headerValue(QNetworkRequest::KnownHeaders header, const QVaria
return value.toByteArray();
}
- case QNetworkRequest::CookieHeader: {
- QList<QNetworkCookie> cookies = qvariant_cast<QList<QNetworkCookie> >(value);
- if (cookies.isEmpty() && value.userType() == qMetaTypeId<QNetworkCookie>())
- cookies << qvariant_cast<QNetworkCookie>(value);
-
- QByteArray result;
- bool first = true;
- for (const QNetworkCookie &cookie : qAsConst(cookies)) {
- if (!first)
- result += "; ";
- first = false;
- result += cookie.toRawForm(QNetworkCookie::NameAndValueOnly);
- }
- return result;
- }
+ case QNetworkRequest::CookieHeader:
+ return makeCookieHeader(value, QNetworkCookie::NameAndValueOnly, "; ");
- case QNetworkRequest::SetCookieHeader: {
- QList<QNetworkCookie> cookies = qvariant_cast<QList<QNetworkCookie> >(value);
- if (cookies.isEmpty() && value.userType() == qMetaTypeId<QNetworkCookie>())
- cookies << qvariant_cast<QNetworkCookie>(value);
-
- QByteArray result;
- bool first = true;
- for (const QNetworkCookie &cookie : qAsConst(cookies)) {
- if (!first)
- result += ", ";
- first = false;
- result += cookie.toRawForm(QNetworkCookie::Full);
- }
- return result;
- }
+ case QNetworkRequest::SetCookieHeader:
+ return makeCookieHeader(value, QNetworkCookie::Full, ", ");
}
return QByteArray();
}
-static int parseHeaderName(const QByteArray &headerName)
+static int parseHeaderName(QByteArrayView headerName)
{
if (headerName.isEmpty())
return -1;
- switch (tolower(headerName.at(0))) {
+ auto is = [headerName](QByteArrayView what) {
+ return headerName.compare(what, Qt::CaseInsensitive) == 0;
+ };
+
+ switch (QtMiscUtils::toAsciiLower(headerName.front())) {
case 'c':
- if (headerName.compare("content-type", Qt::CaseInsensitive) == 0)
+ if (is("content-type"))
return QNetworkRequest::ContentTypeHeader;
- else if (headerName.compare("content-length", Qt::CaseInsensitive) == 0)
+ else if (is("content-length"))
return QNetworkRequest::ContentLengthHeader;
- else if (headerName.compare("cookie", Qt::CaseInsensitive) == 0)
+ else if (is("cookie"))
return QNetworkRequest::CookieHeader;
- else if (qstricmp(headerName.constData(), "content-disposition") == 0)
+ else if (is("content-disposition"))
return QNetworkRequest::ContentDispositionHeader;
break;
case 'e':
- if (qstricmp(headerName.constData(), "etag") == 0)
+ if (is("etag"))
return QNetworkRequest::ETagHeader;
break;
case 'i':
- if (qstricmp(headerName.constData(), "if-modified-since") == 0)
+ if (is("if-modified-since"))
return QNetworkRequest::IfModifiedSinceHeader;
- if (qstricmp(headerName.constData(), "if-match") == 0)
+ if (is("if-match"))
return QNetworkRequest::IfMatchHeader;
- if (qstricmp(headerName.constData(), "if-none-match") == 0)
+ if (is("if-none-match"))
return QNetworkRequest::IfNoneMatchHeader;
break;
case 'l':
- if (headerName.compare("location", Qt::CaseInsensitive) == 0)
+ if (is("location"))
return QNetworkRequest::LocationHeader;
- else if (headerName.compare("last-modified", Qt::CaseInsensitive) == 0)
+ else if (is("last-modified"))
return QNetworkRequest::LastModifiedHeader;
break;
case 's':
- if (headerName.compare("set-cookie", Qt::CaseInsensitive) == 0)
+ if (is("set-cookie"))
return QNetworkRequest::SetCookieHeader;
- else if (headerName.compare("server", Qt::CaseInsensitive) == 0)
+ else if (is("server"))
return QNetworkRequest::ServerHeader;
break;
case 'u':
- if (headerName.compare("user-agent", Qt::CaseInsensitive) == 0)
+ if (is("user-agent"))
return QNetworkRequest::UserAgentHeader;
break;
}
@@ -1152,13 +1214,12 @@ static QVariant parseHttpDate(const QByteArray &raw)
return QVariant(); // transform an invalid QDateTime into a null QVariant
}
-static QVariant parseCookieHeader(const QByteArray &raw)
+static QVariant parseCookieHeader(QByteArrayView raw)
{
QList<QNetworkCookie> result;
- const QList<QByteArray> cookieList = raw.split(';');
- for (const QByteArray &cookie : cookieList) {
+ for (auto cookie : QLatin1StringView(raw).tokenize(';'_L1)) {
QList<QNetworkCookie> parsed = QNetworkCookie::parseCookies(cookie.trimmed());
- if (parsed.count() != 1)
+ if (parsed.size() != 1)
return QVariant(); // invalid Cookie: header
result += parsed;
@@ -1167,9 +1228,9 @@ static QVariant parseCookieHeader(const QByteArray &raw)
return QVariant::fromValue(result);
}
-static QVariant parseETag(const QByteArray &raw)
+static QVariant parseETag(QByteArrayView raw)
{
- const QByteArray trimmed = raw.trimmed();
+ const QByteArrayView trimmed = raw.trimmed();
if (!trimmed.startsWith('"') && !trimmed.startsWith(R"(W/")"))
return QVariant();
@@ -1179,46 +1240,34 @@ static QVariant parseETag(const QByteArray &raw)
return QString::fromLatin1(trimmed);
}
-static QVariant parseIfMatch(const QByteArray &raw)
+template<typename T>
+static QVariant parseMatchImpl(QByteArrayView raw, T op)
{
- const QByteArray trimmedRaw = raw.trimmed();
+ const QByteArrayView trimmedRaw = raw.trimmed();
if (trimmedRaw == "*")
return QStringList(QStringLiteral("*"));
QStringList tags;
- const QList<QByteArray> split = trimmedRaw.split(',');
- for (const QByteArray &element : split) {
- const QByteArray trimmed = element.trimmed();
- if (!trimmed.startsWith('"'))
- continue;
-
- if (!trimmed.endsWith('"'))
- continue;
-
- tags += QString::fromLatin1(trimmed);
+ for (auto &element : QLatin1StringView(trimmedRaw).tokenize(','_L1)) {
+ if (const auto trimmed = element.trimmed(); op(trimmed))
+ tags += QString::fromLatin1(trimmed);
}
return tags;
}
-static QVariant parseIfNoneMatch(const QByteArray &raw)
-{
- const QByteArray trimmedRaw = raw.trimmed();
- if (trimmedRaw == "*")
- return QStringList(QStringLiteral("*"));
-
- QStringList tags;
- const QList<QByteArray> split = trimmedRaw.split(',');
- for (const QByteArray &element : split) {
- const QByteArray trimmed = element.trimmed();
- if (!trimmed.startsWith('"') && !trimmed.startsWith(R"(W/")"))
- continue;
- if (!trimmed.endsWith('"'))
- continue;
+static QVariant parseIfMatch(QByteArrayView raw)
+{
+ return parseMatchImpl(raw, [](QByteArrayView element) {
+ return element.startsWith('"') && element.endsWith('"');
+ });
+}
- tags += QString::fromLatin1(trimmed);
- }
- return tags;
+static QVariant parseIfNoneMatch(QByteArrayView raw)
+{
+ return parseMatchImpl(raw, [](QByteArrayView element) {
+ return (element.startsWith('"') || element.startsWith(R"(W/")")) && element.endsWith('"');
+ });
}
@@ -1235,7 +1284,7 @@ static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QBy
case QNetworkRequest::ContentLengthHeader: {
bool ok;
- qint64 result = value.trimmed().toLongLong(&ok);
+ qint64 result = QByteArrayView(value).trimmed().toLongLong(&ok);
if (ok)
return result;
return QVariant();
@@ -1274,15 +1323,14 @@ static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QBy
}
QNetworkHeadersPrivate::RawHeadersList::ConstIterator
-QNetworkHeadersPrivate::findRawHeader(const QByteArray &key) const
+QNetworkHeadersPrivate::findRawHeader(QAnyStringView key) const
{
- RawHeadersList::ConstIterator it = rawHeaders.constBegin();
- RawHeadersList::ConstIterator end = rawHeaders.constEnd();
- for ( ; it != end; ++it)
- if (it->first.compare(key, Qt::CaseInsensitive) == 0)
- return it;
-
- return end; // not found
+ auto isKeyEqual = [key](const auto &headerPair)
+ {
+ QLatin1StringView name{headerPair.first};
+ return QAnyStringView::compare(name, key, Qt::CaseInsensitive) == 0;
+ };
+ return std::find_if(rawHeaders.begin(), rawHeaders.end(), isKeyEqual);
}
QNetworkHeadersPrivate::RawHeadersList QNetworkHeadersPrivate::allRawHeaders() const
@@ -1294,10 +1342,8 @@ QList<QByteArray> QNetworkHeadersPrivate::rawHeadersKeys() const
{
QList<QByteArray> result;
result.reserve(rawHeaders.size());
- RawHeadersList::ConstIterator it = rawHeaders.constBegin(),
- end = rawHeaders.constEnd();
- for ( ; it != end; ++it)
- result << it->first;
+ for (const auto &pair : rawHeaders)
+ result << pair.first;
return result;
}
@@ -1325,10 +1371,8 @@ void QNetworkHeadersPrivate::setAllRawHeaders(const RawHeadersList &list)
cookedHeaders.clear();
rawHeaders = list;
- RawHeadersList::ConstIterator it = rawHeaders.constBegin();
- RawHeadersList::ConstIterator end = rawHeaders.constEnd();
- for ( ; it != end; ++it)
- parseAndSetHeader(it->first, it->second);
+ for (const auto &[key, value] : std::as_const(rawHeaders))
+ parseAndSetHeader(key, value);
}
void QNetworkHeadersPrivate::setCookedHeader(QNetworkRequest::KnownHeaders header,
@@ -1483,7 +1527,7 @@ QDateTime QNetworkHeadersPrivate::fromHttpDate(const QByteArray &value)
#endif // datestring
if (dt.isValid())
- dt.setTimeSpec(Qt::UTC);
+ dt.setTimeZone(QTimeZone::UTC);
return dt;
}
@@ -1493,3 +1537,5 @@ QByteArray QNetworkHeadersPrivate::toHttpDate(const QDateTime &dt)
}
QT_END_NAMESPACE
+
+#include "moc_qnetworkrequest.cpp"
diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h
index a9abcf8c49..3ca61a2ee3 100644
--- a/src/network/access/qnetworkrequest.h
+++ b/src/network/access/qnetworkrequest.h
@@ -14,10 +14,12 @@ QT_BEGIN_NAMESPACE
class QSslConfiguration;
class QHttp2Configuration;
+class QHttp1Configuration;
class QNetworkRequestPrivate;
class Q_NETWORK_EXPORT QNetworkRequest
{
+ Q_GADGET
public:
enum KnownHeaders {
ContentTypeHeader,
@@ -34,6 +36,8 @@ public:
IfMatchHeader,
IfNoneMatchHeader
};
+ Q_ENUM(KnownHeaders)
+
enum Attribute {
HttpStatusCodeAttribute,
HttpReasonPhraseAttribute,
@@ -63,6 +67,7 @@ public:
AutoDeleteReplyOnFinishAttribute,
ConnectionCacheExpiryTimeoutSecondsAttribute,
Http2CleartextAllowedAttribute,
+ UseCredentialsAttribute,
User = 1000,
UserMax = 32767
@@ -95,6 +100,9 @@ public:
DefaultTransferTimeoutConstant = 30000
};
+ static constexpr auto DefaultTransferTimeout =
+ std::chrono::milliseconds(DefaultTransferTimeoutConstant);
+
QNetworkRequest();
explicit QNetworkRequest(const QUrl &url);
QNetworkRequest(const QNetworkRequest &other);
@@ -116,9 +124,15 @@ public:
void setHeader(KnownHeaders header, const QVariant &value);
// raw headers:
+#if QT_NETWORK_REMOVED_SINCE(6, 7)
bool hasRawHeader(const QByteArray &headerName) const;
+#endif
+ bool hasRawHeader(QAnyStringView headerName) const;
QList<QByteArray> rawHeaderList() const;
+#if QT_NETWORK_REMOVED_SINCE(6, 7)
QByteArray rawHeader(const QByteArray &headerName) const;
+#endif
+ QByteArray rawHeader(QAnyStringView headerName) const;
void setRawHeader(const QByteArray &headerName, const QByteArray &value);
// attributes
@@ -142,18 +156,26 @@ public:
QString peerVerifyName() const;
void setPeerVerifyName(const QString &peerName);
-#if QT_CONFIG(http) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(http)
+ QHttp1Configuration http1Configuration() const;
+ void setHttp1Configuration(const QHttp1Configuration &configuration);
+
QHttp2Configuration http2Configuration() const;
void setHttp2Configuration(const QHttp2Configuration &configuration);
qint64 decompressedSafetyCheckThreshold() const;
void setDecompressedSafetyCheckThreshold(qint64 threshold);
-#endif // QT_CONFIG(http) || defined(Q_CLANG_QDOC)
+#endif // QT_CONFIG(http)
-#if QT_CONFIG(http) || defined(Q_CLANG_QDOC) || defined (Q_OS_WASM)
+#if QT_CONFIG(http) || defined (Q_OS_WASM)
+ QT_NETWORK_INLINE_SINCE(6, 8)
int transferTimeout() const;
- void setTransferTimeout(int timeout = DefaultTransferTimeoutConstant);
-#endif // QT_CONFIG(http) || defined(Q_CLANG_QDOC) || defined (Q_OS_WASM)
+ QT_NETWORK_INLINE_SINCE(6, 8)
+ void setTransferTimeout(int timeout);
+
+ std::chrono::milliseconds transferTimeoutAsDuration() const;
+ void setTransferTimeout(std::chrono::milliseconds duration = DefaultTransferTimeout);
+#endif // QT_CONFIG(http) || defined (Q_OS_WASM)
private:
QSharedDataPointer<QNetworkRequestPrivate> d;
friend class QNetworkRequestPrivate;
@@ -161,6 +183,20 @@ private:
Q_DECLARE_SHARED(QNetworkRequest)
+#if QT_NETWORK_INLINE_IMPL_SINCE(6, 8)
+#if QT_CONFIG(http) || defined (Q_OS_WASM)
+int QNetworkRequest::transferTimeout() const
+{
+ return int(transferTimeoutAsDuration().count());
+}
+
+void QNetworkRequest::setTransferTimeout(int timeout)
+{
+ setTransferTimeout(std::chrono::milliseconds(timeout));
+}
+#endif // QT_CONFIG(http) || defined (Q_OS_WASM)
+#endif // INLINE_SINCE 6.8
+
QT_END_NAMESPACE
QT_DECL_METATYPE_EXTERN(QNetworkRequest, Q_NETWORK_EXPORT)
diff --git a/src/network/access/qnetworkrequest_p.h b/src/network/access/qnetworkrequest_p.h
index 57fb7b35be..48fcdcf1ed 100644
--- a/src/network/access/qnetworkrequest_p.h
+++ b/src/network/access/qnetworkrequest_p.h
@@ -40,7 +40,7 @@ public:
AttributesMap attributes;
QPointer<QObject> originatingObject;
- RawHeadersList::ConstIterator findRawHeader(const QByteArray &key) const;
+ RawHeadersList::ConstIterator findRawHeader(QAnyStringView key) const;
RawHeadersList allRawHeaders() const;
QList<QByteArray> rawHeadersKeys() const;
void setRawHeader(const QByteArray &key, const QByteArray &value);
diff --git a/src/network/access/qnetworkrequestfactory.cpp b/src/network/access/qnetworkrequestfactory.cpp
new file mode 100644
index 0000000000..87738d9086
--- /dev/null
+++ b/src/network/access/qnetworkrequestfactory.cpp
@@ -0,0 +1,727 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qnetworkrequestfactory.h"
+#include "qnetworkrequestfactory_p.h"
+
+#if QT_CONFIG(ssl)
+#include <QtNetwork/qsslconfiguration.h>
+#endif
+
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qmap.h>
+
+QT_BEGIN_NAMESPACE
+
+QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QNetworkRequestFactoryPrivate)
+
+using namespace Qt::StringLiterals;
+
+Q_LOGGING_CATEGORY(lcQrequestfactory, "qt.network.access.request.factory")
+
+/*!
+ \class QNetworkRequestFactory
+ \since 6.7
+ \ingroup shared
+ \inmodule QtNetwork
+
+ \brief Convenience class for grouping remote server endpoints that share
+ common network request properties.
+
+ \preliminary
+
+ REST servers often have endpoints that require the same headers and other data.
+ Grouping such endpoints with a QNetworkRequestFactory makes it more
+ convenient to issue requests to these endpoints; only the typically
+ varying parts such as \e path and \e query parameters are provided
+ when creating a new request.
+
+ Basic usage steps of QNetworkRequestFactory are as follows:
+ \list
+ \li Instantiation
+ \li Setting the data common to all requests
+ \li Issuing requests
+ \endlist
+
+ An example of usage:
+
+ \snippet code/src_network_access_qnetworkrequestfactory.cpp 0
+*/
+
+/*!
+ Creates a new QNetworkRequestFactory object.
+ Use setBaseUrl() to set a valid base URL for the requests.
+
+ \sa QNetworkRequestFactory(const QUrl &baseUrl), setBaseUrl()
+*/
+
+QNetworkRequestFactory::QNetworkRequestFactory()
+ : d(new QNetworkRequestFactoryPrivate)
+{
+}
+
+/*!
+ Creates a new QNetworkRequestFactory object, initializing the base URL to
+ \a baseUrl. The base URL is used to populate subsequent network
+ requests.
+
+ If the URL contains a \e path component, it will be extracted and used
+ as a base path in subsequent network requests. This means that any
+ paths provided when requesting individual requests will be appended
+ to this base path, as illustrated below:
+
+ \snippet code/src_network_access_qnetworkrequestfactory.cpp 1
+ */
+QNetworkRequestFactory::QNetworkRequestFactory(const QUrl &baseUrl)
+ : d(new QNetworkRequestFactoryPrivate(baseUrl))
+{
+}
+
+/*!
+ Destroys this QNetworkRequestFactory object.
+ */
+QNetworkRequestFactory::~QNetworkRequestFactory()
+ = default;
+
+/*!
+ Creates a copy of \a other.
+ */
+QNetworkRequestFactory::QNetworkRequestFactory(const QNetworkRequestFactory &other)
+ = default;
+
+/*!
+ Creates a copy of \a other and returns a reference to this factory.
+ */
+QNetworkRequestFactory &QNetworkRequestFactory::operator=(const QNetworkRequestFactory &other)
+ = default;
+
+/*!
+ \fn QNetworkRequestFactory::QNetworkRequestFactory(QNetworkRequestFactory &&other) noexcept
+
+ Move-constructs the factory from \a other.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new value.
+*/
+
+/*!
+ \fn QNetworkRequestFactory &QNetworkRequestFactory::operator=(QNetworkRequestFactory &&other) noexcept
+
+ Move-assigns \a other and returns a reference to this factory.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new value.
+ */
+
+/*!
+ \fn void QNetworkRequestFactory::swap(QNetworkRequestFactory &other)
+
+ Swaps this factory with \a other. This operation is
+ very fast and never fails.
+ */
+
+/*!
+ Returns the base URL used for the individual requests.
+
+ The base URL may contain a path component. This path is used
+ as path "prefix" for the paths that are provided when generating
+ individual requests.
+
+ \sa setBaseUrl()
+ */
+QUrl QNetworkRequestFactory::baseUrl() const
+{
+ return d->baseUrl;
+}
+
+/*!
+ Sets the base URL used in individual requests to \a url.
+
+ \sa baseUrl()
+ */
+void QNetworkRequestFactory::setBaseUrl(const QUrl &url)
+{
+ if (d->baseUrl == url)
+ return;
+
+ d.detach();
+ d->baseUrl = url;
+}
+
+#if QT_CONFIG(ssl)
+/*!
+ Returns the SSL configuration set to this factory. The SSL configuration
+ is set to each individual request.
+
+ \sa setSslConfiguration()
+ */
+QSslConfiguration QNetworkRequestFactory::sslConfiguration() const
+{
+ return d->sslConfig;
+}
+
+/*!
+ Sets the SSL configuration to \a configuration.
+
+ \sa sslConfiguration()
+ */
+void QNetworkRequestFactory::setSslConfiguration(const QSslConfiguration &configuration)
+{
+ if (d->sslConfig == configuration)
+ return;
+
+ d.detach();
+ d->sslConfig = configuration;
+}
+#endif
+
+/*!
+ Returns a QNetworkRequest.
+
+ The returned request is filled with the data that this factory
+ has been configured with.
+
+ \sa createRequest(const QUrlQuery&), createRequest(const QString&, const QUrlQuery&)
+*/
+
+QNetworkRequest QNetworkRequestFactory::createRequest() const
+{
+ return d->newRequest(d->requestUrl());
+}
+
+/*!
+ Returns a QNetworkRequest.
+
+ The returned request's URL is formed by appending the provided \a path
+ to the baseUrl (which may itself have a path component).
+
+ \sa createRequest(const QString &, const QUrlQuery &), createRequest(), baseUrl()
+*/
+QNetworkRequest QNetworkRequestFactory::createRequest(const QString &path) const
+{
+ return d->newRequest(d->requestUrl(&path));
+}
+
+/*!
+ Returns a QNetworkRequest.
+
+ The returned request's URL is formed by appending the provided \a query
+ to the baseUrl.
+
+ \sa createRequest(const QString &, const QUrlQuery &), createRequest(), baseUrl()
+*/
+QNetworkRequest QNetworkRequestFactory::createRequest(const QUrlQuery &query) const
+{
+ return d->newRequest(d->requestUrl(nullptr, &query));
+}
+
+/*!
+ Returns a QNetworkRequest.
+
+ The returned requests URL is formed by appending the provided \a path
+ and \a query to the baseUrl (which may have a path component).
+
+ If the provided \a path contains query items, they will be combined
+ with the items in \a query.
+
+ \sa createRequest(const QUrlQuery&), createRequest(), baseUrl()
+ */
+QNetworkRequest QNetworkRequestFactory::createRequest(const QString &path, const QUrlQuery &query) const
+{
+ return d->newRequest(d->requestUrl(&path, &query));
+}
+
+/*!
+ Sets \a headers that are common to all requests.
+
+ These headers are added to individual requests' headers.
+ This is a convenience mechanism for setting headers that
+ repeat across requests.
+
+ \sa commonHeaders(), clearCommonHeaders(), createRequest()
+ */
+void QNetworkRequestFactory::setCommonHeaders(const QHttpHeaders &headers)
+{
+ d.detach();
+ d->headers = headers;
+}
+
+/*!
+ Returns the currently set headers.
+
+ \sa setCommonHeaders(), clearCommonHeaders()
+ */
+QHttpHeaders QNetworkRequestFactory::commonHeaders() const
+{
+ return d->headers;
+}
+
+/*!
+ Clears current headers.
+
+ \sa commonHeaders(), setCommonHeaders()
+*/
+void QNetworkRequestFactory::clearCommonHeaders()
+{
+ if (d->headers.isEmpty())
+ return;
+ d.detach();
+ d->headers.clear();
+}
+
+/*!
+ Returns the bearer token that has been set.
+
+ The bearer token, if present, is used to set the
+ \c {Authorization: Bearer my_token} header for requests. This is a common
+ authorization convention and is provided as an additional convenience.
+
+ The means to acquire the bearer token vary. Standard methods include \c OAuth2
+ and the service provider's website/dashboard. It is expected that the bearer
+ token changes over time. For example, when updated with a refresh token,
+ always setting the new token again ensures that subsequent requests have
+ the latest, valid token.
+
+ The presence of the bearer token does not impact the \l commonHeaders()
+ listing. If the \l commonHeaders() also lists \c Authorization header, it
+ will be overwritten.
+
+ \sa setBearerToken(), commonHeaders()
+ */
+QByteArray QNetworkRequestFactory::bearerToken() const
+{
+ return d->bearerToken;
+}
+
+/*!
+ Sets the bearer token to \a token.
+
+ \sa bearerToken(), clearBearerToken()
+*/
+void QNetworkRequestFactory::setBearerToken(const QByteArray &token)
+{
+ if (d->bearerToken == token)
+ return;
+
+ d.detach();
+ d->bearerToken = token;
+}
+
+/*!
+ Clears the bearer token.
+
+ \sa bearerToken()
+*/
+void QNetworkRequestFactory::clearBearerToken()
+{
+ if (d->bearerToken.isEmpty())
+ return;
+
+ d.detach();
+ d->bearerToken.clear();
+}
+
+/*!
+ Returns the username set to this factory.
+
+ \sa setUserName(), clearUserName(), password()
+*/
+QString QNetworkRequestFactory::userName() const
+{
+ return d->userName;
+}
+
+/*!
+ Sets the username of this factory to \a userName.
+
+ The username is set in the request URL when \l createRequest() is called.
+ The QRestAccessManager / QNetworkAccessManager will attempt to use
+ these credentials when the server indicates that authentication
+ is required.
+
+ \sa userName(), clearUserName(), password()
+*/
+void QNetworkRequestFactory::setUserName(const QString &userName)
+{
+ if (d->userName == userName)
+ return;
+ d.detach();
+ d->userName = userName;
+}
+
+/*!
+ Clears the username set to this factory.
+*/
+void QNetworkRequestFactory::clearUserName()
+{
+ if (d->userName.isEmpty())
+ return;
+ d.detach();
+ d->userName.clear();
+}
+
+/*!
+ Returns the password set to this factory.
+
+ \sa password(), clearPassword(), userName()
+*/
+QString QNetworkRequestFactory::password() const
+{
+ return d->password;
+}
+
+/*!
+ Sets the password of this factory to \a password.
+
+ The password is set in the request URL when \l createRequest() is called.
+ The QRestAccessManager / QNetworkAccessManager will attempt to use
+ these credentials when the server indicates that authentication
+ is required.
+
+ \sa password(), clearPassword(), userName()
+*/
+void QNetworkRequestFactory::setPassword(const QString &password)
+{
+ if (d->password == password)
+ return;
+ d.detach();
+ d->password = password;
+}
+
+/*!
+ Clears the password set to this factory.
+
+ \sa password(), setPassword(), userName()
+*/
+void QNetworkRequestFactory::clearPassword()
+{
+ if (d->password.isEmpty())
+ return;
+ d.detach();
+ d->password.clear();
+}
+
+/*!
+ Sets \a timeout used for transfers.
+
+ \sa transferTimeout(), QNetworkRequest::setTransferTimeout(),
+ QNetworkAccessManager::setTransferTimeout()
+*/
+void QNetworkRequestFactory::setTransferTimeout(std::chrono::milliseconds timeout)
+{
+ if (d->transferTimeout == timeout)
+ return;
+
+ d.detach();
+ d->transferTimeout = timeout;
+}
+
+/*!
+ Returns the timeout used for transfers.
+
+ \sa setTransferTimeout(), QNetworkRequest::transferTimeout(),
+ QNetworkAccessManager::transferTimeout()
+*/
+std::chrono::milliseconds QNetworkRequestFactory::transferTimeout() const
+{
+ return d->transferTimeout;
+}
+
+/*!
+ Returns query parameters that are added to individual requests' query
+ parameters. The query parameters are added to any potential query
+ parameters provided with the individual \l createRequest() calls.
+
+ Use cases for using repeating query parameters are server dependent,
+ but typical examples include language setting \c {?lang=en}, format
+ specification \c {?format=json}, API version specification
+ \c {?version=1.0} and API key authentication.
+
+ \sa setQueryParameters(), clearQueryParameters(), createRequest()
+*/
+QUrlQuery QNetworkRequestFactory::queryParameters() const
+{
+ return d->queryParameters;
+}
+
+/*!
+ Sets \a query parameters that are added to individual requests' query
+ parameters.
+
+ \sa queryParameters(), clearQueryParameters()
+ */
+void QNetworkRequestFactory::setQueryParameters(const QUrlQuery &query)
+{
+ if (d->queryParameters == query)
+ return;
+
+ d.detach();
+ d->queryParameters = query;
+}
+
+/*!
+ Clears the query parameters.
+
+ \sa queryParameters()
+*/
+void QNetworkRequestFactory::clearQueryParameters()
+{
+ if (d->queryParameters.isEmpty())
+ return;
+
+ d.detach();
+ d->queryParameters.clear();
+}
+
+/*!
+ \since 6.8
+
+ Sets the priority for any future requests created by this factory to
+ \a priority.
+
+ The default priority is \l QNetworkRequest::NormalPriority.
+
+ \sa priority(), QNetworkRequest::setPriority()
+*/
+void QNetworkRequestFactory::setPriority(QNetworkRequest::Priority priority)
+{
+ if (d->priority == priority)
+ return;
+ d.detach();
+ d->priority = priority;
+}
+
+/*!
+ \since 6.8
+
+ Returns the priority assigned to any future requests created by this
+ factory.
+
+ \sa setPriority(), QNetworkRequest::priority()
+*/
+QNetworkRequest::Priority QNetworkRequestFactory::priority() const
+{
+ return d->priority;
+}
+
+/*!
+ \since 6.8
+
+ Sets the value associated with \a attribute to \a value.
+ If the attribute is already set, the previous value is
+ replaced. The attributes are set to any future requests
+ created by this factory.
+
+ \sa attribute(), clearAttribute(), clearAttributes(),
+ QNetworkRequest::Attribute
+*/
+void QNetworkRequestFactory::setAttribute(QNetworkRequest::Attribute attribute,
+ const QVariant &value)
+{
+ if (attribute == QNetworkRequest::HttpStatusCodeAttribute
+ || attribute == QNetworkRequest::HttpReasonPhraseAttribute
+ || attribute == QNetworkRequest::RedirectionTargetAttribute
+ || attribute == QNetworkRequest::ConnectionEncryptedAttribute
+ || attribute == QNetworkRequest::SourceIsFromCacheAttribute
+ || attribute == QNetworkRequest::HttpPipeliningWasUsedAttribute
+ || attribute == QNetworkRequest::Http2WasUsedAttribute
+ || attribute == QNetworkRequest::OriginalContentLengthAttribute)
+ {
+ qCWarning(lcQrequestfactory, "%i is a reply-only attribute, ignoring.", attribute);
+ return;
+ }
+ d.detach();
+ d->attributes.insert(attribute, value);
+}
+
+/*!
+ \since 6.8
+
+ Returns the value associated with \a attribute. If the
+ attribute has not been set, returns a default-constructed \l QVariant.
+
+ \sa attribute(QNetworkRequest::Attribute, const QVariant &),
+ setAttribute(), clearAttributes(), QNetworkRequest::Attribute
+
+*/
+QVariant QNetworkRequestFactory::attribute(QNetworkRequest::Attribute attribute) const
+{
+ return d->attributes.value(attribute);
+}
+
+/*!
+ \since 6.8
+
+ Returns the value associated with \a attribute. If the
+ attribute has not been set, returns \a defaultValue.
+
+ \sa attribute(), setAttribute(), clearAttributes(),
+ QNetworkRequest::Attribute
+*/
+QVariant QNetworkRequestFactory::attribute(QNetworkRequest::Attribute attribute,
+ const QVariant &defaultValue) const
+{
+ return d->attributes.value(attribute, defaultValue);
+}
+
+/*!
+ \since 6.8
+
+ Clears \a attribute set to this factory.
+
+ \sa attribute(), setAttribute()
+*/
+void QNetworkRequestFactory::clearAttribute(QNetworkRequest::Attribute attribute)
+{
+ if (!d->attributes.contains(attribute))
+ return;
+ d.detach();
+ d->attributes.remove(attribute);
+}
+
+/*!
+ \since 6.8
+
+ Clears any attributes set to this factory.
+
+ \sa attribute(), setAttribute()
+*/
+void QNetworkRequestFactory::clearAttributes()
+{
+ if (d->attributes.isEmpty())
+ return;
+ d.detach();
+ d->attributes.clear();
+}
+
+QNetworkRequestFactoryPrivate::QNetworkRequestFactoryPrivate()
+ = default;
+
+QNetworkRequestFactoryPrivate::QNetworkRequestFactoryPrivate(const QUrl &baseUrl)
+ : baseUrl(baseUrl)
+{
+}
+
+QNetworkRequestFactoryPrivate::~QNetworkRequestFactoryPrivate()
+ = default;
+
+QNetworkRequest QNetworkRequestFactoryPrivate::newRequest(const QUrl &url) const
+{
+ QNetworkRequest request;
+ request.setUrl(url);
+#if QT_CONFIG(ssl)
+ if (!sslConfig.isNull())
+ request.setSslConfiguration(sslConfig);
+#endif
+ // Set the header entries to the request. Combine values as there
+ // may be multiple values per name. Note: this would not necessarily
+ // produce right result for 'Set-Cookie' header if it has multiple values,
+ // but since it is a purely server-side (response) header, not relevant here.
+ const auto headerNames = headers.toMultiMap().uniqueKeys(); // ### fixme: port QNR to QHH
+ for (const auto &name : headerNames)
+ request.setRawHeader(name, headers.combinedValue(name));
+
+ constexpr char Bearer[] = "Bearer ";
+ if (!bearerToken.isEmpty())
+ request.setRawHeader("Authorization"_ba, Bearer + bearerToken);
+
+ request.setTransferTimeout(transferTimeout);
+ request.setPriority(priority);
+
+ for (const auto &[attribute, value] : attributes.asKeyValueRange())
+ request.setAttribute(attribute, value);
+
+ return request;
+}
+
+QUrl QNetworkRequestFactoryPrivate::requestUrl(const QString *path,
+ const QUrlQuery *query) const
+{
+ const QUrl providedPath = path ? QUrl(*path) : QUrl{};
+ const QUrlQuery providedQuery = query ? *query : QUrlQuery();
+
+ if (!providedPath.scheme().isEmpty() || !providedPath.host().isEmpty()) {
+ qCWarning(lcQrequestfactory, "The provided path %ls may only contain path and query item "
+ "components, and other parts will be ignored. Set the baseUrl instead",
+ qUtf16Printable(providedPath.toDisplayString()));
+ }
+
+ QUrl resultUrl = baseUrl;
+ QUrlQuery resultQuery(providedQuery);
+ QString basePath = baseUrl.path();
+
+ resultUrl.setUserName(userName);
+ resultUrl.setPassword(password);
+
+ // Separate the path and query parameters components on the application-provided path
+ const QString requestPath{providedPath.path()};
+ const QUrlQuery pathQueryItems{providedPath};
+
+ if (!pathQueryItems.isEmpty()) {
+ // Add any query items provided as part of the path
+ const auto items = pathQueryItems.queryItems(QUrl::ComponentFormattingOption::FullyEncoded);
+ for (const auto &[key, value]: items)
+ resultQuery.addQueryItem(key, value);
+ }
+
+ if (!queryParameters.isEmpty()) {
+ // Add any query items set to this factory
+ const QList<std::pair<QString,QString>> items =
+ queryParameters.queryItems(QUrl::ComponentFormattingOption::FullyEncoded);
+ for (const auto &item: items)
+ resultQuery.addQueryItem(item.first, item.second);
+ }
+
+ if (!resultQuery.isEmpty())
+ resultUrl.setQuery(resultQuery);
+
+ if (requestPath.isEmpty())
+ return resultUrl;
+
+ // Ensure that the "base path" (the path that may be present
+ // in the baseUrl), and the request path are joined with one '/'
+ // If both have it, remove one, if neither has it, add one
+ if (basePath.endsWith(u'/') && requestPath.startsWith(u'/'))
+ basePath.chop(1);
+ else if (!requestPath.startsWith(u'/') && !basePath.endsWith(u'/'))
+ basePath.append(u'/');
+
+ resultUrl.setPath(basePath.append(requestPath));
+ return resultUrl;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+/*!
+ \fn QDebug QNetworkRequestFactory::operator<<(QDebug debug,
+ const QNetworkRequestFactory &factory)
+
+ Writes \a factory into \a debug stream.
+
+ \sa {Debugging Techniques}
+*/
+QDebug operator<<(QDebug debug, const QNetworkRequestFactory &factory)
+{
+ const QDebugStateSaver saver(debug);
+ debug.resetFormat().nospace();
+
+ debug << "QNetworkRequestFactory(baseUrl = " << factory.baseUrl()
+ << ", headers = " << factory.commonHeaders()
+ << ", queryParameters = " << factory.queryParameters().queryItems()
+ << ", bearerToken = " << (factory.bearerToken().isEmpty() ? "(empty)" : "(is set)")
+ << ", transferTimeout = " << factory.transferTimeout()
+ << ", userName = " << (factory.userName().isEmpty() ? "(empty)" : "(is set)")
+ << ", password = " << (factory.password().isEmpty() ? "(empty)" : "(is set)")
+#if QT_CONFIG(ssl)
+ << ", SSL configuration"
+ << (factory.sslConfiguration().isNull() ? " is not set (default)" : " is set")
+#else
+ << ", no SSL support"
+#endif
+ << ")";
+ return debug;
+}
+#endif // QT_NO_DEBUG_STREAM
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkrequestfactory.h b/src/network/access/qnetworkrequestfactory.h
new file mode 100644
index 0000000000..9d955a51e7
--- /dev/null
+++ b/src/network/access/qnetworkrequestfactory.h
@@ -0,0 +1,100 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QNETWORKREQUESTFACTORY_H
+#define QNETWORKREQUESTFACTORY_H
+
+#include <QtNetwork/qnetworkrequest.h>
+#include <QtNetwork/qhttpheaders.h>
+
+#include <QtCore/qcompare.h>
+#include <QtCore/qshareddata.h>
+#include <QtCore/qurlquery.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qvariant.h>
+
+#include <chrono>
+
+QT_BEGIN_NAMESPACE
+
+class QDebug;
+#if QT_CONFIG(ssl)
+class QSslConfiguration;
+#endif
+
+class QNetworkRequestFactoryPrivate;
+QT_DECLARE_QESDP_SPECIALIZATION_DTOR_WITH_EXPORT(QNetworkRequestFactoryPrivate, Q_NETWORK_EXPORT)
+
+class QT_TECH_PREVIEW_API QNetworkRequestFactory
+{
+public:
+ Q_NETWORK_EXPORT QNetworkRequestFactory();
+ Q_NETWORK_EXPORT explicit QNetworkRequestFactory(const QUrl &baseUrl);
+ Q_NETWORK_EXPORT ~QNetworkRequestFactory();
+
+ Q_NETWORK_EXPORT QNetworkRequestFactory(const QNetworkRequestFactory &other);
+ QNetworkRequestFactory(QNetworkRequestFactory &&other) noexcept = default;
+ Q_NETWORK_EXPORT QNetworkRequestFactory &operator=(const QNetworkRequestFactory &other);
+
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QNetworkRequestFactory)
+ void swap(QNetworkRequestFactory &other) noexcept { d.swap(other.d); }
+
+ Q_NETWORK_EXPORT QUrl baseUrl() const;
+ Q_NETWORK_EXPORT void setBaseUrl(const QUrl &url);
+
+#if QT_CONFIG(ssl)
+ Q_NETWORK_EXPORT QSslConfiguration sslConfiguration() const;
+ Q_NETWORK_EXPORT void setSslConfiguration(const QSslConfiguration &configuration);
+#endif
+
+ Q_NETWORK_EXPORT QNetworkRequest createRequest() const;
+ Q_NETWORK_EXPORT QNetworkRequest createRequest(const QUrlQuery &query) const;
+ Q_NETWORK_EXPORT QNetworkRequest createRequest(const QString &path) const;
+ Q_NETWORK_EXPORT QNetworkRequest createRequest(const QString &path, const QUrlQuery &query) const;
+
+ Q_NETWORK_EXPORT void setCommonHeaders(const QHttpHeaders &headers);
+ Q_NETWORK_EXPORT QHttpHeaders commonHeaders() const;
+ Q_NETWORK_EXPORT void clearCommonHeaders();
+
+ Q_NETWORK_EXPORT QByteArray bearerToken() const;
+ Q_NETWORK_EXPORT void setBearerToken(const QByteArray &token);
+ Q_NETWORK_EXPORT void clearBearerToken();
+
+ Q_NETWORK_EXPORT QString userName() const;
+ Q_NETWORK_EXPORT void setUserName(const QString &userName);
+ Q_NETWORK_EXPORT void clearUserName();
+
+ Q_NETWORK_EXPORT QString password() const;
+ Q_NETWORK_EXPORT void setPassword(const QString &password);
+ Q_NETWORK_EXPORT void clearPassword();
+
+ Q_NETWORK_EXPORT void setTransferTimeout(std::chrono::milliseconds timeout);
+ Q_NETWORK_EXPORT std::chrono::milliseconds transferTimeout() const;
+
+ Q_NETWORK_EXPORT QUrlQuery queryParameters() const;
+ Q_NETWORK_EXPORT void setQueryParameters(const QUrlQuery &query);
+ Q_NETWORK_EXPORT void clearQueryParameters();
+
+ Q_NETWORK_EXPORT void setPriority(QNetworkRequest::Priority priority);
+ Q_NETWORK_EXPORT QNetworkRequest::Priority priority() const;
+
+ Q_NETWORK_EXPORT QVariant attribute(QNetworkRequest::Attribute attribute) const;
+ Q_NETWORK_EXPORT QVariant attribute(QNetworkRequest::Attribute attribute,
+ const QVariant &defaultValue) const;
+ Q_NETWORK_EXPORT void setAttribute(QNetworkRequest::Attribute attribute, const QVariant &value);
+ Q_NETWORK_EXPORT void clearAttribute(QNetworkRequest::Attribute attribute);
+ Q_NETWORK_EXPORT void clearAttributes();
+
+private:
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QNetworkRequestFactory &reply);
+#endif
+
+ QExplicitlySharedDataPointer<QNetworkRequestFactoryPrivate> d;
+};
+
+Q_DECLARE_SHARED(QNetworkRequestFactory)
+
+QT_END_NAMESPACE
+
+#endif // QNETWORKREQUESTFACTORY_H
diff --git a/src/network/access/qnetworkrequestfactory_p.h b/src/network/access/qnetworkrequestfactory_p.h
new file mode 100644
index 0000000000..4116669f21
--- /dev/null
+++ b/src/network/access/qnetworkrequestfactory_p.h
@@ -0,0 +1,56 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QNETWORKREQUESTFACTORY_P_H
+#define QNETWORKREQUESTFACTORY_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access framework. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNetwork/qhttpheaders.h>
+#include <QtNetwork/qnetworkrequest.h>
+#if QT_CONFIG(ssl)
+#include <QtNetwork/qsslconfiguration.h>
+#endif
+#include <QtCore/qhash.h>
+#include <QtCore/qshareddata.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qurlquery.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkRequestFactoryPrivate : public QSharedData
+{
+public:
+ QNetworkRequestFactoryPrivate();
+ explicit QNetworkRequestFactoryPrivate(const QUrl &baseUrl);
+ ~QNetworkRequestFactoryPrivate();
+ QNetworkRequest newRequest(const QUrl &url) const;
+ QUrl requestUrl(const QString *path = nullptr, const QUrlQuery *query = nullptr) const;
+
+#if QT_CONFIG(ssl)
+ QSslConfiguration sslConfig;
+#endif
+ QUrl baseUrl;
+ QHttpHeaders headers;
+ QByteArray bearerToken;
+ QString userName;
+ QString password;
+ QUrlQuery queryParameters;
+ QNetworkRequest::Priority priority = QNetworkRequest::NormalPriority;
+ std::chrono::milliseconds transferTimeout{0};
+ QHash<QNetworkRequest::Attribute, QVariant> attributes;
+};
+
+QT_END_NAMESPACE
+
+#endif // QNETWORKREQUESTFACTORY_P_H
diff --git a/src/network/access/qrestaccessmanager.cpp b/src/network/access/qrestaccessmanager.cpp
new file mode 100644
index 0000000000..7ef682e955
--- /dev/null
+++ b/src/network/access/qrestaccessmanager.cpp
@@ -0,0 +1,828 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qrestaccessmanager.h"
+#include "qrestaccessmanager_p.h"
+#include "qrestreply.h"
+
+#include <QtNetwork/qhttpmultipart.h>
+#include <QtNetwork/qnetworkaccessmanager.h>
+#include <QtNetwork/qnetworkreply.h>
+
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qthread.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest")
+
+/*!
+
+ \class QRestAccessManager
+ \brief The QRestAccessManager is a convenience wrapper for
+ QNetworkAccessManager.
+ \since 6.7
+
+ \ingroup network
+ \inmodule QtNetwork
+ \reentrant
+
+ \preliminary
+
+ QRestAccessManager is a convenience wrapper on top of
+ QNetworkAccessManager. It amends datatypes and HTTP methods
+ that are useful for typical RESTful client applications.
+
+ The usual Qt networking features are accessible by configuring the
+ wrapped QNetworkAccessManager directly. QRestAccessManager does not
+ take ownership of the wrapped QNetworkAccessManager.
+
+ QRestAccessManager and related QRestReply classes can only be used in the
+ thread they live in. See \l {QObject#Thread Affinity}{QObject thread affinity}
+ for more information.
+
+ \section1 Issuing Network Requests and Handling Replies
+
+ Network requests are initiated with a function call corresponding to
+ the desired HTTP method, such as \c get() and \c post().
+
+ \section2 Using Signals and Slots
+
+ The function returns a QNetworkReply* object, whose signals can be used
+ to follow up on the completion of the request in a traditional
+ Qt-signals-and-slots way.
+
+ Here's an example of how you could send a GET request and handle the
+ response:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 0
+
+ \section2 Using Callbacks and Context Objects
+
+ The functions also take a context object of QObject (subclass) type
+ and a callback function as parameters. The callback takes one QRestReply&
+ as a parameter. The callback can be any callable, including a
+ pointer-to-member-function.
+
+ These callbacks are invoked when the reply has finished processing
+ (also in the case the processing finished due to an error).
+
+ The context object can be \c nullptr, although, generally speaking,
+ this is discouraged. Using a valid context object ensures that if the
+ context object is destroyed during request processing, the callback will
+ not be called. Stray callbacks which access a destroyed context is a source
+ of application misbehavior.
+
+ Here's an example of how you could send a GET request and check the
+ response:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 1
+
+ Many of the functions take in data for sending to a server. The data is
+ supplied as the second parameter after the request.
+
+ Here's an example of how you could send a POST request and check the
+ response:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 2
+
+ The provided QRestReply& is valid only while the callback
+ executes. If you need it for longer, you can either move it
+ to another QRestReply, or construct a new one and initialize
+ it with the QNetworkReply (see QRestReply::networkReply()).
+
+ \section2 Supported data types
+
+ The following table summarizes the methods and the supported data types.
+ \c X means support.
+
+ \table
+ \header
+ \li Data type
+ \li \c get()
+ \li \c post()
+ \li \c put()
+ \li \c head()
+ \li \c patch()
+ \li \c deleteResource()
+ \li \c sendCustomRequest()
+ \row
+ \li No data
+ \li X
+ \li -
+ \li -
+ \li X
+ \li -
+ \li X
+ \li -
+ \row
+ \li QByteArray
+ \li X
+ \li X
+ \li X
+ \li -
+ \li X
+ \li -
+ \li X
+ \row
+ \li QJsonDocument *)
+ \li X
+ \li X
+ \li X
+ \li -
+ \li X
+ \li -
+ \li -
+ \row
+ \li QVariantMap **)
+ \li -
+ \li X
+ \li X
+ \li -
+ \li X
+ \li -
+ \li -
+ \row
+ \li QHttpMultiPart
+ \li -
+ \li X
+ \li X
+ \li -
+ \li -
+ \li -
+ \li X
+ \row
+ \li QIODevice
+ \li X
+ \li X
+ \li X
+ \li -
+ \li X
+ \li -
+ \li X
+ \endtable
+
+ *) QJsonDocument is sent in \l QJsonDocument::Compact format,
+ and the \c Content-Type header is set to \c {application/json} if the
+ \c Content-Type header was not set
+
+ **) QVariantMap is converted to and treated as a QJsonObject
+
+ \sa QRestReply, QNetworkRequestFactory, QNetworkAccessManager
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::get(
+ const QNetworkRequest &request,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ Issues an \c {HTTP GET} based on \a request.
+
+ The optional \a callback and \a context object can be provided for
+ handling the request completion as illustrated below:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 3
+
+ Alternatively the signals of the returned QNetworkReply* object can be
+ used. For further information see
+ \l {Issuing Network Requests and Handling Replies}.
+
+ \sa QRestReply
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::get(
+ const QNetworkRequest &request, const QByteArray &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ Issues an \c {HTTP GET} based on \a request and provided \a data.
+
+ The optional \a callback and \a context object can be provided for
+ handling the request completion as illustrated below:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 4
+
+ Alternatively the signals of the returned QNetworkReply* object can be
+ used. For further information see
+ \l {Issuing Network Requests and Handling Replies}.
+
+ \sa QRestReply
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::get(
+ const QNetworkRequest &request, const QJsonDocument &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::get(
+ const QNetworkRequest &request, QIODevice *data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
+ const QNetworkRequest &request, const QJsonDocument &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ Issues an \c {HTTP POST} based on \a request.
+
+ The optional \a callback and \a context object can be provided for
+ handling the request completion as illustrated below:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 5
+
+ Alternatively, the signals of the returned QNetworkReply* object can be
+ used. For further information see
+ \l {Issuing Network Requests and Handling Replies}.
+
+ The \c post() method always requires \a data parameter. The following
+ data types are supported:
+ \list
+ \li QByteArray
+ \li QJsonDocument *)
+ \li QVariantMap **)
+ \li QHttpMultiPart*
+ \li QIODevice*
+ \endlist
+
+ *) Sent in \l QJsonDocument::Compact format, and the
+ \c Content-Type header is set to \c {application/json} if the
+ \c Content-Type header was not set
+ **) QVariantMap is converted to and treated as a QJsonObject
+
+ \sa QRestReply
+*/
+
+/*!
+
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
+ const QNetworkRequest &request, const QVariantMap &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
+ const QNetworkRequest &request, const QByteArray &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
+ const QNetworkRequest &request, QHttpMultiPart *data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
+ const QNetworkRequest &request, QIODevice *data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
+ const QNetworkRequest &request, const QJsonDocument &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ Issues an \c {HTTP PUT} based on \a request.
+
+ The optional \a callback and \a context object can be provided for
+ handling the request completion as illustrated below:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 6
+
+ Alternatively the signals of the returned QNetworkReply* object can be
+ used. For further information see
+ \l {Issuing Network Requests and Handling Replies}.
+
+ The \c put() method always requires \a data parameter. The following
+ data types are supported:
+ \list
+ \li QByteArray
+ \li QJsonDocument *)
+ \li QVariantMap **)
+ \li QHttpMultiPart*
+ \li QIODevice*
+ \endlist
+
+ *) Sent in \l QJsonDocument::Compact format, and the
+ \c Content-Type header is set to \c {application/json} if the
+ \c Content-Type header was not set
+ **) QVariantMap is converted to and treated as a QJsonObject
+
+ \sa QRestReply
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
+ const QNetworkRequest &request, const QVariantMap &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
+ const QNetworkRequest &request, const QByteArray &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
+ const QNetworkRequest &request, QHttpMultiPart *data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
+ const QNetworkRequest &request, QIODevice *data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::patch(
+ const QNetworkRequest &request, const QJsonDocument &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ Issues an \c {HTTP PATCH} based on \a request.
+
+ The optional \a callback and \a context object can be provided for
+ handling the request completion as illustrated below:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 10
+
+ Alternatively the signals of the returned QNetworkReply* object can be
+ used. For further information see
+ \l {Issuing Network Requests and Handling Replies}.
+
+ The \c patch() method always requires \a data parameter. The following
+ data types are supported:
+ \list
+ \li QByteArray
+ \li QJsonDocument *)
+ \li QVariantMap **)
+ \li QIODevice*
+ \endlist
+
+ *) Sent in \l QJsonDocument::Compact format, and the
+ \c Content-Type header is set to \c {application/json} if the
+ \c Content-Type header was not set
+ **) QVariantMap is converted to and treated as a QJsonObject
+
+ \sa QRestReply
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::patch(
+ const QNetworkRequest &request, const QVariantMap &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::patch(
+ const QNetworkRequest &request, const QByteArray &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::patch(
+ const QNetworkRequest &request, QIODevice *data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::head(
+ const QNetworkRequest &request,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ Issues an \c {HTTP HEAD} based on \a request.
+
+ The optional \a callback and \a context object can be provided for
+ handling the request completion as illustrated below:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 7
+
+ Alternatively the signals of the returned QNetworkReply* object can be
+ used. For further information see
+ \l {Issuing Network Requests and Handling Replies}.
+
+ \c head() request does not support providing data.
+
+ \sa QRestReply
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::deleteResource(
+ const QNetworkRequest &request,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ Issues an \c {HTTP DELETE} based on \a request.
+
+ The optional \a callback and \a context object can be provided for
+ handling the request completion as illustrated below:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 8
+
+ Alternatively the signals of the returned QNetworkReply* object can be
+ used. For further information see
+ \l {Issuing Network Requests and Handling Replies}.
+
+ \c deleteResource() request does not support providing data.
+
+ \sa QRestReply
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::sendCustomRequest(
+ const QNetworkRequest& request, const QByteArray &method, const QByteArray &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ Issues \a request based HTTP request with custom \a method and the
+ provided \a data.
+
+ The optional \a callback and \a context object can be provided for
+ handling the request completion as illustrated below:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 9
+
+ Alternatively the signals of the returned QNetworkReply* object can be
+ used. For further information see
+ \l {Issuing Network Requests and Handling Replies}.
+
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::sendCustomRequest(
+ const QNetworkRequest& request, const QByteArray &method, QIODevice *data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::sendCustomRequest(
+ const QNetworkRequest& request, const QByteArray &method, QHttpMultiPart *data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ Constructs a QRestAccessManager object and sets \a parent as the parent
+ object, and \a manager as the underlying QNetworkAccessManager which
+ is used for communication.
+
+ \sa networkAccessManager()
+*/
+
+QRestAccessManager::QRestAccessManager(QNetworkAccessManager *manager, QObject *parent)
+ : QObject(*new QRestAccessManagerPrivate, parent)
+{
+ Q_D(QRestAccessManager);
+ d->qnam = manager;
+ if (!d->qnam)
+ qCWarning(lcQrest, "QRestAccessManager: QNetworkAccesManager is nullptr");
+}
+
+/*!
+ Destroys the QRestAccessManager object.
+*/
+QRestAccessManager::~QRestAccessManager()
+ = default;
+
+/*!
+ Returns the underlying QNetworkAccessManager instance.
+
+ \sa QNetworkAccessManager
+*/
+QNetworkAccessManager *QRestAccessManager::networkAccessManager() const
+{
+ Q_D(const QRestAccessManager);
+ return d->qnam;
+}
+
+QRestAccessManagerPrivate::QRestAccessManagerPrivate()
+ = default;
+
+QRestAccessManagerPrivate::~QRestAccessManagerPrivate()
+{
+ if (!activeRequests.isEmpty()) {
+ qCWarning(lcQrest, "Access manager destroyed while %lld requests were still in progress",
+ qlonglong(activeRequests.size()));
+ }
+}
+
+QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
+ const QJsonDocument &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([](auto qnam, auto req, auto data) { return qnam->post(req, data); },
+ data, request, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
+ const QVariantMap &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ return postWithDataImpl(request, QJsonDocument::fromVariant(data), context, slot);
+}
+
+QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
+ const QByteArray &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->post(request, data); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
+ QHttpMultiPart *data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->post(request, data); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
+ QIODevice *data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->post(request, data); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::getNoDataImpl(const QNetworkRequest &request,
+ const QObject *context, QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->get(request); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::getWithDataImpl(const QNetworkRequest &request,
+ const QByteArray &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->get(request, data); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::getWithDataImpl(const QNetworkRequest &request,
+ const QJsonDocument &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([](auto qnam, auto req, auto data) { return qnam->get(req, data); },
+ data, request, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::getWithDataImpl(const QNetworkRequest &request,
+ QIODevice *data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->get(request, data); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::deleteResourceNoDataImpl(const QNetworkRequest &request,
+ const QObject *context, QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->deleteResource(request); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::headNoDataImpl(const QNetworkRequest &request,
+ const QObject *context, QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->head(request); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
+ const QJsonDocument &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([](auto qnam, auto req, auto data) { return qnam->put(req, data); },
+ data, request, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
+ const QVariantMap &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ return putWithDataImpl(request, QJsonDocument::fromVariant(data), context, slot);
+}
+
+QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
+ const QByteArray &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->put(request, data); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
+ QHttpMultiPart *data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->put(request, data); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request, QIODevice *data,
+ const QObject *context, QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->put(request, data); }, context, slot);
+}
+
+static const auto PATCH = "PATCH"_ba;
+
+QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request,
+ const QJsonDocument &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest(
+ [](auto qnam, auto req, auto data) { return qnam->sendCustomRequest(req, PATCH, data); },
+ data, request, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request,
+ const QVariantMap &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ return patchWithDataImpl(request, QJsonDocument::fromVariant(data), context, slot);
+}
+
+QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request,
+ const QByteArray &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, PATCH, data); },
+ context, slot);
+}
+
+QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request, QIODevice *data,
+ const QObject *context, QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, PATCH, data); },
+ context, slot);
+}
+
+QNetworkReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request,
+ const QByteArray& method, const QByteArray &data,
+ const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, method, data); },
+ context, slot);
+}
+
+QNetworkReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request,
+ const QByteArray& method, QIODevice *data,
+ const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, method, data); },
+ context, slot);
+}
+
+QNetworkReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request,
+ const QByteArray& method, QHttpMultiPart *data,
+ const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, method, data); },
+ context, slot);
+}
+
+QNetworkReply *QRestAccessManagerPrivate::createActiveRequest(QNetworkReply *reply,
+ const QObject *contextObject,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_Q(QRestAccessManager);
+ Q_ASSERT(reply);
+ QtPrivate::SlotObjSharedPtr slotPtr(QtPrivate::SlotObjUniquePtr{slot}); // adopts
+ activeRequests.insert(reply, CallerInfo{contextObject, slotPtr});
+ // The signal connections below are made to 'q' to avoid stray signal
+ // handling upon its destruction while requests were still in progress
+
+ QObject::connect(reply, &QNetworkReply::finished, q, [reply, this]() {
+ handleReplyFinished(reply);
+ });
+ // Safe guard in case reply is destroyed before it's finished
+ QObject::connect(reply, &QObject::destroyed, q, [reply, this]() {
+ activeRequests.remove(reply);
+ });
+ // If context object is destroyed, clean up any possible replies it had associated with it
+ if (contextObject) {
+ QObject::connect(contextObject, &QObject::destroyed, q, [reply, this]() {
+ activeRequests.remove(reply);
+ });
+ }
+ return reply;
+}
+
+void QRestAccessManagerPrivate::verifyThreadAffinity(const QObject *contextObject)
+{
+ Q_Q(QRestAccessManager);
+ if (QThread::currentThread() != q->thread()) {
+ qCWarning(lcQrest, "QRestAccessManager can only be called in the thread it belongs to");
+ Q_ASSERT(false);
+ }
+ if (contextObject && (contextObject->thread() != q->thread())) {
+ qCWarning(lcQrest, "QRestAccessManager: the context object must reside in the same thread");
+ Q_ASSERT(false);
+ }
+}
+
+QNetworkReply* QRestAccessManagerPrivate::warnNoAccessManager()
+{
+ qCWarning(lcQrest, "QRestAccessManager: QNetworkAccessManager not set");
+ return nullptr;
+}
+
+void QRestAccessManagerPrivate::handleReplyFinished(QNetworkReply *reply)
+{
+ auto request = activeRequests.find(reply);
+ if (request == activeRequests.end()) {
+ qCDebug(lcQrest, "QRestAccessManager: Unexpected reply received, ignoring");
+ return;
+ }
+
+ CallerInfo caller = request.value();
+ activeRequests.erase(request);
+
+ if (caller.slot) {
+ // Callback was provided
+ QRestReply restReply(reply);
+ void *argv[] = { nullptr, &restReply };
+ // If we have context object, use it
+ QObject *context = caller.contextObject
+ ? const_cast<QObject*>(caller.contextObject.get()) : nullptr;
+ caller.slot->call(context, argv);
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qrestaccessmanager.cpp"
diff --git a/src/network/access/qrestaccessmanager.h b/src/network/access/qrestaccessmanager.h
new file mode 100644
index 0000000000..3245b41785
--- /dev/null
+++ b/src/network/access/qrestaccessmanager.h
@@ -0,0 +1,127 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QRESTACCESSMANAGER_H
+#define QRESTACCESSMANAGER_H
+
+#if 0
+#pragma qt_class(QRestAccessManager)
+#endif
+
+#include <QtNetwork/qnetworkaccessmanager.h>
+
+QT_BEGIN_NAMESPACE
+
+class QDebug;
+class QRestReply;
+
+#define QREST_METHOD_WITH_DATA(METHOD, DATA) \
+public: \
+template <typename Functor, if_compatible_callback<Functor> = true> \
+QNetworkReply *METHOD(const QNetworkRequest &request, DATA data, \
+ const ContextTypeForFunctor<Functor> *context, \
+ Functor &&callback) \
+{ \
+ return METHOD##WithDataImpl(request, data, context, \
+ QtPrivate::makeCallableObject<CallbackPrototype>(std::forward<Functor>(callback))); \
+} \
+QNetworkReply *METHOD(const QNetworkRequest &request, DATA data) \
+{ \
+ return METHOD##WithDataImpl(request, data, nullptr, nullptr); \
+} \
+private: \
+QNetworkReply *METHOD##WithDataImpl(const QNetworkRequest &request, DATA data, \
+ const QObject *context, QtPrivate::QSlotObjectBase *slot); \
+/* end */
+
+#define QREST_METHOD_NO_DATA(METHOD) \
+public: \
+template <typename Functor, if_compatible_callback<Functor> = true> \
+QNetworkReply *METHOD(const QNetworkRequest &request, \
+ const ContextTypeForFunctor<Functor> *context, \
+ Functor &&callback) \
+{ \
+ return METHOD##NoDataImpl(request, context, \
+ QtPrivate::makeCallableObject<CallbackPrototype>(std::forward<Functor>(callback))); \
+} \
+QNetworkReply *METHOD(const QNetworkRequest &request) \
+{ \
+ return METHOD##NoDataImpl(request, nullptr, nullptr); \
+} \
+private: \
+QNetworkReply *METHOD##NoDataImpl(const QNetworkRequest &request, \
+ const QObject *context, QtPrivate::QSlotObjectBase *slot); \
+/* end */
+
+#define QREST_METHOD_CUSTOM_WITH_DATA(DATA) \
+public: \
+template <typename Functor, if_compatible_callback<Functor> = true> \
+QNetworkReply *sendCustomRequest(const QNetworkRequest& request, const QByteArray &method, DATA data, \
+ const ContextTypeForFunctor<Functor> *context, \
+ Functor &&callback) \
+{ \
+ return customWithDataImpl(request, method, data, context, \
+ QtPrivate::makeCallableObject<CallbackPrototype>(std::forward<Functor>(callback))); \
+} \
+QNetworkReply *sendCustomRequest(const QNetworkRequest& request, const QByteArray &method, DATA data) \
+{ \
+ return customWithDataImpl(request, method, data, nullptr, nullptr); \
+} \
+private: \
+QNetworkReply *customWithDataImpl(const QNetworkRequest& request, const QByteArray &method, \
+ DATA data, const QObject* context, \
+ QtPrivate::QSlotObjectBase *slot); \
+/* end */
+
+class QRestAccessManagerPrivate;
+class QT_TECH_PREVIEW_API Q_NETWORK_EXPORT QRestAccessManager : public QObject
+{
+ Q_OBJECT
+ using CallbackPrototype = void(*)(QRestReply&);
+ template <typename Functor>
+ using ContextTypeForFunctor = typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType;
+ template <typename Functor>
+ using if_compatible_callback = std::enable_if_t<
+ QtPrivate::AreFunctionsCompatible<CallbackPrototype, Functor>::value, bool>;
+public:
+ explicit QRestAccessManager(QNetworkAccessManager *manager, QObject *parent = nullptr);
+ ~QRestAccessManager() override;
+
+ QNetworkAccessManager *networkAccessManager() const;
+
+ QREST_METHOD_NO_DATA(deleteResource)
+ QREST_METHOD_NO_DATA(head)
+ QREST_METHOD_NO_DATA(get)
+ QREST_METHOD_WITH_DATA(get, const QByteArray &)
+ QREST_METHOD_WITH_DATA(get, const QJsonDocument &)
+ QREST_METHOD_WITH_DATA(get, QIODevice *)
+ QREST_METHOD_WITH_DATA(post, const QJsonDocument &)
+ QREST_METHOD_WITH_DATA(post, const QVariantMap &)
+ QREST_METHOD_WITH_DATA(post, const QByteArray &)
+ QREST_METHOD_WITH_DATA(post, QHttpMultiPart *)
+ QREST_METHOD_WITH_DATA(post, QIODevice *)
+ QREST_METHOD_WITH_DATA(put, const QJsonDocument &)
+ QREST_METHOD_WITH_DATA(put, const QVariantMap &)
+ QREST_METHOD_WITH_DATA(put, const QByteArray &)
+ QREST_METHOD_WITH_DATA(put, QHttpMultiPart *)
+ QREST_METHOD_WITH_DATA(put, QIODevice *)
+ QREST_METHOD_WITH_DATA(patch, const QJsonDocument &)
+ QREST_METHOD_WITH_DATA(patch, const QVariantMap &)
+ QREST_METHOD_WITH_DATA(patch, const QByteArray &)
+ QREST_METHOD_WITH_DATA(patch, QIODevice *)
+ QREST_METHOD_CUSTOM_WITH_DATA(const QByteArray &)
+ QREST_METHOD_CUSTOM_WITH_DATA(QIODevice *)
+ QREST_METHOD_CUSTOM_WITH_DATA(QHttpMultiPart *)
+
+private:
+ Q_DECLARE_PRIVATE(QRestAccessManager)
+ Q_DISABLE_COPY(QRestAccessManager)
+};
+
+#undef QREST_METHOD_NO_DATA
+#undef QREST_METHOD_WITH_DATA
+#undef QREST_METHOD_CUSTOM_WITH_DATA
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/qrestaccessmanager_p.h b/src/network/access/qrestaccessmanager_p.h
new file mode 100644
index 0000000000..51af299f5c
--- /dev/null
+++ b/src/network/access/qrestaccessmanager_p.h
@@ -0,0 +1,89 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QRESTACCESSMANAGER_P_H
+#define QRESTACCESSMANAGER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qrestaccessmanager.h"
+#include "private/qobject_p.h"
+
+#include <QtNetwork/qnetworkaccessmanager.h>
+
+#include <QtCore/qjsonarray.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qxpfunctional.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRestReply;
+class QRestAccessManagerPrivate : public QObjectPrivate
+{
+public:
+ QRestAccessManagerPrivate();
+ ~QRestAccessManagerPrivate() override;
+
+ QNetworkReply* createActiveRequest(QNetworkReply *reply, const QObject *contextObject,
+ QtPrivate::QSlotObjectBase *slot);
+ void handleReplyFinished(QNetworkReply *reply);
+
+ using ReqOpRef = qxp::function_ref<QNetworkReply*(QNetworkAccessManager*) const>;
+ QNetworkReply *executeRequest(ReqOpRef requestOperation,
+ const QObject *context, QtPrivate::QSlotObjectBase *slot)
+ {
+ if (!qnam)
+ return warnNoAccessManager();
+ verifyThreadAffinity(context);
+ QNetworkReply *reply = requestOperation(qnam);
+ return createActiveRequest(reply, context, slot);
+ }
+
+ using ReqOpRefJson = qxp::function_ref<QNetworkReply*(QNetworkAccessManager*,
+ const QNetworkRequest &,
+ const QByteArray &) const>;
+ QNetworkReply *executeRequest(ReqOpRefJson requestOperation, const QJsonDocument &jsonDoc,
+ const QNetworkRequest &request,
+ const QObject *context, QtPrivate::QSlotObjectBase *slot)
+ {
+ if (!qnam)
+ return warnNoAccessManager();
+ verifyThreadAffinity(context);
+ QNetworkRequest req(request);
+ if (!request.header(QNetworkRequest::ContentTypeHeader).isValid()) {
+ req.setHeader(QNetworkRequest::ContentTypeHeader,
+ QLatin1StringView{"application/json"});
+ }
+ QNetworkReply *reply = requestOperation(qnam, req, jsonDoc.toJson(QJsonDocument::Compact));
+ return createActiveRequest(reply, context, slot);
+ }
+
+ void verifyThreadAffinity(const QObject *contextObject);
+ Q_DECL_COLD_FUNCTION
+ QNetworkReply* warnNoAccessManager();
+
+ struct CallerInfo {
+ QPointer<const QObject> contextObject = nullptr;
+ QtPrivate::SlotObjSharedPtr slot;
+ };
+ QHash<QNetworkReply*, CallerInfo> activeRequests;
+
+ QNetworkAccessManager *qnam = nullptr;
+ bool deletesRepliesOnFinished = true;
+ Q_DECLARE_PUBLIC(QRestAccessManager)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/qrestreply.cpp b/src/network/access/qrestreply.cpp
new file mode 100644
index 0000000000..155e5877fd
--- /dev/null
+++ b/src/network/access/qrestreply.cpp
@@ -0,0 +1,560 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qrestreply.h"
+#include "qrestreply_p.h"
+
+#include <QtNetwork/private/qnetworkreply_p.h>
+
+#include <QtCore/qbytearrayview.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qlatin1stringmatcher.h>
+#include <QtCore/qlatin1stringview.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qstringconverter.h>
+
+#include <QtCore/qxpfunctional.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+Q_DECLARE_LOGGING_CATEGORY(lcQrest)
+
+/*!
+ \class QRestReply
+ \since 6.7
+ \brief QRestReply is a convenience wrapper for QNetworkReply.
+
+ \reentrant
+ \ingroup network
+ \inmodule QtNetwork
+
+ \preliminary
+
+ QRestReply wraps a QNetworkReply and provides convenience methods for data
+ and status handling. The methods provide convenience for typical RESTful
+ client applications.
+
+ QRestReply doesn't take ownership of the wrapped QNetworkReply, and the
+ lifetime and ownership of the reply is as defined by QNetworkAccessManager
+ documentation.
+
+ QRestReply object is not copyable, but is movable.
+
+ \sa QRestAccessManager, QNetworkReply, QNetworkAccessManager,
+ QNetworkAccessManager::setAutoDeleteReplies()
+*/
+
+/*!
+ Creates a QRestReply and initializes the wrapped QNetworkReply to \a reply.
+*/
+QRestReply::QRestReply(QNetworkReply *reply)
+ : wrapped(reply)
+{
+ if (!wrapped)
+ qCWarning(lcQrest, "QRestReply: QNetworkReply is nullptr");
+}
+
+/*!
+ Destroys this QRestReply object.
+*/
+QRestReply::~QRestReply()
+{
+ delete d;
+}
+
+/*!
+ \fn QRestReply::QRestReply(QRestReply &&other) noexcept
+
+ Move-constructs the reply from \a other.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new value.
+*/
+
+/*!
+ \fn QRestReply &QRestReply::operator=(QRestReply &&other) noexcept
+
+ Move-assigns \a other and returns a reference to this reply.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new value.
+*/
+
+/*!
+ Returns a pointer to the underlying QNetworkReply wrapped by this object.
+*/
+QNetworkReply *QRestReply::networkReply() const
+{
+ return wrapped;
+}
+
+/*!
+ Returns the received data as a QJsonDocument.
+
+ The returned value is wrapped in \c std::optional. If the conversion
+ from the received data fails (empty data or JSON parsing error),
+ \c std::nullopt is returned, and \a error is filled with details.
+
+ Calling this function consumes the received data, and any further calls
+ to get response data will return empty.
+
+ This function returns \c {std::nullopt} and will not consume
+ any data if the reply is not finished. If \a error is passed, it will be
+ set to QJsonParseError::NoError to distinguish this case from an actual
+ error.
+
+ \sa readBody(), readText()
+*/
+std::optional<QJsonDocument> QRestReply::readJson(QJsonParseError *error)
+{
+ if (!wrapped) {
+ if (error)
+ *error = {0, QJsonParseError::ParseError::NoError};
+ return std::nullopt;
+ }
+
+ if (!wrapped->isFinished()) {
+ qCWarning(lcQrest, "readJson() called on an unfinished reply, ignoring");
+ if (error)
+ *error = {0, QJsonParseError::ParseError::NoError};
+ return std::nullopt;
+ }
+ QJsonParseError parseError;
+ const QByteArray data = wrapped->readAll();
+ const QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);
+ if (error)
+ *error = parseError;
+ if (parseError.error)
+ return std::nullopt;
+ return doc;
+}
+
+/*!
+ Returns the received data as a QByteArray.
+
+ Calling this function consumes the data received so far, and any further
+ calls to get response data will return empty until further data has been
+ received.
+
+ \sa readJson(), readText(), QNetworkReply::bytesAvailable(),
+ QNetworkReply::readyRead()
+*/
+QByteArray QRestReply::readBody()
+{
+ return wrapped ? wrapped->readAll() : QByteArray{};
+}
+
+/*!
+ Returns the received data as a QString.
+
+ The received data is decoded into a QString (UTF-16). If available, the decoding
+ uses the \e Content-Type header's \e charset parameter to determine the
+ source encoding. If the encoding information is not available or not supported
+ by \l QStringConverter, UTF-8 is used by default.
+
+ Calling this function consumes the data received so far. Returns
+ a default constructed value if no new data is available, or if the
+ decoding is not supported by \l QStringConverter, or if the decoding
+ has errors (for example invalid characters).
+
+ \sa readJson(), readBody(), QNetworkReply::readyRead()
+*/
+QString QRestReply::readText()
+{
+ QString result;
+ if (!wrapped)
+ return result;
+
+ QByteArray data = wrapped->readAll();
+ if (data.isEmpty())
+ return result;
+
+ // Text decoding needs to persist decoding state across calls to this function,
+ // so allocate decoder if not yet allocated.
+ if (!d)
+ d = new QRestReplyPrivate;
+
+ if (!d->decoder) {
+ const QByteArray charset = QRestReplyPrivate::contentCharset(wrapped);
+ d->decoder.emplace(charset.constData());
+ if (!d->decoder->isValid()) { // the decoder may not support the mimetype's charset
+ qCWarning(lcQrest, "readText(): Charset \"%s\" is not supported", charset.constData());
+ return result;
+ }
+ }
+ // Check if the decoder already had an error, or has errors after decoding current data chunk
+ if (d->decoder->hasError() || (result = (*d->decoder)(data), d->decoder->hasError())) {
+ qCWarning(lcQrest, "readText(): Decoding error occurred");
+ return {};
+ }
+ return result;
+}
+
+/*!
+ Returns the HTTP status received in the server response.
+ The value is \e 0 if not available (the status line has not been received,
+ yet).
+
+ \note The HTTP status is reported as indicated by the received HTTP
+ response. An error() may occur after receiving the status, for instance
+ due to network disconnection while receiving a long response.
+ These potential subsequent errors are not represented by the reported
+ HTTP status.
+
+ \sa isSuccess(), hasError(), error()
+*/
+int QRestReply::httpStatus() const
+{
+ return wrapped ? wrapped->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() : 0;
+}
+
+/*!
+ \fn bool QRestReply::isSuccess() const
+
+ Returns whether the HTTP status is between 200..299 and no
+ further errors have occurred while receiving the response (for example,
+ abrupt disconnection while receiving the body data). This function
+ is a convenient way to check whether the response is considered successful.
+
+ \sa httpStatus(), hasError(), error()
+*/
+
+/*!
+ Returns whether the HTTP status is between 200..299.
+
+ \sa isSuccess(), httpStatus(), hasError(), error()
+*/
+bool QRestReply::isHttpStatusSuccess() const
+{
+ const int status = httpStatus();
+ return status >= 200 && status < 300;
+}
+
+/*!
+ Returns whether an error has occurred. This includes errors such as
+ network and protocol errors, but excludes cases where the server
+ successfully responded with an HTTP error status (for example
+ \c {500 Internal Server Error}). Use \l httpStatus() or
+ \l isHttpStatusSuccess() to get the HTTP status information.
+
+ \sa httpStatus(), isSuccess(), error(), errorString()
+*/
+bool QRestReply::hasError() const
+{
+ if (!wrapped)
+ return false;
+
+ const int status = httpStatus();
+ if (status > 0) {
+ // The HTTP status is set upon receiving the response headers, but the
+ // connection might still fail later while receiving the body data.
+ return wrapped->error() == QNetworkReply::RemoteHostClosedError;
+ }
+ return wrapped->error() != QNetworkReply::NoError;
+}
+
+/*!
+ Returns the last error, if any. The errors include
+ errors such as network and protocol errors, but exclude
+ cases when the server successfully responded with an HTTP status.
+
+ \sa httpStatus(), isSuccess(), hasError(), errorString()
+*/
+QNetworkReply::NetworkError QRestReply::error() const
+{
+ if (!hasError())
+ return QNetworkReply::NetworkError::NoError;
+ return wrapped->error();
+}
+
+/*!
+ Returns a human-readable description of the last network error.
+
+ \sa httpStatus(), isSuccess(), hasError(), error()
+*/
+QString QRestReply::errorString() const
+{
+ if (hasError())
+ return wrapped->errorString();
+ return {};
+}
+
+QRestReplyPrivate::QRestReplyPrivate()
+ = default;
+
+QRestReplyPrivate::~QRestReplyPrivate()
+ = default;
+
+#ifndef QT_NO_DEBUG_STREAM
+static QLatin1StringView operationName(QNetworkAccessManager::Operation operation)
+{
+ switch (operation) {
+ case QNetworkAccessManager::Operation::GetOperation:
+ return "GET"_L1;
+ case QNetworkAccessManager::Operation::HeadOperation:
+ return "HEAD"_L1;
+ case QNetworkAccessManager::Operation::PostOperation:
+ return "POST"_L1;
+ case QNetworkAccessManager::Operation::PutOperation:
+ return "PUT"_L1;
+ case QNetworkAccessManager::Operation::DeleteOperation:
+ return "DELETE"_L1;
+ case QNetworkAccessManager::Operation::CustomOperation:
+ return "CUSTOM"_L1;
+ case QNetworkAccessManager::Operation::UnknownOperation:
+ return "UNKNOWN"_L1;
+ }
+ Q_UNREACHABLE_RETURN({});
+}
+
+/*!
+ \fn QDebug QRestReply::operator<<(QDebug debug, const QRestReply &reply)
+
+ Writes the \a reply into the \a debug object for debugging purposes.
+
+ \sa {Debugging Techniques}
+*/
+QDebug operator<<(QDebug debug, const QRestReply &reply)
+{
+ const QDebugStateSaver saver(debug);
+ debug.resetFormat().nospace();
+ if (!reply.networkReply()) {
+ debug << "QRestReply(no network reply)";
+ return debug;
+ }
+ debug << "QRestReply(isSuccess = " << reply.isSuccess()
+ << ", httpStatus = " << reply.httpStatus()
+ << ", isHttpStatusSuccess = " << reply.isHttpStatusSuccess()
+ << ", hasError = " << reply.hasError()
+ << ", errorString = " << reply.errorString()
+ << ", error = " << reply.error()
+ << ", isFinished = " << reply.networkReply()->isFinished()
+ << ", bytesAvailable = " << reply.networkReply()->bytesAvailable()
+ << ", url " << reply.networkReply()->url()
+ << ", operation = " << operationName(reply.networkReply()->operation())
+ << ", reply headers = " << reply.networkReply()->rawHeaderPairs()
+ << ")";
+ return debug;
+}
+#endif // QT_NO_DEBUG_STREAM
+
+static constexpr auto parse_OWS(QByteArrayView data) noexcept
+{
+ struct R {
+ QByteArrayView ows, tail;
+ };
+
+ constexpr auto is_OWS_char = [](auto ch) { return ch == ' ' || ch == '\t'; };
+
+ qsizetype i = 0;
+ while (i < data.size() && is_OWS_char(data[i]))
+ ++i;
+
+ return R{data.first(i), data.sliced(i)};
+}
+
+static constexpr void eat_OWS(QByteArrayView &data) noexcept
+{
+ data = parse_OWS(data).tail;
+}
+
+static constexpr auto parse_quoted_string(QByteArrayView data, qxp::function_ref<void(char) const> yield)
+{
+ struct R {
+ QByteArrayView quotedString, tail;
+ constexpr explicit operator bool() const noexcept { return !quotedString.isEmpty(); }
+ };
+
+ if (!data.startsWith('"'))
+ return R{{}, data};
+
+ qsizetype i = 1; // one past initial DQUOTE
+ while (i < data.size()) {
+ switch (auto ch = data[i++]) {
+ case '"': // final DQUOTE -> end of string
+ return R{data.first(i), data.sliced(i)};
+ case '\\': // quoted-pair
+ // https://www.rfc-editor.org/rfc/rfc9110.html#section-5.6.4-3:
+ // Recipients that process the value of a quoted-string MUST handle a
+ // quoted-pair as if it were replaced by the octet following the backslash.
+ if (i == data.size())
+ break; // premature end
+ ch = data[i++]; // eat '\\'
+ [[fallthrough]];
+ default:
+ // we don't validate quoted-string octets to be only qdtext (Postel's Law)
+ yield(ch);
+ }
+ }
+
+ return R{{}, data}; // premature end
+}
+
+static constexpr bool is_tchar(char ch) noexcept
+{
+ // ### optimize
+ switch (ch) {
+ case '!':
+ case '#':
+ case '$':
+ case '%':
+ case '&':
+ case '\'':
+ case '*':
+ case '+':
+ case '-':
+ case '.':
+ case '^':
+ case '_':
+ case '`':
+ case '|':
+ case '~':
+ return true;
+ default:
+ return (ch >= 'a' && ch <= 'z')
+ || (ch >= '0' && ch <= '9')
+ || (ch >= 'A' && ch <= 'Z');
+ }
+}
+
+static constexpr auto parse_token(QByteArrayView data) noexcept
+{
+ struct R {
+ QByteArrayView token, tail;
+ constexpr explicit operator bool() const noexcept { return !token.isEmpty(); }
+ };
+
+ qsizetype i = 0;
+ while (i < data.size() && is_tchar(data[i]))
+ ++i;
+
+ return R{data.first(i), data.sliced(i)};
+}
+
+static constexpr auto parse_parameter(QByteArrayView data, qxp::function_ref<void(char) const> yield)
+{
+ struct R {
+ QLatin1StringView name; QByteArrayView value; QByteArrayView tail;
+ constexpr explicit operator bool() const noexcept { return !name.isEmpty(); }
+ };
+
+ const auto invalid = R{{}, {}, data}; // preserves original `data`
+
+ // parameter = parameter-name "=" parameter-value
+ // parameter-name = token
+ // parameter-value = ( token / quoted-string )
+
+ const auto name = parse_token(data);
+ if (!name)
+ return invalid;
+ data = name.tail;
+
+ eat_OWS(data); // not in the grammar, but accepted under Postel's Law
+
+ if (!data.startsWith('='))
+ return invalid;
+ data = data.sliced(1);
+
+ eat_OWS(data); // not in the grammar, but accepted under Postel's Law
+
+ if (Q_UNLIKELY(data.startsWith('"'))) { // value is a quoted-string
+
+ const auto value = parse_quoted_string(data, yield);
+ if (!value)
+ return invalid;
+ data = value.tail;
+
+ return R{QLatin1StringView{name.token}, value.quotedString, data};
+
+ } else { // value is a token
+
+ const auto value = parse_token(data);
+ if (!value)
+ return invalid;
+ data = value.tail;
+
+ return R{QLatin1StringView{name.token}, value.token, data};
+ }
+}
+
+static auto parse_content_type(QByteArrayView data)
+{
+ struct R {
+ QLatin1StringView type, subtype;
+ std::string charset;
+ constexpr explicit operator bool() const noexcept { return !type.isEmpty(); }
+ };
+
+ eat_OWS(data); // not in the grammar, but accepted under Postel's Law
+
+ const auto type = parse_token(data);
+ if (!type)
+ return R{};
+ data = type.tail;
+
+ eat_OWS(data); // not in the grammar, but accepted under Postel's Law
+
+ if (!data.startsWith('/'))
+ return R{};
+ data = data.sliced(1);
+
+ eat_OWS(data); // not in the grammar, but accepted under Postel's Law
+
+ const auto subtype = parse_token(data);
+ if (!subtype)
+ return R{};
+ data = subtype.tail;
+
+ eat_OWS(data);
+
+ auto r = R{QLatin1StringView{type.token}, QLatin1StringView{subtype.token}, {}};
+
+ while (data.startsWith(';')) {
+
+ data = data.sliced(1); // eat ';'
+
+ eat_OWS(data);
+
+ const auto param = parse_parameter(data, [&](char ch) { r.charset.append(1, ch); });
+ if (param.name.compare("charset"_L1, Qt::CaseInsensitive) == 0) {
+ if (r.charset.empty() && !param.value.startsWith('"')) // wasn't a quoted-string
+ r.charset.assign(param.value.begin(), param.value.end());
+ return r; // charset found
+ }
+ r.charset.clear(); // wasn't an actual charset
+ if (param.tail.size() == data.size()) // no progress was made
+ break; // returns {type, subtype}
+ // otherwise, continue (accepting e.g. `;;`)
+ data = param.tail;
+
+ eat_OWS(data);
+ }
+
+ return r; // no charset found
+}
+
+QByteArray QRestReplyPrivate::contentCharset(const QNetworkReply* reply)
+{
+ // Content-type consists of mimetype and optional parameters, of which one may be 'charset'
+ // Example values and their combinations below are all valid, see RFC 7231 section 3.1.1.5
+ // and RFC 2045 section 5.1
+ //
+ // text/plain; charset=utf-8
+ // text/plain; charset=utf-8;version=1.7
+ // text/plain; charset = utf-8
+ // text/plain; charset ="utf-8"
+
+ const QByteArray contentTypeValue =
+ reply->header(QNetworkRequest::KnownHeaders::ContentTypeHeader).toByteArray();
+
+ const auto r = parse_content_type(contentTypeValue);
+ if (r && !r.charset.empty())
+ return QByteArrayView(r.charset).toByteArray();
+ else
+ return "UTF-8"_ba; // Default to the most commonly used UTF-8.
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qrestreply.h b/src/network/access/qrestreply.h
new file mode 100644
index 0000000000..c32fff1d4e
--- /dev/null
+++ b/src/network/access/qrestreply.h
@@ -0,0 +1,71 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QRESTREPLY_H
+#define QRESTREPLY_H
+
+#include <QtNetwork/qnetworkreply.h>
+
+#include <QtCore/qpointer.h>
+
+#include <optional>
+#include <utility>
+
+QT_BEGIN_NAMESPACE
+
+class QByteArray;
+class QDebug;
+struct QJsonParseError;
+class QJsonDocument;
+class QString;
+
+class QRestReplyPrivate;
+class QT_TECH_PREVIEW_API QRestReply
+{
+public:
+ Q_NETWORK_EXPORT explicit QRestReply(QNetworkReply *reply);
+ Q_NETWORK_EXPORT ~QRestReply();
+
+ QRestReply(QRestReply &&other) noexcept
+ : wrapped(std::move(other.wrapped)),
+ d(std::exchange(other.d, nullptr))
+ {
+ }
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QRestReply)
+ void swap(QRestReply &other) noexcept
+ {
+ wrapped.swap(other.wrapped);
+ qt_ptr_swap(d, other.d);
+ }
+
+ Q_NETWORK_EXPORT QNetworkReply *networkReply() const;
+
+ Q_NETWORK_EXPORT std::optional<QJsonDocument> readJson(QJsonParseError *error = nullptr);
+ Q_NETWORK_EXPORT QByteArray readBody();
+ Q_NETWORK_EXPORT QString readText();
+
+ bool isSuccess() const
+ {
+ return !hasError() && isHttpStatusSuccess();
+ }
+ Q_NETWORK_EXPORT int httpStatus() const;
+ Q_NETWORK_EXPORT bool isHttpStatusSuccess() const;
+
+ Q_NETWORK_EXPORT bool hasError() const;
+ Q_NETWORK_EXPORT QNetworkReply::NetworkError error() const;
+ Q_NETWORK_EXPORT QString errorString() const;
+
+private:
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QRestReply &reply);
+#endif
+ QPointer<QNetworkReply> wrapped;
+ QRestReplyPrivate *d = nullptr;
+ Q_DISABLE_COPY(QRestReply)
+};
+
+Q_DECLARE_SHARED(QRestReply)
+
+QT_END_NAMESPACE
+
+#endif // QRESTREPLY_H
diff --git a/src/network/access/qrestreply_p.h b/src/network/access/qrestreply_p.h
new file mode 100644
index 0000000000..ec963cf168
--- /dev/null
+++ b/src/network/access/qrestreply_p.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QRESTREPLY_P_H
+#define QRESTREPLY_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/private/qstringconverter_p.h>
+
+#include <optional>
+
+QT_BEGIN_NAMESPACE
+
+class QByteArray;
+class QNetworkReply;
+
+class QRestReplyPrivate
+{
+public:
+ QRestReplyPrivate();
+ ~QRestReplyPrivate();
+
+ std::optional<QStringDecoder> decoder;
+
+ static QByteArray contentCharset(const QNetworkReply *reply);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/qsocketabstraction_p.h b/src/network/access/qsocketabstraction_p.h
new file mode 100644
index 0000000000..2b40b80244
--- /dev/null
+++ b/src/network/access/qsocketabstraction_p.h
@@ -0,0 +1,91 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSOCKETABSTRACTION_P_H
+#define QSOCKETABSTRACTION_P_H
+
+#include <private/qtnetworkglobal_p.h>
+
+#include <QtNetwork/qabstractsocket.h>
+#include <QtNetwork/qlocalsocket.h>
+
+#include <QtCore/qxpfunctional.h>
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+QT_BEGIN_NAMESPACE
+
+// Helper functions to deal with a QIODevice that is either a socket or a local
+// socket.
+namespace QSocketAbstraction {
+template <typename Fn, typename... Args>
+auto visit(Fn &&fn, QIODevice *socket, Args &&...args)
+{
+ if (auto *s = qobject_cast<QAbstractSocket *>(socket))
+ return std::forward<Fn>(fn)(s, std::forward<Args>(args)...);
+ if (auto *s = qobject_cast<QLocalSocket *>(socket))
+ return std::forward<Fn>(fn)(s, std::forward<Args>(args)...);
+ Q_UNREACHABLE();
+}
+
+// Since QLocalSocket's LocalSocketState's values are defined as being equal
+// to some of QAbstractSocket's SocketState's values, we can use the superset
+// of the two as the return type.
+inline QAbstractSocket::SocketState socketState(QIODevice *device)
+{
+ auto getState = [](auto *s) {
+ using T = std::remove_pointer_t<decltype(s)>;
+ if constexpr (std::is_same_v<T, QAbstractSocket>) {
+ return s->state();
+ } else if constexpr (std::is_same_v<T, QLocalSocket>) {
+ QLocalSocket::LocalSocketState st = s->state();
+ return static_cast<QAbstractSocket::SocketState>(st);
+ }
+ Q_UNREACHABLE();
+ };
+ return visit(getState, device);
+}
+
+// Same as for socketState(), but for the errors
+inline QAbstractSocket::SocketError socketError(QIODevice *device)
+{
+ auto getError = [](auto *s) {
+ using T = std::remove_pointer_t<decltype(s)>;
+ if constexpr (std::is_same_v<T, QAbstractSocket>) {
+ return s->error();
+ } else if constexpr (std::is_same_v<T, QLocalSocket>) {
+ QLocalSocket::LocalSocketError st = s->error();
+ return static_cast<QAbstractSocket::SocketError>(st);
+ }
+ Q_UNREACHABLE();
+ };
+ return visit(getError, device);
+}
+
+inline QString socketPeerName(QIODevice *device)
+{
+ auto getPeerName = [](auto *s) {
+ using T = std::remove_pointer_t<decltype(s)>;
+ if constexpr (std::is_same_v<T, QAbstractSocket>) {
+ return s->peerName();
+ } else if constexpr (std::is_same_v<T, QLocalSocket>) {
+ return s->serverName();
+ }
+ Q_UNREACHABLE();
+ };
+ return visit(getPeerName, device);
+}
+} // namespace QSocketAbstraction
+
+QT_END_NAMESPACE
+
+#endif // QSOCKETABSTRACTION_P_H
diff --git a/src/network/android/jar/CMakeLists.txt b/src/network/android/jar/CMakeLists.txt
index 9e8b1b726a..b5172b6aba 100644
--- a/src/network/android/jar/CMakeLists.txt
+++ b/src/network/android/jar/CMakeLists.txt
@@ -1,20 +1,19 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
-
-# Generated from jar.pro.
+# SPDX-License-Identifier: BSD-3-Clause
set(java_sources
src/org/qtproject/qt/android/network/QtNetwork.java
)
-qt_internal_add_jar(Qt${QtBase_VERSION_MAJOR}AndroidNetwork # special case
+qt_internal_add_jar(Qt${QtBase_VERSION_MAJOR}AndroidNetwork
INCLUDE_JARS ${QT_ANDROID_JAR}
SOURCES ${java_sources}
OUTPUT_DIR "${QT_BUILD_DIR}/jar"
)
-install_jar(Qt${QtBase_VERSION_MAJOR}AndroidNetwork # special case
- DESTINATION jar
+qt_path_join(destination ${INSTALL_DATADIR} "jar")
+
+install_jar(Qt${QtBase_VERSION_MAJOR}AndroidNetwork
+ DESTINATION ${destination}
COMPONENT Devel
)
-
diff --git a/src/network/android/jar/build.gradle b/src/network/android/jar/build.gradle
index c947852f79..68a9381ad2 100644
--- a/src/network/android/jar/build.gradle
+++ b/src/network/android/jar/build.gradle
@@ -7,7 +7,7 @@ buildscript {
}
dependencies {
- classpath 'com.android.tools.build:gradle:7.0.2'
+ classpath 'com.android.tools.build:gradle:8.0.2'
}
}
@@ -23,12 +23,10 @@ repositories {
}
android {
- compileSdkVersion 31
- buildToolsVersion "31.0.3"
+ compileSdk 34
defaultConfig {
minSdkVersion 23
- targetSdkVersion 31
}
sourceSets {
diff --git a/src/network/compat/removed_api.cpp b/src/network/compat/removed_api.cpp
new file mode 100644
index 0000000000..86951d9222
--- /dev/null
+++ b/src/network/compat/removed_api.cpp
@@ -0,0 +1,67 @@
+// Copyright (c) 2023 LLC «V Kontakte»
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#define QT_NETWORK_BUILD_REMOVED_API
+
+#include "qtnetworkglobal.h"
+
+QT_USE_NAMESPACE
+
+#if QT_NETWORK_REMOVED_SINCE(6, 7)
+
+#include "qhostinfo.h"
+
+// static
+int QHostInfo::lookupHost(const QString &name, QObject *receiver, const char *member)
+{
+ const auto *r = receiver;
+ return lookupHost(name, r, member);
+}
+
+
+#include "qnetworkreply.h"
+
+QByteArray QNetworkReply::rawHeader(const QByteArray &headerName) const
+{
+ return rawHeader(qToByteArrayViewIgnoringNull(headerName));
+}
+
+bool QNetworkReply::hasRawHeader(const QByteArray &headerName) const
+{
+ return hasRawHeader(qToByteArrayViewIgnoringNull(headerName));
+}
+
+#include "qnetworkrequest.h"
+
+QByteArray QNetworkRequest::rawHeader(const QByteArray &headerName) const
+{
+ return rawHeader(qToByteArrayViewIgnoringNull(headerName));
+}
+
+bool QNetworkRequest::hasRawHeader(const QByteArray &headerName) const
+{
+ return hasRawHeader(qToByteArrayViewIgnoringNull(headerName));
+}
+
+#include "qnetworkcookie.h"
+
+QList<QNetworkCookie> QNetworkCookie::parseCookies(const QByteArray &cookieString)
+{
+ return parseCookies(qToByteArrayViewIgnoringNull(cookieString));
+}
+
+// #include "qotherheader.h"
+// // implement removed functions from qotherheader.h
+// order sections alphabetically
+
+#endif // QT_NETWORK_REMOVED_SINCE(6, 7)
+
+#if QT_NETWORK_REMOVED_SINCE(6, 8)
+
+#include "qnetworkrequest.h" // inlined API
+
+// #include "qotherheader.h"
+// // implement removed functions from qotherheader.h
+// order sections alphabetically
+
+#endif // QT_NETWORK_REMOVED_SINCE(6, 8)
diff --git a/src/network/configure.cmake b/src/network/configure.cmake
index 0a64ad0cb2..cda444b873 100644
--- a/src/network/configure.cmake
+++ b/src/network/configure.cmake
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+# SPDX-License-Identifier: BSD-3-Clause
#### Inputs
@@ -9,57 +9,10 @@
qt_find_package(WrapBrotli PROVIDED_TARGETS WrapBrotli::WrapBrotliDec MODULE_NAME network QMAKE_LIB brotli)
qt_find_package(Libproxy PROVIDED_TARGETS PkgConfig::Libproxy MODULE_NAME network QMAKE_LIB libproxy)
-qt_find_package(WrapOpenSSLHeaders PROVIDED_TARGETS WrapOpenSSLHeaders::WrapOpenSSLHeaders MODULE_NAME network)
-# openssl_headers
-qt_config_compile_test(openssl_headers
- LIBRARIES
- WrapOpenSSLHeaders::WrapOpenSSLHeaders
- CODE
-"#include <openssl/ssl.h>
-#include <openssl/opensslv.h>
-#if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER-0 < 0x10101000L
-# error OpenSSL >= 1.1.1 is required
-#endif
-#if !defined(OPENSSL_NO_EC) && !defined(SSL_CTRL_SET_CURVES)
-# error OpenSSL was reported as >= 1.1.1 but is missing required features, possibly it is libressl which is unsupported
-#endif
-
-int main(void)
-{
- /* BEGIN TEST: */
- /* END TEST: */
- return 0;
-}
-")
-
-qt_find_package(WrapOpenSSL PROVIDED_TARGETS WrapOpenSSL::WrapOpenSSL MODULE_NAME network QMAKE_LIB openssl)
-# openssl
-qt_config_compile_test(openssl
- LIBRARIES
- WrapOpenSSL::WrapOpenSSL
- CODE
-"#include <openssl/ssl.h>
-#include <openssl/opensslv.h>
-#if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER-0 < 0x10101000L
-# error OpenSSL >= 1.1.1 is required
-#endif
-#if !defined(OPENSSL_NO_EC) && !defined(SSL_CTRL_SET_CURVES)
-# error OpenSSL was reported as >= 1.1.1 but is missing required features, possibly it is libressl which is unsupported
-#endif
-
-int main(void)
-{
- /* BEGIN TEST: */
-SSL_free(SSL_new(0));
- /* END TEST: */
- return 0;
-}
-")
-
qt_find_package(GSSAPI PROVIDED_TARGETS GSSAPI::GSSAPI MODULE_NAME network QMAKE_LIB gssapi)
qt_find_package(GLIB2 OPTIONAL_COMPONENTS GOBJECT PROVIDED_TARGETS GLIB2::GOBJECT MODULE_NAME core QMAKE_LIB gobject)
qt_find_package(GLIB2 OPTIONAL_COMPONENTS GIO PROVIDED_TARGETS GLIB2::GIO MODULE_NAME core QMAKE_LIB gio)
-
+qt_find_package(WrapResolv PROVIDED_TARGETS WrapResolv::WrapResolv MODULE_NAME network QMAKE_LIB libresolv)
#### Tests
@@ -84,22 +37,6 @@ freeifaddrs(list);
"# FIXME: use: unmapped library: network
)
-# ifr_index
-qt_config_compile_test(ifr_index
- LABEL "ifr_index"
- CODE
-"#include <net/if.h>
-
-int main(void)
-{
- /* BEGIN TEST: */
-struct ifreq req;
-req.ifr_index = 0;
- /* END TEST: */
- return 0;
-}
-")
-
# ipv6ifname
qt_config_compile_test(ipv6ifname
LABEL "IPv6 ifname"
@@ -147,6 +84,25 @@ ci.ifa_prefered = ci.ifa_valid = 0;
}
")
+# res_setserver
+qt_config_compile_test(res_setservers
+ LABEL "res_setservers()"
+ LIBRARIES
+ WrapResolv::WrapResolv
+ CODE
+"#include <sys/types.h>
+#include <netinet/in.h>
+#include <resolv.h>
+int main()
+{
+ union res_sockaddr_union sa;
+ res_state s = nullptr;
+ res_setservers(s, &sa, 1);
+ return 0;
+}
+"
+)
+
# sctp
qt_config_compile_test(sctp
LABEL "SCTP support"
@@ -213,6 +169,7 @@ qt_config_compile_test(networklistmanager
LABEL "Network List Manager"
CODE
"#include <netlistmgr.h>
+#include <ocidl.h>
#include <wrl/client.h>
int main(void)
@@ -236,18 +193,19 @@ connectionPointContainer->FindConnectionPoint(IID_INetworkConnectionEvents, &con
qt_feature("getifaddrs" PUBLIC
LABEL "getifaddrs()"
- CONDITION TEST_getifaddrs
+ CONDITION VXWORKS OR UNIX AND NOT QT_FEATURE_linux_netlink AND TEST_getifaddrs
)
qt_feature_definition("getifaddrs" "QT_NO_GETIFADDRS" NEGATE VALUE "1")
-qt_feature("ifr_index" PRIVATE
- LABEL "ifr_index"
- CONDITION TEST_ifr_index
-)
qt_feature("ipv6ifname" PUBLIC
LABEL "IPv6 ifname"
- CONDITION TEST_ipv6ifname
+ CONDITION VXWORKS OR UNIX AND NOT QT_FEATURE_linux_netlink AND TEST_ipv6ifname
)
qt_feature_definition("ipv6ifname" "QT_NO_IPV6IFNAME" NEGATE VALUE "1")
+qt_feature("libresolv" PRIVATE
+ LABEL "libresolv"
+ CONDITION WrapResolv_FOUND
+ AUTODETECT UNIX
+)
qt_feature("libproxy" PRIVATE
LABEL "libproxy"
AUTODETECT OFF
@@ -257,26 +215,10 @@ qt_feature("linux-netlink" PRIVATE
LABEL "Linux AF_NETLINK"
CONDITION LINUX AND NOT ANDROID AND TEST_linux_netlink
)
-qt_feature("openssl" PRIVATE
- LABEL "OpenSSL"
- CONDITION QT_FEATURE_openssl_runtime OR QT_FEATURE_openssl_linked
- ENABLE false
+qt_feature("res_setservers" PRIVATE
+ LABEL "res_setservers()"
+ CONDITION QT_FEATURE_libresolv AND TEST_res_setservers
)
-qt_feature_definition("openssl" "QT_NO_OPENSSL" NEGATE)
-qt_feature_config("openssl" QMAKE_PUBLIC_QT_CONFIG)
-qt_feature("openssl-runtime"
- AUTODETECT NOT WASM
- CONDITION TEST_openssl_headers
- ENABLE INPUT_openssl STREQUAL 'yes' OR INPUT_openssl STREQUAL 'runtime'
- DISABLE INPUT_openssl STREQUAL 'no' OR INPUT_openssl STREQUAL 'linked' OR INPUT_ssl STREQUAL 'no'
-)
-qt_feature("openssl-linked" PRIVATE
- LABEL " Qt directly linked to OpenSSL"
- AUTODETECT OFF
- CONDITION TEST_openssl
- ENABLE INPUT_openssl STREQUAL 'linked'
-)
-qt_feature_definition("openssl-linked" "QT_LINKED_OPENSSL")
qt_feature("securetransport" PUBLIC
LABEL "SecureTransport"
CONDITION APPLE
@@ -304,11 +246,7 @@ qt_feature("ocsp" PUBLIC
SECTION "Networking"
LABEL "OCSP-stapling"
PURPOSE "Provides OCSP stapling support"
- CONDITION QT_FEATURE_opensslv11 AND TEST_ocsp
-)
-qt_feature("opensslv11" PUBLIC
- LABEL "OpenSSL 1.1"
- CONDITION QT_FEATURE_openssl
+ CONDITION QT_FEATURE_openssl AND TEST_ocsp
)
qt_feature("sctp" PUBLIC
LABEL "SCTP"
@@ -377,7 +315,7 @@ qt_feature("dnslookup" PUBLIC
SECTION "Networking"
LABEL "QDnsLookup"
PURPOSE "Provides API for DNS lookups."
- CONDITION NOT INTEGRITY
+ CONDITION QT_FEATURE_thread AND NOT INTEGRITY
)
qt_feature("gssapi" PUBLIC
SECTION "Networking"
@@ -401,8 +339,9 @@ qt_feature("networklistmanager" PRIVATE
)
qt_feature("topleveldomain" PUBLIC
SECTION "Networking"
- LABEL "qTopLevelDomain()"
- PURPOSE "Provides support for extracting the top level domain from URLs. If enabled, a binary dump of the Public Suffix List (http://www.publicsuffix.org, Mozilla License) is included. The data is then also used in QNetworkCookieJar::validateCookie."
+ LABEL "qIsEffectiveTLD()"
+ PURPOSE "Provides support for checking if a domain is a top level domain. If enabled, a binary dump of the Public Suffix List (http://www.publicsuffix.org, Mozilla License) is included. The data is used in QNetworkCookieJar."
+ AUTODETECT NOT WASM
DISABLE INPUT_publicsuffix STREQUAL "no"
)
qt_feature("publicsuffix-qt" PRIVATE
@@ -435,9 +374,6 @@ qt_configure_add_summary_entry(
ARGS "schannel"
CONDITION WIN32
)
-qt_configure_add_summary_entry(ARGS "openssl")
-qt_configure_add_summary_entry(ARGS "openssl-linked")
-qt_configure_add_summary_entry(ARGS "opensslv11")
qt_configure_add_summary_entry(ARGS "dtls")
qt_configure_add_summary_entry(ARGS "ocsp")
qt_configure_add_summary_entry(ARGS "sctp")
@@ -448,10 +384,3 @@ qt_configure_add_summary_entry(ARGS "topleveldomain")
qt_configure_add_summary_entry(ARGS "publicsuffix-qt")
qt_configure_add_summary_entry(ARGS "publicsuffix-system")
qt_configure_end_summary_section() # end of "Qt Network" section
-# special case begin
-qt_configure_add_report_entry(
- TYPE NOTE
- MESSAGE "When linking against OpenSSL, you can override the default library names through OPENSSL_LIBS. For example: OPENSSL_LIBS='-L/opt/ssl/lib -lssl -lcrypto' ./configure -openssl-linked"
- CONDITION NOT ANDROID AND QT_FEATURE_openssl_linked AND ( NOT TEST_openssl.source EQUAL 0 ) AND OPENSSL_ROOT_DIR STREQUAL '' AND INPUT_openssl.libs STREQUAL '' AND INPUT_openssl.libs.debug STREQUAL '' OR FIXME
-)
-# special case end
diff --git a/src/network/doc/images/network-examples.png b/src/network/doc/images/network-examples.png
deleted file mode 100644
index 15dfba850a..0000000000
--- a/src/network/doc/images/network-examples.png
+++ /dev/null
Binary files differ
diff --git a/src/network/doc/images/network-examples.webp b/src/network/doc/images/network-examples.webp
new file mode 100644
index 0000000000..4f6269ec8b
--- /dev/null
+++ b/src/network/doc/images/network-examples.webp
Binary files differ
diff --git a/src/network/doc/qtnetwork.qdocconf b/src/network/doc/qtnetwork.qdocconf
index ad6f5d2850..1e1162bdac 100644
--- a/src/network/doc/qtnetwork.qdocconf
+++ b/src/network/doc/qtnetwork.qdocconf
@@ -23,7 +23,7 @@ qhp.QtNetwork.subprojects.classes.sortPages = true
tagfile = ../../../doc/qtnetwork/qtnetwork.tags
-depends += qtcore qtgui qtdoc qmake qtcmake
+depends += qtcore qtgui qtdoc qmake qtcmake qtwidgets
headerdirs += ..
@@ -40,5 +40,12 @@ imagedirs += images \
navigation.landingpage = "Qt Network"
navigation.cppclassespage = "Qt Network C++ Classes"
-# Fail the documentation build if there are more warnings than the limit
+manifestmeta.highlighted.names = \
+ "QtNetwork/Fortune Client" \
+ "QtNetwork/Fortune Server" \
+ "QtNetwork/HTTP Client" \
+ "QtNetwork/Secure Socket Client" \
+ "QtNetwork/Torrent Example"
+
+# Enforce zero documentation warnings
warninglimit = 0
diff --git a/src/network/doc/snippets/CMakeLists.txt b/src/network/doc/snippets/CMakeLists.txt
index 1fdd9c7968..05e5668e24 100644
--- a/src/network/doc/snippets/CMakeLists.txt
+++ b/src/network/doc/snippets/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
cmake_minimum_required(VERSION 3.16)
diff --git a/src/network/doc/snippets/code/doc_src_qtnetwork.cpp b/src/network/doc/snippets/code/doc_src_qtnetwork.cpp
deleted file mode 100644
index dae56d5db5..0000000000
--- a/src/network/doc/snippets/code/doc_src_qtnetwork.cpp
+++ /dev/null
@@ -1,6 +0,0 @@
-// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-
-//! [1]
-#include <QtNetwork>
-//! [1]
diff --git a/src/network/doc/snippets/code/src_network_access_qhttpmultipart.cpp b/src/network/doc/snippets/code/src_network_access_qhttpmultipart.cpp
index cf51c68861..73308b5547 100644
--- a/src/network/doc/snippets/code/src_network_access_qhttpmultipart.cpp
+++ b/src/network/doc/snippets/code/src_network_access_qhttpmultipart.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
diff --git a/src/network/doc/snippets/code/src_network_access_qhttppart.cpp b/src/network/doc/snippets/code/src_network_access_qhttppart.cpp
index 38d1b7aff0..059a4c60e9 100644
--- a/src/network/doc/snippets/code/src_network_access_qhttppart.cpp
+++ b/src/network/doc/snippets/code/src_network_access_qhttppart.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2016 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
Content-Type: text/plain
diff --git a/src/network/doc/snippets/code/src_network_access_qnetworkdiskcache.cpp b/src/network/doc/snippets/code/src_network_access_qnetworkdiskcache.cpp
index f830439124..1087f52035 100644
--- a/src/network/doc/snippets/code/src_network_access_qnetworkdiskcache.cpp
+++ b/src/network/doc/snippets/code/src_network_access_qnetworkdiskcache.cpp
@@ -4,17 +4,20 @@
//! [0]
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
QNetworkDiskCache *diskCache = new QNetworkDiskCache(this);
-diskCache->setCacheDirectory("cacheDir");
+QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation)
+ + QLatin1StringView("/cacheDir/");
+diskCache->setCacheDirectory(directory);
manager->setCache(diskCache);
//! [0]
//! [1]
+using namespace Qt::StringLiterals;
// do a normal request (preferred from network, as this is the default)
-QNetworkRequest request(QUrl(QString("http://qt-project.org")));
+QNetworkRequest request(QUrl(u"http://qt-project.org"_s));
manager->get(request);
// do a request preferred from cache
-QNetworkRequest request2(QUrl(QString("http://qt-project.org")));
+QNetworkRequest request2(QUrl(u"http://qt-project.org"_s));
request2.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
manager->get(request2);
//! [1]
diff --git a/src/network/doc/snippets/code/src_network_access_qnetworkrequestfactory.cpp b/src/network/doc/snippets/code/src_network_access_qnetworkrequestfactory.cpp
new file mode 100644
index 0000000000..f7994d442c
--- /dev/null
+++ b/src/network/doc/snippets/code/src_network_access_qnetworkrequestfactory.cpp
@@ -0,0 +1,27 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+using namespace Qt::StringLiterals;
+
+//! [0]
+// Instantiate a factory somewhere suitable in the application
+QNetworkRequestFactory api{{"https://example.com/v1"_L1}};
+
+// Set bearer token
+api.setBearerToken("my_token");
+
+// Issue requests (reply handling omitted for brevity)
+manager.get(api.createRequest("models"_L1)); // https://example.com/v1/models
+// The conventional leading '/' for the path can be used as well
+manager.get(api.createRequest("/models"_L1)); // https://example.com/v1/models
+//! [0]
+
+
+//! [1]
+// Here the API version v2 is used as the base path:
+QNetworkRequestFactory api{{"https://example.com/v2"_L1}};
+// ...
+manager.get(api.createRequest("models"_L1)); // https://example.com/v2/models
+// Equivalent with a leading '/'
+manager.get(api.createRequest("/models"_L1)); // https://example.com/v2/models
+//! [1]
+
diff --git a/src/network/doc/snippets/code/src_network_access_qrestaccessmanager.cpp b/src/network/doc/snippets/code/src_network_access_qrestaccessmanager.cpp
new file mode 100644
index 0000000000..8f9e00f4b6
--- /dev/null
+++ b/src/network/doc/snippets/code/src_network_access_qrestaccessmanager.cpp
@@ -0,0 +1,104 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+//! [0]
+QNetworkReply *reply = manager->get(request);
+QObject::connect(reply, &QNetworkReply::finished, this, [reply]() {
+ // The reply may be wrapped in the finish handler:
+ QRestReply restReply(reply);
+ if (restReply.isSuccess())
+ // ...
+});
+//! [0]
+
+
+//! [1]
+// With lambda
+manager->get(request, this, [this](QRestReply &reply) {
+ if (reply.isSuccess()) {
+ // ...
+ }
+});
+// With member function
+manager->get(request, this, &MyClass::handleFinished);
+//! [1]
+
+
+//! [2]
+QJsonDocument myJson;
+// ...
+manager->post(request, myJson, this, [this](QRestReply &reply) {
+ if (!reply.isSuccess()) {
+ // ...
+ }
+ if (std::optional json = reply.readJson()) {
+ // use *json
+ }
+});
+//! [2]
+
+
+//! [3]
+manager->get(request, this, [this](QRestReply &reply) {
+ if (!reply.isSuccess())
+ // handle error
+ if (std::optional json = reply.readJson())
+ // use *json
+});
+//! [3]
+
+
+//! [4]
+manager->get(request, myData, this, [this](QRestReply &reply) {
+ if (reply.isSuccess())
+ // ...
+});
+//! [4]
+
+
+//! [5]
+manager->post(request, myData, this, [this](QRestReply &reply) {
+ if (reply.isSuccess())
+ // ...
+});
+//! [5]
+
+
+//! [6]
+manager->put(request, myData, this, [this](QRestReply &reply) {
+ if (reply.isSuccess())
+ // ...
+});
+//! [6]
+
+
+//! [7]
+manager->head(request, this, [this](QRestReply &reply) {
+ if (reply.isSuccess())
+ // ...
+});
+//! [7]
+
+
+//! [8]
+manager->deleteResource(request, this, [this](QRestReply &reply) {
+ if (reply.isSuccess())
+ // ...
+});
+//! [8]
+
+
+//! [9]
+manager->sendCustomRequest(request, "MYMETHOD", myData, this, [this](QRestReply &reply) {
+ if (reply.isSuccess())
+ // ...
+});
+//! [9]
+
+
+//! [10]
+manager->patch(request, myData, this, [this](QRestReply &reply) {
+ if (reply.isSuccess())
+ // ...
+});
+//! [10]
diff --git a/src/network/doc/snippets/code/src_network_kernel_qdnslookup.cpp b/src/network/doc/snippets/code/src_network_kernel_qdnslookup.cpp
index fdcc8b0ce4..ce976001bb 100644
--- a/src/network/doc/snippets/code/src_network_kernel_qdnslookup.cpp
+++ b/src/network/doc/snippets/code/src_network_kernel_qdnslookup.cpp
@@ -6,8 +6,7 @@ void MyObject::lookupServers()
{
// Create a DNS lookup.
dns = new QDnsLookup(this);
- connect(dns, SIGNAL(finished()),
- this, SLOT(handleServers()));
+ connect(dns, &QDnsLookup::finished, this, &MyObject::handleServers);
// Find the XMPP servers for gmail.com
dns->setType(QDnsLookup::SRV);
diff --git a/src/network/doc/snippets/code/src_network_kernel_qhostinfo.cpp b/src/network/doc/snippets/code/src_network_kernel_qhostinfo.cpp
index b845e5bcc7..7c39827b94 100644
--- a/src/network/doc/snippets/code/src_network_kernel_qhostinfo.cpp
+++ b/src/network/doc/snippets/code/src_network_kernel_qhostinfo.cpp
@@ -3,12 +3,10 @@
//! [0]
// To find the IP address of qt-project.org
-QHostInfo::lookupHost("qt-project.org",
- this, SLOT(printResults(QHostInfo)));
+QHostInfo::lookupHost("qt-project.org", this, &MyWidget::printResults);
// To find the host name for 4.2.2.1
-QHostInfo::lookupHost("4.2.2.1",
- this, SLOT(printResults(QHostInfo)));
+QHostInfo::lookupHost("4.2.2.1", this, &MyWidget::printResults);
//! [0]
@@ -18,8 +16,7 @@ QHostInfo info = QHostInfo::fromName("qt-project.org");
//! [2]
-QHostInfo::lookupHost("www.kde.org",
- this, SLOT(lookedUp(QHostInfo)));
+QHostInfo::lookupHost("www.kde.org", this, &MyWidget::lookedUp);
//! [2]
@@ -39,8 +36,7 @@ void MyWidget::lookedUp(const QHostInfo &host)
//! [4]
-QHostInfo::lookupHost("4.2.2.1",
- this, SLOT(lookedUp(QHostInfo)));
+QHostInfo::lookupHost("4.2.2.1", this, &MyWidget::lookedUp);
//! [4]
diff --git a/src/network/doc/snippets/code/src_network_kernel_qnetworkinterface.cpp b/src/network/doc/snippets/code/src_network_kernel_qnetworkinterface.cpp
index 4f3839532d..d34750aaaf 100644
--- a/src/network/doc/snippets/code/src_network_kernel_qnetworkinterface.cpp
+++ b/src/network/doc/snippets/code/src_network_kernel_qnetworkinterface.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
QNetworkInterface::interfaceFromName(name).index()
diff --git a/src/network/doc/snippets/code/src_network_ssl_qsslconfiguration.cpp b/src/network/doc/snippets/code/src_network_ssl_qsslconfiguration.cpp
index edc542ac3f..d9a493d1ce 100644
--- a/src/network/doc/snippets/code/src_network_ssl_qsslconfiguration.cpp
+++ b/src/network/doc/snippets/code/src_network_ssl_qsslconfiguration.cpp
@@ -6,10 +6,3 @@ QSslConfiguration config = sslSocket.sslConfiguration();
config.setProtocol(QSsl::TlsV1_2);
sslSocket.setSslConfiguration(config);
//! [0]
-
-
-//! [1]
-QSslConfiguration tlsConfig = QSslConfiguration::defaultConfiguration();
-tlsConfig.setCiphers(QStringLiteral("DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:AES256-SHA"));
-//! [1]
-
diff --git a/src/network/doc/snippets/code/src_network_ssl_qsslpresharedkeyauthenticator.cpp b/src/network/doc/snippets/code/src_network_ssl_qsslpresharedkeyauthenticator.cpp
index 1af356ee00..b3242e057d 100644
--- a/src/network/doc/snippets/code/src_network_ssl_qsslpresharedkeyauthenticator.cpp
+++ b/src/network/doc/snippets/code/src_network_ssl_qsslpresharedkeyauthenticator.cpp
@@ -1,5 +1,5 @@
// Copyright (C) 2018 The Qt Company Ltd.
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
//! [0]
connect(socket, &QSslSocket::preSharedKeyAuthenticationRequired,
diff --git a/src/network/doc/snippets/code/src_network_ssl_qsslsocket.cpp b/src/network/doc/snippets/code/src_network_ssl_qsslsocket.cpp
index 6ad795fcb9..eed032589e 100644
--- a/src/network/doc/snippets/code/src_network_ssl_qsslsocket.cpp
+++ b/src/network/doc/snippets/code/src_network_ssl_qsslsocket.cpp
@@ -5,7 +5,7 @@ using namespace Qt::StringLiterals;
//! [0]
QSslSocket *socket = new QSslSocket(this);
-connect(socket, SIGNAL(encrypted()), this, SLOT(ready()));
+connect(socket, &QSslSocket::encrypted, this, &Receiver::ready);
socket->connectToHostEncrypted("imap.example.com", 993);
//! [0]
@@ -42,19 +42,13 @@ while (socket.waitForReadyRead())
//! [3]
QSslSocket socket;
-connect(&socket, SIGNAL(encrypted()), receiver, SLOT(socketEncrypted()));
+connect(&socket, &QSslSocket::encrypted, receiver, &Receiver::socketEncrypted);
socket.connectToHostEncrypted("imap", 993);
socket->write("1 CAPABILITY\r\n");
//! [3]
-//! [4]
-QSslSocket socket;
-socket.setCiphers("DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:AES256-SHA");
-//! [4]
-
-
//! [5]
socket->connectToHostEncrypted("imap", 993);
if (socket->waitForEncrypted(1000))
diff --git a/src/network/doc/snippets/network/CMakeLists.txt b/src/network/doc/snippets/network/CMakeLists.txt
index 4c9bbba823..b75aeafea0 100644
--- a/src/network/doc/snippets/network/CMakeLists.txt
+++ b/src/network/doc/snippets/network/CMakeLists.txt
@@ -1,5 +1,5 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
add_library(network_cppsnippets OBJECT tcpwait.cpp)
diff --git a/src/network/doc/src/examples.qdoc b/src/network/doc/src/examples.qdoc
index 1eb397d011..ee9084c74c 100644
--- a/src/network/doc/src/examples.qdoc
+++ b/src/network/doc/src/examples.qdoc
@@ -7,7 +7,7 @@
\title Network Examples
\brief How to do network programming in Qt.
- \image network-examples.png
+ \image network-examples.webp
Qt is provided with an extensive set of network classes to support both
client-based and server side network programming.
@@ -22,18 +22,12 @@
\li \l{network/blockingfortuneclient}{Blocking Fortune Client}\raisedaster
\li \l{network/broadcastreceiver}{Broadcast Receiver}
\li \l{network/broadcastsender}{Broadcast Sender}
- \li \l{network/download}{Download}
- \li \l{network/downloadmanager}{Download Manager}
\li \l{network/network-chat}{Network Chat}
\li \l{network/fortuneclient}{Fortune Client}\raisedaster
\li \l{network/fortuneserver}{Fortune Server}\raisedaster
\li \l{network/http}{HTTP}
- \li \l{network/loopback}{Loopback}
\li \l{network/threadedfortuneserver}{Threaded Fortune Server}\raisedaster
\li \l{network/torrent}{Torrent}
- \li \l{network/googlesuggest}{Google Suggest}
- \li \l{network/bearercloud}{Bearer Cloud}\raisedaster
- \li \l{network/bearermonitor}{Bearer Monitor}
\li \l{network/securesocketclient}{Secure Socket Client}
\li \l{network/multicastreceiver}{Multicast Receiver}
\li \l{network/multicastsender}{Multicast Sender}
diff --git a/src/network/doc/src/ssl.qdoc b/src/network/doc/src/ssl.qdoc
index 84247aee1b..83549f61e8 100644
--- a/src/network/doc/src/ssl.qdoc
+++ b/src/network/doc/src/ssl.qdoc
@@ -13,13 +13,16 @@
the \l{OpenSSL Toolkit}, or any appropriate TLS plugin to perform encryption
and protocol handling.
- From Qt version 5.15 onwards, the officially supported version for OpenSSL
+ From Qt version 5.15 onward, the officially supported version for OpenSSL
is 1.1.1 or later.
+ Qt version 5.15.1 onward is also compatible with OpenSSL 3.
+
\annotatedlist ssl
+ For Android applications see \l{Adding OpenSSL Support for Android}.
- \section1 Enabling and Disabling SSL Support
+ \section1 Enabling and Disabling SSL Support when Building Qt from Source
When building Qt from source, Qt builds plugins for native TLS libraries
that are supported for the operating system you are building for. For
diff --git a/src/network/kernel/PSL-LICENSE.txt b/src/network/kernel/PSL-LICENSE.txt
deleted file mode 100644
index d0a1fa1482..0000000000
--- a/src/network/kernel/PSL-LICENSE.txt
+++ /dev/null
@@ -1,373 +0,0 @@
-Mozilla Public License Version 2.0
-==================================
-
-1. Definitions
---------------
-
-1.1. "Contributor"
- means each individual or legal entity that creates, contributes to
- the creation of, or owns Covered Software.
-
-1.2. "Contributor Version"
- means the combination of the Contributions of others (if any) used
- by a Contributor and that particular Contributor's Contribution.
-
-1.3. "Contribution"
- means Covered Software of a particular Contributor.
-
-1.4. "Covered Software"
- means Source Code Form to which the initial Contributor has attached
- the notice in Exhibit A, the Executable Form of such Source Code
- Form, and Modifications of such Source Code Form, in each case
- including portions thereof.
-
-1.5. "Incompatible With Secondary Licenses"
- means
-
- (a) that the initial Contributor has attached the notice described
- in Exhibit B to the Covered Software; or
-
- (b) that the Covered Software was made available under the terms of
- version 1.1 or earlier of the License, but not also under the
- terms of a Secondary License.
-
-1.6. "Executable Form"
- means any form of the work other than Source Code Form.
-
-1.7. "Larger Work"
- means a work that combines Covered Software with other material, in
- a separate file or files, that is not Covered Software.
-
-1.8. "License"
- means this document.
-
-1.9. "Licensable"
- means having the right to grant, to the maximum extent possible,
- whether at the time of the initial grant or subsequently, any and
- all of the rights conveyed by this License.
-
-1.10. "Modifications"
- means any of the following:
-
- (a) any file in Source Code Form that results from an addition to,
- deletion from, or modification of the contents of Covered
- Software; or
-
- (b) any new file in Source Code Form that contains any Covered
- Software.
-
-1.11. "Patent Claims" of a Contributor
- means any patent claim(s), including without limitation, method,
- process, and apparatus claims, in any patent Licensable by such
- Contributor that would be infringed, but for the grant of the
- License, by the making, using, selling, offering for sale, having
- made, import, or transfer of either its Contributions or its
- Contributor Version.
-
-1.12. "Secondary License"
- means either the GNU General Public License, Version 2.0, the GNU
- Lesser General Public License, Version 2.1, the GNU Affero General
- Public License, Version 3.0, or any later versions of those
- licenses.
-
-1.13. "Source Code Form"
- means the form of the work preferred for making modifications.
-
-1.14. "You" (or "Your")
- means an individual or a legal entity exercising rights under this
- License. For legal entities, "You" includes any entity that
- controls, is controlled by, or is under common control with You. For
- purposes of this definition, "control" means (a) the power, direct
- or indirect, to cause the direction or management of such entity,
- whether by contract or otherwise, or (b) ownership of more than
- fifty percent (50%) of the outstanding shares or beneficial
- ownership of such entity.
-
-2. License Grants and Conditions
---------------------------------
-
-2.1. Grants
-
-Each Contributor hereby grants You a world-wide, royalty-free,
-non-exclusive license:
-
-(a) under intellectual property rights (other than patent or trademark)
- Licensable by such Contributor to use, reproduce, make available,
- modify, display, perform, distribute, and otherwise exploit its
- Contributions, either on an unmodified basis, with Modifications, or
- as part of a Larger Work; and
-
-(b) under Patent Claims of such Contributor to make, use, sell, offer
- for sale, have made, import, and otherwise transfer either its
- Contributions or its Contributor Version.
-
-2.2. Effective Date
-
-The licenses granted in Section 2.1 with respect to any Contribution
-become effective for each Contribution on the date the Contributor first
-distributes such Contribution.
-
-2.3. Limitations on Grant Scope
-
-The licenses granted in this Section 2 are the only rights granted under
-this License. No additional rights or licenses will be implied from the
-distribution or licensing of Covered Software under this License.
-Notwithstanding Section 2.1(b) above, no patent license is granted by a
-Contributor:
-
-(a) for any code that a Contributor has removed from Covered Software;
- or
-
-(b) for infringements caused by: (i) Your and any other third party's
- modifications of Covered Software, or (ii) the combination of its
- Contributions with other software (except as part of its Contributor
- Version); or
-
-(c) under Patent Claims infringed by Covered Software in the absence of
- its Contributions.
-
-This License does not grant any rights in the trademarks, service marks,
-or logos of any Contributor (except as may be necessary to comply with
-the notice requirements in Section 3.4).
-
-2.4. Subsequent Licenses
-
-No Contributor makes additional grants as a result of Your choice to
-distribute the Covered Software under a subsequent version of this
-License (see Section 10.2) or under the terms of a Secondary License (if
-permitted under the terms of Section 3.3).
-
-2.5. Representation
-
-Each Contributor represents that the Contributor believes its
-Contributions are its original creation(s) or it has sufficient rights
-to grant the rights to its Contributions conveyed by this License.
-
-2.6. Fair Use
-
-This License is not intended to limit any rights You have under
-applicable copyright doctrines of fair use, fair dealing, or other
-equivalents.
-
-2.7. Conditions
-
-Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
-in Section 2.1.
-
-3. Responsibilities
--------------------
-
-3.1. Distribution of Source Form
-
-All distribution of Covered Software in Source Code Form, including any
-Modifications that You create or to which You contribute, must be under
-the terms of this License. You must inform recipients that the Source
-Code Form of the Covered Software is governed by the terms of this
-License, and how they can obtain a copy of this License. You may not
-attempt to alter or restrict the recipients' rights in the Source Code
-Form.
-
-3.2. Distribution of Executable Form
-
-If You distribute Covered Software in Executable Form then:
-
-(a) such Covered Software must also be made available in Source Code
- Form, as described in Section 3.1, and You must inform recipients of
- the Executable Form how they can obtain a copy of such Source Code
- Form by reasonable means in a timely manner, at a charge no more
- than the cost of distribution to the recipient; and
-
-(b) You may distribute such Executable Form under the terms of this
- License, or sublicense it under different terms, provided that the
- license for the Executable Form does not attempt to limit or alter
- the recipients' rights in the Source Code Form under this License.
-
-3.3. Distribution of a Larger Work
-
-You may create and distribute a Larger Work under terms of Your choice,
-provided that You also comply with the requirements of this License for
-the Covered Software. If the Larger Work is a combination of Covered
-Software with a work governed by one or more Secondary Licenses, and the
-Covered Software is not Incompatible With Secondary Licenses, this
-License permits You to additionally distribute such Covered Software
-under the terms of such Secondary License(s), so that the recipient of
-the Larger Work may, at their option, further distribute the Covered
-Software under the terms of either this License or such Secondary
-License(s).
-
-3.4. Notices
-
-You may not remove or alter the substance of any license notices
-(including copyright notices, patent notices, disclaimers of warranty,
-or limitations of liability) contained within the Source Code Form of
-the Covered Software, except that You may alter any license notices to
-the extent required to remedy known factual inaccuracies.
-
-3.5. Application of Additional Terms
-
-You may choose to offer, and to charge a fee for, warranty, support,
-indemnity or liability obligations to one or more recipients of Covered
-Software. However, You may do so only on Your own behalf, and not on
-behalf of any Contributor. You must make it absolutely clear that any
-such warranty, support, indemnity, or liability obligation is offered by
-You alone, and You hereby agree to indemnify every Contributor for any
-liability incurred by such Contributor as a result of warranty, support,
-indemnity or liability terms You offer. You may include additional
-disclaimers of warranty and limitations of liability specific to any
-jurisdiction.
-
-4. Inability to Comply Due to Statute or Regulation
----------------------------------------------------
-
-If it is impossible for You to comply with any of the terms of this
-License with respect to some or all of the Covered Software due to
-statute, judicial order, or regulation then You must: (a) comply with
-the terms of this License to the maximum extent possible; and (b)
-describe the limitations and the code they affect. Such description must
-be placed in a text file included with all distributions of the Covered
-Software under this License. Except to the extent prohibited by statute
-or regulation, such description must be sufficiently detailed for a
-recipient of ordinary skill to be able to understand it.
-
-5. Termination
---------------
-
-5.1. The rights granted under this License will terminate automatically
-if You fail to comply with any of its terms. However, if You become
-compliant, then the rights granted under this License from a particular
-Contributor are reinstated (a) provisionally, unless and until such
-Contributor explicitly and finally terminates Your grants, and (b) on an
-ongoing basis, if such Contributor fails to notify You of the
-non-compliance by some reasonable means prior to 60 days after You have
-come back into compliance. Moreover, Your grants from a particular
-Contributor are reinstated on an ongoing basis if such Contributor
-notifies You of the non-compliance by some reasonable means, this is the
-first time You have received notice of non-compliance with this License
-from such Contributor, and You become compliant prior to 30 days after
-Your receipt of the notice.
-
-5.2. If You initiate litigation against any entity by asserting a patent
-infringement claim (excluding declaratory judgment actions,
-counter-claims, and cross-claims) alleging that a Contributor Version
-directly or indirectly infringes any patent, then the rights granted to
-You by any and all Contributors for the Covered Software under Section
-2.1 of this License shall terminate.
-
-5.3. In the event of termination under Sections 5.1 or 5.2 above, all
-end user license agreements (excluding distributors and resellers) which
-have been validly granted by You or Your distributors under this License
-prior to termination shall survive termination.
-
-************************************************************************
-* *
-* 6. Disclaimer of Warranty *
-* ------------------------- *
-* *
-* Covered Software is provided under this License on an "as is" *
-* basis, without warranty of any kind, either expressed, implied, or *
-* statutory, including, without limitation, warranties that the *
-* Covered Software is free of defects, merchantable, fit for a *
-* particular purpose or non-infringing. The entire risk as to the *
-* quality and performance of the Covered Software is with You. *
-* Should any Covered Software prove defective in any respect, You *
-* (not any Contributor) assume the cost of any necessary servicing, *
-* repair, or correction. This disclaimer of warranty constitutes an *
-* essential part of this License. No use of any Covered Software is *
-* authorized under this License except under this disclaimer. *
-* *
-************************************************************************
-
-************************************************************************
-* *
-* 7. Limitation of Liability *
-* -------------------------- *
-* *
-* Under no circumstances and under no legal theory, whether tort *
-* (including negligence), contract, or otherwise, shall any *
-* Contributor, or anyone who distributes Covered Software as *
-* permitted above, be liable to You for any direct, indirect, *
-* special, incidental, or consequential damages of any character *
-* including, without limitation, damages for lost profits, loss of *
-* goodwill, work stoppage, computer failure or malfunction, or any *
-* and all other commercial damages or losses, even if such party *
-* shall have been informed of the possibility of such damages. This *
-* limitation of liability shall not apply to liability for death or *
-* personal injury resulting from such party's negligence to the *
-* extent applicable law prohibits such limitation. Some *
-* jurisdictions do not allow the exclusion or limitation of *
-* incidental or consequential damages, so this exclusion and *
-* limitation may not apply to You. *
-* *
-************************************************************************
-
-8. Litigation
--------------
-
-Any litigation relating to this License may be brought only in the
-courts of a jurisdiction where the defendant maintains its principal
-place of business and such litigation shall be governed by laws of that
-jurisdiction, without reference to its conflict-of-law provisions.
-Nothing in this Section shall prevent a party's ability to bring
-cross-claims or counter-claims.
-
-9. Miscellaneous
-----------------
-
-This License represents the complete agreement concerning the subject
-matter hereof. If any provision of this License is held to be
-unenforceable, such provision shall be reformed only to the extent
-necessary to make it enforceable. Any law or regulation which provides
-that the language of a contract shall be construed against the drafter
-shall not be used to construe this License against a Contributor.
-
-10. Versions of the License
----------------------------
-
-10.1. New Versions
-
-Mozilla Foundation is the license steward. Except as provided in Section
-10.3, no one other than the license steward has the right to modify or
-publish new versions of this License. Each version will be given a
-distinguishing version number.
-
-10.2. Effect of New Versions
-
-You may distribute the Covered Software under the terms of the version
-of the License under which You originally received the Covered Software,
-or under the terms of any subsequent version published by the license
-steward.
-
-10.3. Modified Versions
-
-If you create software not governed by this License, and you want to
-create a new license for such software, you may create and use a
-modified version of this License if you rename the license and remove
-any references to the name of the license steward (except to note that
-such modified license differs from this License).
-
-10.4. Distributing Source Code Form that is Incompatible With Secondary
-Licenses
-
-If You choose to distribute Source Code Form that is Incompatible With
-Secondary Licenses under the terms of this version of the License, the
-notice described in Exhibit B of this License must be attached.
-
-Exhibit A - Source Code Form License Notice
--------------------------------------------
-
- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at https://mozilla.org/MPL/2.0/.
-
-If it is not possible or desirable to put the notice in a particular
-file, then You may include the notice in a location (such as a LICENSE
-file in a relevant directory) where a recipient would be likely to look
-for such a notice.
-
-You may add additional accurate notices of copyright ownership.
-
-Exhibit B - "Incompatible With Secondary Licenses" Notice
----------------------------------------------------------
-
- This Source Code Form is "Incompatible With Secondary Licenses", as
- defined by the Mozilla Public License, v. 2.0.
diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp
index 4b9ed021be..e42450d7e5 100644
--- a/src/network/kernel/qauthenticator.cpp
+++ b/src/network/kernel/qauthenticator.cpp
@@ -14,6 +14,7 @@
#include <qstring.h>
#include <qdatetime.h>
#include <qrandom.h>
+#include <QtNetwork/qhttpheaders.h>
#ifdef Q_OS_WIN
#include <qmutex.h>
@@ -444,13 +445,14 @@ static bool verifyDigestMD5(QByteArrayView value)
return true; // assume it's ok if algorithm is not specified
}
-void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByteArray>> &values,
+void QAuthenticatorPrivate::parseHttpResponse(const QHttpHeaders &headers,
bool isProxy, QStringView host)
{
#if !QT_CONFIG(gssapi)
Q_UNUSED(host);
#endif
- const char *search = isProxy ? "proxy-authenticate" : "www-authenticate";
+ const auto search = isProxy ? QHttpHeaders::WellKnownHeader::ProxyAuthenticate
+ : QHttpHeaders::WellKnownHeader::WWWAuthenticate;
method = None;
/*
@@ -463,26 +465,23 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt
authentication parameters.
*/
- QByteArray headerVal;
- for (int i = 0; i < values.size(); ++i) {
- const QPair<QByteArray, QByteArray> &current = values.at(i);
- if (current.first.compare(search, Qt::CaseInsensitive) != 0)
- continue;
- QByteArray str = current.second.toLower();
- if (method < Basic && str.startsWith("basic")) {
+ QByteArrayView headerVal;
+ for (const auto &current : headers.values(search)) {
+ const QLatin1StringView str(current);
+ if (method < Basic && str.startsWith("basic"_L1, Qt::CaseInsensitive)) {
method = Basic;
- headerVal = current.second.mid(6);
- } else if (method < Ntlm && str.startsWith("ntlm")) {
+ headerVal = QByteArrayView(current).mid(6);
+ } else if (method < Ntlm && str.startsWith("ntlm"_L1, Qt::CaseInsensitive)) {
method = Ntlm;
- headerVal = current.second.mid(5);
- } else if (method < DigestMd5 && str.startsWith("digest")) {
+ headerVal = QByteArrayView(current).mid(5);
+ } else if (method < DigestMd5 && str.startsWith("digest"_L1, Qt::CaseInsensitive)) {
// Make sure the algorithm is actually MD5 before committing to it:
- if (!verifyDigestMD5(QByteArrayView(current.second).sliced(7)))
+ if (!verifyDigestMD5(QByteArrayView(current).sliced(7)))
continue;
method = DigestMd5;
- headerVal = current.second.mid(7);
- } else if (method < Negotiate && str.startsWith("negotiate")) {
+ headerVal = QByteArrayView(current).mid(7);
+ } else if (method < Negotiate && str.startsWith("negotiate"_L1, Qt::CaseInsensitive)) {
#if QT_CONFIG(sspi) || QT_CONFIG(gssapi) // if it's not supported then we shouldn't try to use it
#if QT_CONFIG(gssapi)
// For GSSAPI there needs to be a KDC set up for the host (afaict).
@@ -492,14 +491,14 @@ void QAuthenticatorPrivate::parseHttpResponse(const QList<QPair<QByteArray, QByt
continue;
#endif
method = Negotiate;
- headerVal = current.second.mid(10);
+ headerVal = QByteArrayView(current).mid(10);
#endif
}
}
// Reparse credentials since we know the method now
updateCredentials();
- challenge = headerVal.trimmed();
+ challenge = headerVal.trimmed().toByteArray();
QHash<QByteArray, QByteArray> options = parseDigestAuthenticationChallenge(challenge);
// Sets phase to Start if this updates our realm and sets the two locations where we store
@@ -547,16 +546,14 @@ QByteArray QAuthenticatorPrivate::calculateResponse(QByteArrayView requestMethod
Q_UNUSED(host);
#endif
QByteArray response;
- const char* methodString = nullptr;
+ QByteArrayView methodString;
switch(method) {
case QAuthenticatorPrivate::None:
- methodString = "";
phase = Done;
break;
case QAuthenticatorPrivate::Basic:
methodString = "Basic";
- response = user.toLatin1() + ':' + password.toLatin1();
- response = response.toBase64();
+ response = (user + ':'_L1 + password).toLatin1().toBase64();
phase = Done;
break;
case QAuthenticatorPrivate::DigestMd5:
@@ -645,26 +642,35 @@ QByteArray QAuthenticatorPrivate::calculateResponse(QByteArrayView requestMethod
break;
}
- return QByteArray::fromRawData(methodString, qstrlen(methodString)) + ' ' + response;
+ return methodString + ' ' + response;
}
// ---------------------------- Digest Md5 code ----------------------------------------
+static bool containsAuth(QByteArrayView data)
+{
+ for (auto element : QLatin1StringView(data).tokenize(','_L1)) {
+ if (element == "auth"_L1)
+ return true;
+ }
+ return false;
+}
+
QHash<QByteArray, QByteArray>
QAuthenticatorPrivate::parseDigestAuthenticationChallenge(QByteArrayView challenge)
{
QHash<QByteArray, QByteArray> options;
// parse the challenge
const char *d = challenge.data();
- const char *end = d + challenge.length();
+ const char *end = d + challenge.size();
while (d < end) {
while (d < end && (*d == ' ' || *d == '\n' || *d == '\r'))
++d;
const char *start = d;
while (d < end && *d != '=')
++d;
- QByteArray key = QByteArray(start, d - start);
+ QByteArrayView key = QByteArrayView(start, d - start);
++d;
if (d >= end)
break;
@@ -695,13 +701,12 @@ QAuthenticatorPrivate::parseDigestAuthenticationChallenge(QByteArrayView challen
while (d < end && *d != ',')
++d;
++d;
- options[key] = value;
+ options[key.toByteArray()] = std::move(value);
}
QByteArray qop = options.value("qop");
if (!qop.isEmpty()) {
- QList<QByteArray> qopoptions = qop.split(',');
- if (!qopoptions.contains("auth"))
+ if (!containsAuth(qop))
return QHash<QByteArray, QByteArray>();
// #### can't do auth-int currently
// if (qop.contains("auth-int"))
@@ -798,7 +803,7 @@ QByteArray QAuthenticatorPrivate::digestMd5Response(QByteArrayView challenge, QB
++nonceCount;
QByteArray nonceCountString = QByteArray::number(nonceCount, 16);
- while (nonceCountString.length() < 8)
+ while (nonceCountString.size() < 8)
nonceCountString.prepend('0');
QByteArray nonce = options.value("nonce");
@@ -1079,7 +1084,7 @@ static int qEncodeNtlmString(QNtlmBuffer& buf, int offset, const QString& s, boo
{
if (!unicode)
return qEncodeNtlmBuffer(buf, offset, s.toLatin1());
- buf.len = 2 * s.length();
+ buf.len = 2 * s.size();
buf.maxLen = buf.len;
buf.offset = (offset + 1) & ~1;
return buf.offset + buf.len;
@@ -1201,7 +1206,7 @@ static QByteArray qNtlmPhase1()
static QByteArray qStringAsUcs2Le(const QString& src)
{
- QByteArray rc(2*src.length(), 0);
+ QByteArray rc(2*src.size(), 0);
unsigned short *d = (unsigned short*)rc.data();
for (QChar ch : src)
*d++ = qToLittleEndian(quint16(ch.unicode()));
@@ -1214,7 +1219,7 @@ static QString qStringFromUcs2Le(QByteArray src)
{
Q_ASSERT(src.size() % 2 == 0);
unsigned short *d = (unsigned short*)src.data();
- for (int i = 0; i < src.length() / 2; ++i) {
+ for (int i = 0; i < src.size() / 2; ++i) {
d[i] = qFromLittleEndian(d[i]);
}
return QString((const QChar *)src.data(), src.size()/2);
@@ -1256,7 +1261,7 @@ QByteArray qEncodeHmacMd5(QByteArray &key, QByteArrayView message)
hash.reset();
// Adjust the key length to blockSize
- if (blockSize < key.length()) {
+ if (blockSize < key.size()) {
hash.addData(key);
key = hash.result(); //MD5 will always return 16 bytes length output
}
@@ -1759,7 +1764,7 @@ static QByteArray qGssapiContinue(QAuthenticatorPrivate *ctx, QByteArrayView cha
if (!challenge.isEmpty()) {
inBuf.value = const_cast<char*>(challenge.data());
- inBuf.length = challenge.length();
+ inBuf.length = challenge.size();
}
majStat = gss_init_sec_context(&minStat,
diff --git a/src/network/kernel/qauthenticator.h b/src/network/kernel/qauthenticator.h
index 4d8e85c000..a05d359e93 100644
--- a/src/network/kernel/qauthenticator.h
+++ b/src/network/kernel/qauthenticator.h
@@ -16,6 +16,7 @@ class QUrl;
class Q_NETWORK_EXPORT QAuthenticator
{
+ Q_GADGET
public:
QAuthenticator();
~QAuthenticator();
diff --git a/src/network/kernel/qauthenticator_p.h b/src/network/kernel/qauthenticator_p.h
index f8579a46c0..bc16139941 100644
--- a/src/network/kernel/qauthenticator_p.h
+++ b/src/network/kernel/qauthenticator_p.h
@@ -26,13 +26,14 @@
QT_BEGIN_NAMESPACE
class QHttpResponseHeader;
+class QHttpHeaders;
#if QT_CONFIG(sspi) // SSPI
class QSSPIWindowsHandles;
#elif QT_CONFIG(gssapi) // GSSAPI
class QGssApiHandles;
#endif
-class Q_AUTOTEST_EXPORT QAuthenticatorPrivate
+class Q_NETWORK_EXPORT QAuthenticatorPrivate
{
public:
enum Method { None, Basic, Negotiate, Ntlm, DigestMd5, };
@@ -80,8 +81,7 @@ public:
static QHash<QByteArray, QByteArray>
parseDigestAuthenticationChallenge(QByteArrayView challenge);
- void parseHttpResponse(const QList<QPair<QByteArray, QByteArray>> &, bool isProxy,
- QStringView host);
+ void parseHttpResponse(const QHttpHeaders &headers, bool isProxy, QStringView host);
void updateCredentials();
static bool isMethodSupported(QByteArrayView method);
diff --git a/src/network/kernel/qdnslookup.cpp b/src/network/kernel/qdnslookup.cpp
index fb8a96a049..c310c7e28e 100644
--- a/src/network/kernel/qdnslookup.cpp
+++ b/src/network/kernel/qdnslookup.cpp
@@ -1,11 +1,14 @@
// Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+// Copyright (C) 2023 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qdnslookup.h"
#include "qdnslookup_p.h"
+#include <qapplicationstatic.h>
#include <qcoreapplication.h>
#include <qdatetime.h>
+#include <qloggingcategory.h>
#include <qrandom.h>
#include <qurl.h>
@@ -13,11 +16,20 @@
QT_BEGIN_NAMESPACE
-QT_IMPL_METATYPE_EXTERN(QDnsLookupReply)
+static Q_LOGGING_CATEGORY(lcDnsLookup, "qt.network.dnslookup", QtCriticalMsg)
-#if QT_CONFIG(thread)
-Q_GLOBAL_STATIC(QDnsLookupThreadPool, theDnsLookupThreadPool);
-#endif
+namespace {
+struct QDnsLookupThreadPool : QThreadPool
+{
+ QDnsLookupThreadPool()
+ {
+ // Run up to 5 lookups in parallel.
+ setMaxThreadCount(5);
+ }
+};
+}
+
+Q_APPLICATION_STATIC(QDnsLookupThreadPool, theDnsLookupThreadPool);
static bool qt_qdnsmailexchangerecord_less_than(const QDnsMailExchangeRecord &r1, const QDnsMailExchangeRecord &r2)
{
@@ -121,9 +133,6 @@ static void qt_qdnsservicerecord_sort(QList<QDnsServiceRecord> &records)
}
}
-const char *QDnsLookupPrivate::msgNoIpV6NameServerAdresses =
- QT_TRANSLATE_NOOP("QDnsLookupRunnable", "IPv6 addresses for nameservers are currently not supported");
-
/*!
\class QDnsLookup
\brief The QDnsLookup class represents a DNS lookup.
@@ -178,6 +187,9 @@ const char *QDnsLookupPrivate::msgNoIpV6NameServerAdresses =
\value NotFoundError the requested domain name does not exist
(NXDOMAIN).
+
+ \value TimeoutError the server was not reached or did not reply
+ in time (since 6.6).
*/
/*!
@@ -233,8 +245,8 @@ const char *QDnsLookupPrivate::msgNoIpV6NameServerAdresses =
QDnsLookup::QDnsLookup(QObject *parent)
: QObject(*new QDnsLookupPrivate, parent)
{
- qRegisterMetaType<QDnsLookupReply>();
}
+
/*!
Constructs a QDnsLookup object for the given \a type and \a name and sets
\a parent as the parent object.
@@ -244,7 +256,6 @@ QDnsLookup::QDnsLookup(Type type, const QString &name, QObject *parent)
: QObject(*new QDnsLookupPrivate, parent)
{
Q_D(QDnsLookup);
- qRegisterMetaType<QDnsLookupReply>();
d->name = name;
d->type = type;
}
@@ -252,17 +263,39 @@ QDnsLookup::QDnsLookup(Type type, const QString &name, QObject *parent)
/*!
\fn QDnsLookup::QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, QObject *parent)
\since 5.4
- Constructs a QDnsLookup object for the given \a type, \a name and
- \a nameserver and sets \a parent as the parent object.
+
+ Constructs a QDnsLookup object to issue a query for \a name of record type
+ \a type, using the DNS server \a nameserver running on the default DNS port,
+ and sets \a parent as the parent object.
*/
QDnsLookup::QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, QObject *parent)
+ : QDnsLookup(type, name, nameserver, DnsPort, parent)
+{
+}
+
+/*!
+ \fn QDnsLookup::QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, quint16 port, QObject *parent)
+ \since 6.6
+
+ Constructs a QDnsLookup object to issue a query for \a name of record type
+ \a type, using the DNS server \a nameserver running on port \a port, and
+ sets \a parent as the parent object.
+
+//! [nameserver-port]
+ \note Setting the port number to any value other than the default (53) can
+ cause the name resolution to fail, depending on the operating system
+ limitations and firewalls. Notably, the Windows API used by QDnsLookup is
+ unable to handle alternate port numbers.
+//! [nameserver-port]
+*/
+QDnsLookup::QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, quint16 port, QObject *parent)
: QObject(*new QDnsLookupPrivate, parent)
{
Q_D(QDnsLookup);
- qRegisterMetaType<QDnsLookupReply>();
d->name = name;
d->type = type;
+ d->port = port;
d->nameserver = nameserver;
}
@@ -310,6 +343,10 @@ bool QDnsLookup::isFinished() const
\property QDnsLookup::name
\brief the name to lookup.
+ If the name to look up is empty, QDnsLookup will attempt to resolve the
+ root domain of DNS. That query is usually performed with QDnsLookup::type
+ set to \l{QDnsLookup::Type}{NS}.
+
\note The name will be encoded using IDNA, which means it's unsuitable for
querying SRV records compatible with the DNS-SD specification.
*/
@@ -376,6 +413,46 @@ QBindable<QHostAddress> QDnsLookup::bindableNameserver()
}
/*!
+ \property QDnsLookup::nameserverPort
+ \since 6.6
+ \brief the port number of nameserver to use for DNS lookup.
+ \include qdnslookup.cpp nameserver-port
+*/
+
+quint16 QDnsLookup::nameserverPort() const
+{
+ return d_func()->port;
+}
+
+void QDnsLookup::setNameserverPort(quint16 nameserverPort)
+{
+ Q_D(QDnsLookup);
+ d->port = nameserverPort;
+}
+
+QBindable<quint16> QDnsLookup::bindableNameserverPort()
+{
+ Q_D(QDnsLookup);
+ return &d->port;
+}
+
+/*!
+ \since 6.6
+ Sets the nameserver to \a nameserver and the port to \a port.
+
+ \include qdnslookup.cpp nameserver-port
+
+ \sa QDnsLookup::nameserver, QDnsLookup::nameserverPort
+*/
+void QDnsLookup::setNameserver(const QHostAddress &nameserver, quint16 port)
+{
+ Qt::beginPropertyUpdateGroup();
+ setNameserver(nameserver);
+ setNameserverPort(port);
+ Qt::endPropertyUpdateGroup();
+}
+
+/*!
Returns the list of canonical name records associated with this lookup.
*/
@@ -476,13 +553,29 @@ void QDnsLookup::lookup()
Q_D(QDnsLookup);
d->isFinished = false;
d->reply = QDnsLookupReply();
- d->runnable = new QDnsLookupRunnable(d->type, QUrl::toAce(d->name), d->nameserver);
- connect(d->runnable, SIGNAL(finished(QDnsLookupReply)),
- this, SLOT(_q_lookupFinished(QDnsLookupReply)),
- Qt::BlockingQueuedConnection);
-#if QT_CONFIG(thread)
- theDnsLookupThreadPool()->start(d->runnable);
+ if (!QCoreApplication::instance()) {
+ // NOT qCWarning because this isn't a result of the lookup
+ qWarning("QDnsLookup requires a QCoreApplication");
+ return;
+ }
+
+ auto l = [this](const QDnsLookupReply &reply) {
+ Q_D(QDnsLookup);
+ if (d->runnable == sender()) {
+#ifdef QDNSLOOKUP_DEBUG
+ qDebug("DNS reply for %s: %i (%s)", qPrintable(d->name), reply.error, qPrintable(reply.errorString));
#endif
+ d->reply = reply;
+ d->runnable = nullptr;
+ d->isFinished = true;
+ emit finished();
+ }
+ };
+
+ d->runnable = new QDnsLookupRunnable(d);
+ connect(d->runnable, &QDnsLookupRunnable::finished, this, l,
+ Qt::BlockingQueuedConnection);
+ theDnsLookupThreadPool->start(d->runnable);
}
/*!
@@ -959,18 +1052,26 @@ QDnsTextRecord &QDnsTextRecord::operator=(const QDnsTextRecord &other)
very fast and never fails.
*/
-void QDnsLookupPrivate::_q_lookupFinished(const QDnsLookupReply &_reply)
+static QDnsLookupRunnable::EncodedLabel encodeLabel(const QString &label)
{
- Q_Q(QDnsLookup);
- if (runnable == q->sender()) {
-#ifdef QDNSLOOKUP_DEBUG
- qDebug("DNS reply for %s: %i (%s)", qPrintable(name), _reply.error, qPrintable(_reply.errorString));
+ QDnsLookupRunnable::EncodedLabel::value_type rootDomain = u'.';
+ if (label.isEmpty())
+ return QDnsLookupRunnable::EncodedLabel(1, rootDomain);
+
+ QString encodedLabel = qt_ACE_do(label, ToAceOnly, ForbidLeadingDot);
+#ifdef Q_OS_WIN
+ return encodedLabel;
+#else
+ return std::move(encodedLabel).toLatin1();
#endif
- reply = _reply;
- runnable = nullptr;
- isFinished = true;
- emit q->finished();
- }
+}
+
+inline QDnsLookupRunnable::QDnsLookupRunnable(const QDnsLookupPrivate *d)
+ : requestName(encodeLabel(d->name)),
+ nameserver(d->nameserver),
+ requestType(d->type),
+ port(d->port)
+{
}
void QDnsLookupRunnable::run()
@@ -978,60 +1079,53 @@ void QDnsLookupRunnable::run()
QDnsLookupReply reply;
// Validate input.
- if (requestName.isEmpty()) {
+ if (qsizetype n = requestName.size(); n > MaxDomainNameLength || n == 0) {
reply.error = QDnsLookup::InvalidRequestError;
- reply.errorString = tr("Invalid domain name");
- emit finished(reply);
- return;
+ reply.errorString = QDnsLookup::tr("Invalid domain name");
+ } else {
+ // Perform request.
+ query(&reply);
+
+ // Sort results.
+ qt_qdnsmailexchangerecord_sort(reply.mailExchangeRecords);
+ qt_qdnsservicerecord_sort(reply.serviceRecords);
}
- // Perform request.
- query(requestType, requestName, nameserver, &reply);
-
- // Sort results.
- qt_qdnsmailexchangerecord_sort(reply.mailExchangeRecords);
- qt_qdnsservicerecord_sort(reply.serviceRecords);
-
emit finished(reply);
-}
-
-#if QT_CONFIG(thread)
-QDnsLookupThreadPool::QDnsLookupThreadPool()
- : signalsConnected(false)
-{
- // Run up to 5 lookups in parallel.
- setMaxThreadCount(5);
-}
-
-void QDnsLookupThreadPool::start(QRunnable *runnable)
-{
- // Ensure threads complete at application destruction.
- if (!signalsConnected) {
- QMutexLocker signalsLocker(&signalsMutex);
- if (!signalsConnected) {
- QCoreApplication *app = QCoreApplication::instance();
- if (!app) {
- qWarning("QDnsLookup requires a QCoreApplication");
- delete runnable;
- return;
- }
- moveToThread(app->thread());
- connect(app, SIGNAL(destroyed()),
- SLOT(_q_applicationDestroyed()), Qt::DirectConnection);
- signalsConnected = true;
- }
+ // maybe print the lookup error as warning
+ switch (reply.error) {
+ case QDnsLookup::NoError:
+ case QDnsLookup::OperationCancelledError:
+ case QDnsLookup::NotFoundError:
+ case QDnsLookup::ServerFailureError:
+ case QDnsLookup::ServerRefusedError:
+ case QDnsLookup::TimeoutError:
+ break; // no warning for these
+
+ case QDnsLookup::ResolverError:
+ case QDnsLookup::InvalidRequestError:
+ case QDnsLookup::InvalidReplyError:
+ qCWarning(lcDnsLookup()).nospace()
+ << "DNS lookup failed (" << reply.error << "): "
+ << qUtf16Printable(reply.errorString)
+ << "; request was " << this; // continues below
}
-
- QThreadPool::start(runnable);
}
-void QDnsLookupThreadPool::_q_applicationDestroyed()
+inline QDebug operator<<(QDebug &d, QDnsLookupRunnable *r)
{
- waitForDone();
- signalsConnected = false;
+ // continued: print the information about the request
+ d << r->requestName.left(MaxDomainNameLength);
+ if (r->requestName.size() > MaxDomainNameLength)
+ d << "... (truncated)";
+ d << " type " << r->requestType;
+ if (!r->nameserver.isNull())
+ d << " to nameserver " << qUtf16Printable(r->nameserver.toString())
+ << " port " << (r->port ? r->port : DnsPort);
+ return d;
}
-#endif // QT_CONFIG(thread)
+
QT_END_NAMESPACE
#include "moc_qdnslookup.cpp"
diff --git a/src/network/kernel/qdnslookup.h b/src/network/kernel/qdnslookup.h
index b0bb34ee75..ae89a0a11f 100644
--- a/src/network/kernel/qdnslookup.h
+++ b/src/network/kernel/qdnslookup.h
@@ -8,7 +8,6 @@
#include <QtCore/qlist.h>
#include <QtCore/qobject.h>
#include <QtCore/qshareddata.h>
-#include <QtCore/qsharedpointer.h>
#include <QtCore/qstring.h>
#include <QtCore/qproperty.h>
@@ -147,6 +146,8 @@ class Q_NETWORK_EXPORT QDnsLookup : public QObject
Q_PROPERTY(Type type READ type WRITE setType NOTIFY typeChanged BINDABLE bindableType)
Q_PROPERTY(QHostAddress nameserver READ nameserver WRITE setNameserver NOTIFY nameserverChanged
BINDABLE bindableNameserver)
+ Q_PROPERTY(quint16 nameserverPort READ nameserverPort WRITE setNameserverPort
+ NOTIFY nameserverPortChanged BINDABLE bindableNameserverPort)
public:
enum Error
@@ -158,7 +159,8 @@ public:
InvalidReplyError,
ServerFailureError,
ServerRefusedError,
- NotFoundError
+ NotFoundError,
+ TimeoutError,
};
Q_ENUM(Error)
@@ -179,6 +181,8 @@ public:
explicit QDnsLookup(QObject *parent = nullptr);
QDnsLookup(Type type, const QString &name, QObject *parent = nullptr);
QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, QObject *parent = nullptr);
+ QDnsLookup(Type type, const QString &name, const QHostAddress &nameserver, quint16 port,
+ QObject *parent = nullptr);
~QDnsLookup();
Error error() const;
@@ -196,6 +200,10 @@ public:
QHostAddress nameserver() const;
void setNameserver(const QHostAddress &nameserver);
QBindable<QHostAddress> bindableNameserver();
+ quint16 nameserverPort() const;
+ void setNameserverPort(quint16 port);
+ QBindable<quint16> bindableNameserverPort();
+ void setNameserver(const QHostAddress &nameserver, quint16 port);
QList<QDnsDomainNameRecord> canonicalNameRecords() const;
QList<QDnsHostAddressRecord> hostAddressRecords() const;
@@ -215,10 +223,10 @@ Q_SIGNALS:
void nameChanged(const QString &name);
void typeChanged(Type type);
void nameserverChanged(const QHostAddress &nameserver);
+ void nameserverPortChanged(quint16 port);
private:
Q_DECLARE_PRIVATE(QDnsLookup)
- Q_PRIVATE_SLOT(d_func(), void _q_lookupFinished(const QDnsLookupReply &reply))
};
QT_END_NAMESPACE
diff --git a/src/network/kernel/qdnslookup_android.cpp b/src/network/kernel/qdnslookup_android.cpp
deleted file mode 100644
index 8fc1265e80..0000000000
--- a/src/network/kernel/qdnslookup_android.cpp
+++ /dev/null
@@ -1,20 +0,0 @@
-// Copyright (C) 2012 Collabora Ltd, author <robin.burchell@collabora.co.uk>
-// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-
-#include "qdnslookup_p.h"
-
-QT_BEGIN_NAMESPACE
-
-void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply)
-{
- Q_UNUSED(requestType);
- Q_UNUSED(requestName);
- Q_UNUSED(nameserver);
- Q_UNUSED(reply);
- qWarning("Not yet supported on Android");
- reply->error = QDnsLookup::ResolverError;
- reply->errorString = tr("Not yet supported on Android");
- return;
-}
-
-QT_END_NAMESPACE
diff --git a/src/network/kernel/qdnslookup_dummy.cpp b/src/network/kernel/qdnslookup_dummy.cpp
new file mode 100644
index 0000000000..6cc6ed92c5
--- /dev/null
+++ b/src/network/kernel/qdnslookup_dummy.cpp
@@ -0,0 +1,15 @@
+// Copyright (C) 2012 Collabora Ltd, author <robin.burchell@collabora.co.uk>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qdnslookup_p.h"
+
+QT_BEGIN_NAMESPACE
+
+void QDnsLookupRunnable::query(QDnsLookupReply *reply)
+{
+ reply->error = QDnsLookup::ResolverError;
+ reply->errorString = tr("Not yet supported on this OS");
+ return;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/kernel/qdnslookup_p.h b/src/network/kernel/qdnslookup_p.h
index 21262346b5..da4721411b 100644
--- a/src/network/kernel/qdnslookup_p.h
+++ b/src/network/kernel/qdnslookup_p.h
@@ -1,4 +1,5 @@
// Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+// Copyright (C) 2023 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QDNSLOOKUP_P_H
@@ -18,13 +19,13 @@
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "QtCore/qmutex.h"
#include "QtCore/qrunnable.h"
-#include "QtCore/qsharedpointer.h"
#if QT_CONFIG(thread)
#include "QtCore/qthreadpool.h"
#endif
#include "QtNetwork/qdnslookup.h"
#include "QtNetwork/qhostaddress.h"
#include "private/qobject_p.h"
+#include "private/qurl_p.h"
QT_REQUIRE_CONFIG(dnslookup);
@@ -32,16 +33,16 @@ QT_BEGIN_NAMESPACE
//#define QDNSLOOKUP_DEBUG
+constexpr qsizetype MaxDomainNameLength = 255;
+constexpr quint16 DnsPort = 53;
+
class QDnsLookupRunnable;
+QDebug operator<<(QDebug &, QDnsLookupRunnable *);
class QDnsLookupReply
{
public:
- QDnsLookupReply()
- : error(QDnsLookup::NoError)
- { }
-
- QDnsLookup::Error error;
+ QDnsLookup::Error error = QDnsLookup::NoError;
QString errorString;
QList<QDnsDomainNameRecord> canonicalNameRecords;
@@ -51,23 +52,86 @@ public:
QList<QDnsDomainNameRecord> pointerRecords;
QList<QDnsServiceRecord> serviceRecords;
QList<QDnsTextRecord> textRecords;
+
+ // helper methods
+ void setError(QDnsLookup::Error err, QString &&msg)
+ {
+ error = err;
+ errorString = std::move(msg);
+ }
+
+ void makeResolverSystemError(int code = -1)
+ {
+ Q_ASSERT(allAreEmpty());
+ setError(QDnsLookup::ResolverError, qt_error_string(code));
+ }
+
+ void makeTimeoutError()
+ {
+ Q_ASSERT(allAreEmpty());
+ setError(QDnsLookup::TimeoutError, QDnsLookup::tr("Request timed out"));
+ }
+
+ void makeDnsRcodeError(quint8 rcode)
+ {
+ Q_ASSERT(allAreEmpty());
+ switch (rcode) {
+ case 1: // FORMERR
+ error = QDnsLookup::InvalidRequestError;
+ errorString = QDnsLookup::tr("Server could not process query");
+ return;
+ case 2: // SERVFAIL
+ case 4: // NOTIMP
+ error = QDnsLookup::ServerFailureError;
+ errorString = QDnsLookup::tr("Server failure");
+ return;
+ case 3: // NXDOMAIN
+ error = QDnsLookup::NotFoundError;
+ errorString = QDnsLookup::tr("Non existent domain");
+ return;
+ case 5: // REFUSED
+ error = QDnsLookup::ServerRefusedError;
+ errorString = QDnsLookup::tr("Server refused to answer");
+ return;
+ default:
+ error = QDnsLookup::InvalidReplyError;
+ errorString = QDnsLookup::tr("Invalid reply received (rcode %1)")
+ .arg(rcode);
+ return;
+ }
+ }
+
+ void makeInvalidReplyError(QString &&msg = QString())
+ {
+ if (msg.isEmpty())
+ msg = QDnsLookup::tr("Invalid reply received");
+ else
+ msg = QDnsLookup::tr("Invalid reply received (%1)").arg(std::move(msg));
+ *this = QDnsLookupReply(); // empty our lists
+ setError(QDnsLookup::InvalidReplyError, std::move(msg));
+ }
+
+private:
+ bool allAreEmpty() const
+ {
+ return canonicalNameRecords.isEmpty()
+ && hostAddressRecords.isEmpty()
+ && mailExchangeRecords.isEmpty()
+ && nameServerRecords.isEmpty()
+ && pointerRecords.isEmpty()
+ && serviceRecords.isEmpty()
+ && textRecords.isEmpty();
+ }
};
class QDnsLookupPrivate : public QObjectPrivate
{
public:
QDnsLookupPrivate()
- : isFinished(false)
- , type(QDnsLookup::A)
- , runnable(nullptr)
+ : type(QDnsLookup::A)
+ , port(DnsPort)
{ }
- void _q_lookupFinished(const QDnsLookupReply &reply);
-
- static const char *msgNoIpV6NameServerAdresses;
-
- bool isFinished;
-
void nameChanged()
{
emit q_func()->nameChanged(name);
@@ -75,6 +139,13 @@ public:
Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, QString, name,
&QDnsLookupPrivate::nameChanged);
+ void nameserverChanged()
+ {
+ emit q_func()->nameserverChanged(nameserver);
+ }
+ Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, QHostAddress, nameserver,
+ &QDnsLookupPrivate::nameserverChanged);
+
void typeChanged()
{
emit q_func()->typeChanged(type);
@@ -83,15 +154,18 @@ public:
Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, QDnsLookup::Type,
type, &QDnsLookupPrivate::typeChanged);
- void nameserverChanged()
+ void nameserverPortChanged()
{
- emit q_func()->nameserverChanged(nameserver);
+ emit q_func()->nameserverPortChanged(port);
}
- Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, QHostAddress, nameserver,
- &QDnsLookupPrivate::nameserverChanged);
+
+ Q_OBJECT_BINDABLE_PROPERTY(QDnsLookupPrivate, quint16,
+ port, &QDnsLookupPrivate::nameserverPortChanged);
+
QDnsLookupReply reply;
- QDnsLookupRunnable *runnable;
+ QDnsLookupRunnable *runnable = nullptr;
+ bool isFinished = false;
Q_DECLARE_PUBLIC(QDnsLookup)
};
@@ -101,40 +175,31 @@ class QDnsLookupRunnable : public QObject, public QRunnable
Q_OBJECT
public:
- QDnsLookupRunnable(QDnsLookup::Type type, const QByteArray &name, const QHostAddress &nameserver)
- : requestType(type)
- , requestName(name)
- , nameserver(nameserver)
- { }
+#ifdef Q_OS_WIN
+ using EncodedLabel = QString;
+#else
+ using EncodedLabel = QByteArray;
+#endif
+
+ QDnsLookupRunnable(const QDnsLookupPrivate *d);
void run() override;
signals:
void finished(const QDnsLookupReply &reply);
private:
- static void query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply);
- QDnsLookup::Type requestType;
- QByteArray requestName;
- QHostAddress nameserver;
-};
-
-#if QT_CONFIG(thread)
-class QDnsLookupThreadPool : public QThreadPool
-{
- Q_OBJECT
-
-public:
- QDnsLookupThreadPool();
- void start(QRunnable *runnable);
-
-private slots:
- void _q_applicationDestroyed();
+ template <typename T> static QString decodeLabel(T encodedLabel)
+ {
+ return qt_ACE_do(encodedLabel.toString(), NormalizeAce, ForbidLeadingDot);
+ }
+ void query(QDnsLookupReply *reply);
-private:
- QMutex signalsMutex;
- bool signalsConnected;
+ EncodedLabel requestName;
+ QHostAddress nameserver;
+ QDnsLookup::Type requestType;
+ quint16 port;
+ friend QDebug operator<<(QDebug &, QDnsLookupRunnable *);
};
-#endif // QT_CONFIG(thread)
class QDnsRecordPrivate : public QSharedData
{
@@ -202,6 +267,4 @@ public:
QT_END_NAMESPACE
-QT_DECL_METATYPE_EXTERN(QDnsLookupReply, Q_NETWORK_EXPORT)
-
#endif // QDNSLOOKUP_P_H
diff --git a/src/network/kernel/qdnslookup_unix.cpp b/src/network/kernel/qdnslookup_unix.cpp
index 75f7c6c440..5696a3ca70 100644
--- a/src/network/kernel/qdnslookup_unix.cpp
+++ b/src/network/kernel/qdnslookup_unix.cpp
@@ -1,387 +1,392 @@
// Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+// Copyright (C) 2023 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qdnslookup_p.h"
-#if QT_CONFIG(library)
-#include <qlibrary.h>
-#endif
-#include <qvarlengtharray.h>
+#include <qendian.h>
#include <qscopedpointer.h>
#include <qurl.h>
-#include <private/qnativesocketengine_p.h>
+#include <qvarlengtharray.h>
+#include <private/qnativesocketengine_p.h> // for setSockAddr
+#include <private/qtnetwork-config_p.h>
+
+QT_REQUIRE_CONFIG(libresolv);
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/nameser.h>
-#if !defined(Q_OS_OPENBSD)
+#if __has_include(<arpa/nameser_compat.h>)
# include <arpa/nameser_compat.h>
#endif
+#include <errno.h>
#include <resolv.h>
-#if defined(__GNU_LIBRARY__) && !defined(__UCLIBC__)
-# include <gnu/lib-names.h>
-#endif
+#include <array>
-#if defined(Q_OS_FREEBSD) || QT_CONFIG(dlopen)
-# include <dlfcn.h>
+#ifndef T_OPT
+// the older arpa/nameser_compat.h wasn't updated between 1999 and 2016 in glibc
+# define T_OPT ns_t_opt
#endif
-#include <cstring>
-
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-#if QT_CONFIG(library)
+// minimum IPv6 MTU (1280) minus the IPv6 (40) and UDP headers (8)
+static constexpr qsizetype ReplyBufferSize = 1280 - 40 - 8;
+
+// https://www.rfc-editor.org/rfc/rfc6891
+static constexpr unsigned char Edns0Record[] = {
+ 0x00, // root label
+ T_OPT >> 8, T_OPT & 0xff, // type OPT
+ ReplyBufferSize >> 8, ReplyBufferSize & 0xff, // payload size
+ NOERROR, // extended rcode
+ 0, // version
+ 0x00, 0x00, // flags
+ 0x00, 0x00, // option length
+};
-#if defined(Q_OS_OPENBSD)
-typedef struct __res_state* res_state;
-#endif
-typedef int (*dn_expand_proto)(const unsigned char *, const unsigned char *, const unsigned char *, char *, int);
-static dn_expand_proto local_dn_expand = nullptr;
-typedef void (*res_nclose_proto)(res_state);
-static res_nclose_proto local_res_nclose = nullptr;
-typedef int (*res_ninit_proto)(res_state);
-static res_ninit_proto local_res_ninit = nullptr;
-typedef int (*res_nquery_proto)(res_state, const char *, int, int, unsigned char *, int);
-static res_nquery_proto local_res_nquery = nullptr;
-
-// Custom deleter to close resolver state.
-
-struct QDnsLookupStateDeleter
+// maximum length of a EDNS0 query with a 255-character domain (rounded up to 16)
+static constexpr qsizetype QueryBufferSize =
+ HFIXEDSZ + QFIXEDSZ + MAXCDNAME + 1 + sizeof(Edns0Record);
+using QueryBuffer = std::array<unsigned char, (QueryBufferSize + 15) / 16 * 16>;
+
+namespace {
+struct QDnsCachedName
{
- static inline void cleanup(struct __res_state *pointer)
- {
- local_res_nclose(pointer);
- }
+ QString name;
+ int code = 0;
+ QDnsCachedName(const QString &name, int code) : name(name), code(code) {}
};
+}
+Q_DECLARE_TYPEINFO(QDnsCachedName, Q_RELOCATABLE_TYPE);
+using Cache = QList<QDnsCachedName>; // QHash or QMap are overkill
-static QFunctionPointer resolveSymbol(QLibrary &lib, const char *sym)
+#if QT_CONFIG(res_setservers)
+// https://www.ibm.com/docs/en/i/7.3?topic=ssw_ibm_i_73/apis/ressetservers.html
+// https://docs.oracle.com/cd/E86824_01/html/E54774/res-setservers-3resolv.html
+static bool applyNameServer(res_state state, const QHostAddress &nameserver, quint16 port)
{
- if (lib.isLoaded())
- return lib.resolve(sym);
-
-#if defined(RTLD_DEFAULT) && (defined(Q_OS_FREEBSD) || QT_CONFIG(dlopen))
- return reinterpret_cast<QFunctionPointer>(dlsym(RTLD_DEFAULT, sym));
+ if (!nameserver.isNull()) {
+ union res_sockaddr_union u;
+ setSockaddr(reinterpret_cast<sockaddr *>(&u.sin), nameserver, port);
+ res_setservers(state, &u, 1);
+ }
+ return true;
+}
#else
- return nullptr;
-#endif
+template <typename T> void setNsMap(T &ext, std::enable_if_t<sizeof(T::nsmap) != 0, uint16_t> v)
+{
+ // Set nsmap[] to indicate that nsaddrs[0] is an IPv6 address
+ // See: https://sourceware.org/ml/libc-hacker/2002-05/msg00035.html
+ // Unneeded since glibc 2.22 (2015), but doesn't hurt to set it
+ // See: https://sourceware.org/git/?p=glibc.git;a=commit;h=2212c1420c92a33b0e0bd9a34938c9814a56c0f7
+ ext.nsmap[0] = v;
}
-
-static bool resolveLibraryInternal()
+template <typename T> void setNsMap(T &, ...)
{
- QLibrary lib;
-#ifdef LIBRESOLV_SO
- lib.setFileName(QStringLiteral(LIBRESOLV_SO));
- if (!lib.load())
-#endif
- {
- lib.setFileName("resolv"_L1);
- lib.load();
- }
+ // fallback
+}
- local_dn_expand = dn_expand_proto(resolveSymbol(lib, "__dn_expand"));
- if (!local_dn_expand)
- local_dn_expand = dn_expand_proto(resolveSymbol(lib, "dn_expand"));
+template <bool Condition>
+using EnableIfIPv6 = std::enable_if_t<Condition, const QHostAddress *>;
- local_res_nclose = res_nclose_proto(resolveSymbol(lib, "__res_nclose"));
- if (!local_res_nclose)
- local_res_nclose = res_nclose_proto(resolveSymbol(lib, "res_9_nclose"));
- if (!local_res_nclose)
- local_res_nclose = res_nclose_proto(resolveSymbol(lib, "res_nclose"));
+template <typename State>
+bool setIpv6NameServer(State *state,
+ EnableIfIPv6<sizeof(std::declval<State>()._u._ext.nsaddrs) != 0> addr,
+ quint16 port)
+{
+ // glibc-like API to set IPv6 name servers
+ struct sockaddr_in6 *ns = state->_u._ext.nsaddrs[0];
+
+ // nsaddrs will be NULL if no nameserver is set in /etc/resolv.conf
+ if (!ns) {
+ // Memory allocated here will be free()'d in res_close() as we
+ // have done res_init() above.
+ ns = static_cast<struct sockaddr_in6*>(calloc(1, sizeof(struct sockaddr_in6)));
+ Q_CHECK_PTR(ns);
+ state->_u._ext.nsaddrs[0] = ns;
+ }
- local_res_ninit = res_ninit_proto(resolveSymbol(lib, "__res_ninit"));
- if (!local_res_ninit)
- local_res_ninit = res_ninit_proto(resolveSymbol(lib, "res_9_ninit"));
- if (!local_res_ninit)
- local_res_ninit = res_ninit_proto(resolveSymbol(lib, "res_ninit"));
+ setNsMap(state->_u._ext, MAXNS + 1);
+ state->_u._ext.nscount6 = 1;
+ setSockaddr(ns, *addr, port);
+ return true;
+}
- local_res_nquery = res_nquery_proto(resolveSymbol(lib, "__res_nquery"));
- if (!local_res_nquery)
- local_res_nquery = res_nquery_proto(resolveSymbol(lib, "res_9_nquery"));
- if (!local_res_nquery)
- local_res_nquery = res_nquery_proto(resolveSymbol(lib, "res_nquery"));
+template <typename State> bool setIpv6NameServer(State *, const void *, quint16)
+{
+ // fallback
+ return false;
+}
+static bool applyNameServer(res_state state, const QHostAddress &nameserver, quint16 port)
+{
+ if (nameserver.isNull())
+ return true;
+
+ state->nscount = 1;
+ state->nsaddr_list[0].sin_family = AF_UNSPEC;
+ if (nameserver.protocol() == QAbstractSocket::IPv6Protocol)
+ return setIpv6NameServer(state, &nameserver, port);
+ setSockaddr(&state->nsaddr_list[0], nameserver, port);
return true;
}
-Q_GLOBAL_STATIC_WITH_ARGS(bool, resolveLibrary, (resolveLibraryInternal()))
+#endif // !QT_CONFIG(res_setservers)
-void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply)
+static int
+prepareQueryBuffer(res_state state, QueryBuffer &buffer, const char *label, ns_rcode type)
{
- // Load dn_expand, res_ninit and res_nquery on demand.
- resolveLibrary();
-
- // If dn_expand, res_ninit or res_nquery is missing, fail.
- if (!local_dn_expand || !local_res_nclose || !local_res_ninit || !local_res_nquery) {
- reply->error = QDnsLookup::ResolverError;
- reply->errorString = tr("Resolver functions not found");
- return;
- }
+ // Create header and our query
+ int queryLength = res_nmkquery(state, QUERY, label, C_IN, type, nullptr, 0, nullptr,
+ buffer.data(), buffer.size());
+ Q_ASSERT(queryLength < int(buffer.size()));
+ if (Q_UNLIKELY(queryLength < 0))
+ return queryLength;
+
+ // Append EDNS0 record and set the number of additional RRs to 1
+ Q_ASSERT(queryLength + sizeof(Edns0Record) < buffer.size());
+ std::copy_n(std::begin(Edns0Record), sizeof(Edns0Record), buffer.begin() + queryLength);
+ reinterpret_cast<HEADER *>(buffer.data())->arcount = qToBigEndian<quint16>(1);
+
+ return queryLength + sizeof(Edns0Record);
+}
+void QDnsLookupRunnable::query(QDnsLookupReply *reply)
+{
// Initialize state.
- struct __res_state state;
- std::memset(&state, 0, sizeof(state));
- if (local_res_ninit(&state) < 0) {
- reply->error = QDnsLookup::ResolverError;
- reply->errorString = tr("Resolver initialization failed");
- return;
+ std::remove_pointer_t<res_state> state = {};
+ if (res_ninit(&state) < 0) {
+ int error = errno;
+ qErrnoWarning(error, "QDnsLookup: Resolver initialization failed");
+ return reply->makeResolverSystemError(error);
}
+ auto guard = qScopeGuard([&] { res_nclose(&state); });
//Check if a nameserver was set. If so, use it
- if (!nameserver.isNull()) {
- if (nameserver.protocol() == QAbstractSocket::IPv4Protocol) {
- state.nsaddr_list[0].sin_addr.s_addr = htonl(nameserver.toIPv4Address());
- state.nscount = 1;
- } else if (nameserver.protocol() == QAbstractSocket::IPv6Protocol) {
-#if defined(Q_OS_LINUX)
- struct sockaddr_in6 *ns;
- ns = state._u._ext.nsaddrs[0];
- // nsaddrs will be NULL if no nameserver is set in /etc/resolv.conf
- if (!ns) {
- // Memory allocated here will be free'd in res_close() as we
- // have done res_init() above.
- ns = (struct sockaddr_in6*) calloc(1, sizeof(struct sockaddr_in6));
- Q_CHECK_PTR(ns);
- state._u._ext.nsaddrs[0] = ns;
- }
-#ifndef __UCLIBC__
- // Set nsmap[] to indicate that nsaddrs[0] is an IPv6 address
- // See: https://sourceware.org/ml/libc-hacker/2002-05/msg00035.html
- state._u._ext.nsmap[0] = MAXNS + 1;
-#endif
- state._u._ext.nscount6 = 1;
- ns->sin6_family = AF_INET6;
- ns->sin6_port = htons(53);
- SetSALen::set(ns, sizeof(*ns));
-
- Q_IPV6ADDR ipv6Address = nameserver.toIPv6Address();
- for (int i=0; i<16; i++) {
- ns->sin6_addr.s6_addr[i] = ipv6Address[i];
- }
-#else
- qWarning("%s", QDnsLookupPrivate::msgNoIpV6NameServerAdresses);
- reply->error = QDnsLookup::ResolverError;
- reply->errorString = tr(QDnsLookupPrivate::msgNoIpV6NameServerAdresses);
- return;
-#endif
- }
- }
+ if (!applyNameServer(&state, nameserver, port))
+ return reply->setError(QDnsLookup::ResolverError,
+ QDnsLookup::tr("IPv6 nameservers are currently not supported on this OS"));
#ifdef QDNSLOOKUP_DEBUG
state.options |= RES_DEBUG;
#endif
- QScopedPointer<struct __res_state, QDnsLookupStateDeleter> state_ptr(&state);
+
+ // Prepare the DNS query.
+ QueryBuffer qbuffer;
+ int queryLength = prepareQueryBuffer(&state, qbuffer, requestName.constData(), ns_rcode(requestType));
+ if (Q_UNLIKELY(queryLength < 0))
+ return reply->makeResolverSystemError();
// Perform DNS query.
- QVarLengthArray<unsigned char, PACKETSZ> buffer(PACKETSZ);
- std::memset(buffer.data(), 0, buffer.size());
- int responseLength = local_res_nquery(&state, requestName, C_IN, requestType, buffer.data(), buffer.size());
- if (Q_UNLIKELY(responseLength > PACKETSZ)) {
- buffer.resize(responseLength);
- std::memset(buffer.data(), 0, buffer.size());
- responseLength = local_res_nquery(&state, requestName, C_IN, requestType, buffer.data(), buffer.size());
+ QVarLengthArray<unsigned char, ReplyBufferSize> buffer(ReplyBufferSize);
+ auto attemptToSend = [&]() {
+ std::memset(buffer.data(), 0, HFIXEDSZ); // the header is enough
+ int responseLength = res_nsend(&state, qbuffer.data(), queryLength, buffer.data(), buffer.size());
+ if (responseLength >= 0)
+ return responseLength; // success
+
+ // libresolv uses ETIMEDOUT for resolver errors ("no answer")
+ if (errno == ECONNREFUSED)
+ reply->setError(QDnsLookup::ServerRefusedError, qt_error_string());
+ else if (errno != ETIMEDOUT)
+ reply->makeResolverSystemError(); // some other error
+
+ auto query = reinterpret_cast<HEADER *>(qbuffer.data());
+ auto header = reinterpret_cast<HEADER *>(buffer.data());
+ if (query->id == header->id && header->qr)
+ reply->makeDnsRcodeError(header->rcode);
+ else
+ reply->makeTimeoutError(); // must really be a timeout
+ return -1;
+ };
+
+ // strictly use UDP, we'll deal with truncated replies ourselves
+ state.options |= RES_IGNTC;
+ int responseLength = attemptToSend();
+ if (responseLength < 0)
+ return;
+
+ // check if we need to use the virtual circuit (TCP)
+ auto header = reinterpret_cast<HEADER *>(buffer.data());
+ if (header->rcode == NOERROR && header->tc) {
+ // yes, increase our buffer size
+ buffer.resize(std::numeric_limits<quint16>::max());
+ header = reinterpret_cast<HEADER *>(buffer.data());
+
+ // remove the EDNS record in the query
+ reinterpret_cast<HEADER *>(qbuffer.data())->arcount = 0;
+ queryLength -= sizeof(Edns0Record);
+
+ // send using the virtual circuit
+ state.options |= RES_USEVC;
+ responseLength = attemptToSend();
if (Q_UNLIKELY(responseLength > buffer.size())) {
// Ok, we give up.
- reply->error = QDnsLookup::ResolverError;
- reply->errorString.clear(); // We cannot be more specific, alas.
- return;
+ return reply->setError(QDnsLookup::ResolverError,
+ QDnsLookup::tr("Reply was too large"));
}
}
-
- unsigned char *response = buffer.data();
- // Check the response header. Though res_nquery returns -1 as a
- // responseLength in case of error, we still can extract the
- // exact error code from the response.
- HEADER *header = (HEADER*)response;
- const int answerCount = ntohs(header->ancount);
- switch (header->rcode) {
- case NOERROR:
- break;
- case FORMERR:
- reply->error = QDnsLookup::InvalidRequestError;
- reply->errorString = tr("Server could not process query");
- return;
- case SERVFAIL:
- reply->error = QDnsLookup::ServerFailureError;
- reply->errorString = tr("Server failure");
+ if (responseLength < 0)
return;
- case NXDOMAIN:
- reply->error = QDnsLookup::NotFoundError;
- reply->errorString = tr("Non existent domain");
- return;
- case REFUSED:
- reply->error = QDnsLookup::ServerRefusedError;
- reply->errorString = tr("Server refused to answer");
- return;
- default:
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid reply received");
- return;
- }
// Check the reply is valid.
- if (responseLength < int(sizeof(HEADER))) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid reply received");
- return;
- }
+ if (responseLength < int(sizeof(HEADER)))
+ return reply->makeInvalidReplyError();
- // Skip the query host, type (2 bytes) and class (2 bytes).
- char host[PACKETSZ], answer[PACKETSZ];
- unsigned char *p = response + sizeof(HEADER);
- int status = local_dn_expand(response, response + responseLength, p, host, sizeof(host));
- if (status < 0) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Could not expand domain name");
- return;
+ // Parse the reply.
+ if (header->rcode)
+ return reply->makeDnsRcodeError(header->rcode);
+
+ qptrdiff offset = sizeof(HEADER);
+ unsigned char *response = buffer.data();
+ int status;
+
+ auto expandHost = [&, cache = Cache{}](qptrdiff offset) mutable {
+ if (uchar n = response[offset]; n & NS_CMPRSFLGS) {
+ // compressed name, see if we already have it cached
+ if (offset + 1 < responseLength) {
+ int id = ((n & ~NS_CMPRSFLGS) << 8) | response[offset + 1];
+ auto it = std::find_if(cache.constBegin(), cache.constEnd(),
+ [id](const QDnsCachedName &n) { return n.code == id; });
+ if (it != cache.constEnd()) {
+ status = 2;
+ return it->name;
+ }
+ }
+ }
+
+ // uncached, expand it
+ char host[MAXCDNAME + 1];
+ status = dn_expand(response, response + responseLength, response + offset,
+ host, sizeof(host));
+ if (status >= 0)
+ return cache.emplaceBack(decodeLabel(QLatin1StringView(host)), offset).name;
+
+ // failed
+ reply->makeInvalidReplyError(QDnsLookup::tr("Could not expand domain name"));
+ return QString();
+ };
+
+ if (ntohs(header->qdcount) == 1) {
+ // Skip the query host, type (2 bytes) and class (2 bytes).
+ expandHost(offset);
+ if (status < 0)
+ return;
+ if (offset + status + 4 >= responseLength)
+ header->qdcount = 0xffff; // invalid reply below
+ else
+ offset += status + 4;
}
- p += status + 4;
+ if (ntohs(header->qdcount) > 1)
+ return reply->makeInvalidReplyError();
// Extract results.
+ const int answerCount = ntohs(header->ancount);
int answerIndex = 0;
- while ((p < response + responseLength) && (answerIndex < answerCount)) {
- status = local_dn_expand(response, response + responseLength, p, host, sizeof(host));
- if (status < 0) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Could not expand domain name");
+ while ((offset < responseLength) && (answerIndex < answerCount)) {
+ const QString name = expandHost(offset);
+ if (status < 0)
return;
- }
- const QString name = QUrl::fromAce(host);
- p += status;
- const quint16 type = (p[0] << 8) | p[1];
- p += 2; // RR type
- p += 2; // RR class
- const quint32 ttl = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
- p += 4;
- const quint16 size = (p[0] << 8) | p[1];
- p += 2;
+ offset += status;
+ if (offset + RRFIXEDSZ > responseLength) {
+ // probably just a truncated reply, return what we have
+ return;
+ }
+ const quint16 type = qFromBigEndian<quint16>(response + offset);
+ const qint16 rrclass = qFromBigEndian<quint16>(response + offset + 2);
+ const quint32 ttl = qFromBigEndian<quint32>(response + offset + 4);
+ const quint16 size = qFromBigEndian<quint16>(response + offset + 8);
+ offset += RRFIXEDSZ;
+ if (offset + size > responseLength)
+ return; // truncated
+ if (rrclass != C_IN)
+ continue;
if (type == QDnsLookup::A) {
- if (size != 4) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid IPv4 address record");
- return;
- }
- const quint32 addr = (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
+ if (size != 4)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid IPv4 address record"));
+ const quint32 addr = qFromBigEndian<quint32>(response + offset);
QDnsHostAddressRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
record.d->value = QHostAddress(addr);
reply->hostAddressRecords.append(record);
} else if (type == QDnsLookup::AAAA) {
- if (size != 16) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid IPv6 address record");
- return;
- }
+ if (size != 16)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid IPv6 address record"));
QDnsHostAddressRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
- record.d->value = QHostAddress(p);
+ record.d->value = QHostAddress(response + offset);
reply->hostAddressRecords.append(record);
} else if (type == QDnsLookup::CNAME) {
- status = local_dn_expand(response, response + responseLength, p, answer, sizeof(answer));
- if (status < 0) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid canonical name record");
- return;
- }
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
- record.d->value = QUrl::fromAce(answer);
+ record.d->value = expandHost(offset);
+ if (status < 0)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid canonical name record"));
reply->canonicalNameRecords.append(record);
} else if (type == QDnsLookup::NS) {
- status = local_dn_expand(response, response + responseLength, p, answer, sizeof(answer));
- if (status < 0) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid name server record");
- return;
- }
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
- record.d->value = QUrl::fromAce(answer);
+ record.d->value = expandHost(offset);
+ if (status < 0)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid name server record"));
reply->nameServerRecords.append(record);
} else if (type == QDnsLookup::PTR) {
- status = local_dn_expand(response, response + responseLength, p, answer, sizeof(answer));
- if (status < 0) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid pointer record");
- return;
- }
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
- record.d->value = QUrl::fromAce(answer);
+ record.d->value = expandHost(offset);
+ if (status < 0)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid pointer record"));
reply->pointerRecords.append(record);
} else if (type == QDnsLookup::MX) {
- const quint16 preference = (p[0] << 8) | p[1];
- status = local_dn_expand(response, response + responseLength, p + 2, answer, sizeof(answer));
- if (status < 0) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid mail exchange record");
- return;
- }
+ const quint16 preference = qFromBigEndian<quint16>(response + offset);
QDnsMailExchangeRecord record;
- record.d->exchange = QUrl::fromAce(answer);
+ record.d->exchange = expandHost(offset + 2);
record.d->name = name;
record.d->preference = preference;
record.d->timeToLive = ttl;
+ if (status < 0)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid mail exchange record"));
reply->mailExchangeRecords.append(record);
} else if (type == QDnsLookup::SRV) {
- const quint16 priority = (p[0] << 8) | p[1];
- const quint16 weight = (p[2] << 8) | p[3];
- const quint16 port = (p[4] << 8) | p[5];
- status = local_dn_expand(response, response + responseLength, p + 6, answer, sizeof(answer));
- if (status < 0) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid service record");
- return;
- }
+ const quint16 priority = qFromBigEndian<quint16>(response + offset);
+ const quint16 weight = qFromBigEndian<quint16>(response + offset + 2);
+ const quint16 port = qFromBigEndian<quint16>(response + offset + 4);
QDnsServiceRecord record;
record.d->name = name;
- record.d->target = QUrl::fromAce(answer);
+ record.d->target = expandHost(offset + 6);
record.d->port = port;
record.d->priority = priority;
record.d->timeToLive = ttl;
record.d->weight = weight;
+ if (status < 0)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid service record"));
reply->serviceRecords.append(record);
} else if (type == QDnsLookup::TXT) {
- unsigned char *txt = p;
QDnsTextRecord record;
record.d->name = name;
record.d->timeToLive = ttl;
- while (txt < p + size) {
- const unsigned char length = *txt;
+ qptrdiff txt = offset;
+ while (txt < offset + size) {
+ const unsigned char length = response[txt];
txt++;
- if (txt + length > p + size) {
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = tr("Invalid text record");
- return;
- }
- record.d->values << QByteArray((char*)txt, length);
+ if (txt + length > offset + size)
+ return reply->makeInvalidReplyError(QDnsLookup::tr("Invalid text record"));
+ record.d->values << QByteArrayView(response + txt, length).toByteArray();
txt += length;
}
reply->textRecords.append(record);
}
- p += size;
+ offset += size;
answerIndex++;
}
}
-#else
-void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply)
-{
- Q_UNUSED(requestType);
- Q_UNUSED(requestName);
- Q_UNUSED(nameserver);
- reply->error = QDnsLookup::ResolverError;
- reply->errorString = tr("Resolver library can't be loaded: No runtime library loading support");
- return;
-}
-
-#endif /* QT_CONFIG(library) */
-
QT_END_NAMESPACE
diff --git a/src/network/kernel/qdnslookup_win.cpp b/src/network/kernel/qdnslookup_win.cpp
index 564966e395..72d5ae5c86 100644
--- a/src/network/kernel/qdnslookup_win.cpp
+++ b/src/network/kernel/qdnslookup_win.cpp
@@ -1,69 +1,115 @@
// Copyright (C) 2012 Jeremy Lainé <jeremy.laine@m4x.org>
+// Copyright (C) 2023 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <winsock2.h>
#include "qdnslookup_p.h"
#include <qurl.h>
+#include <private/qnativesocketengine_p.h>
#include <private/qsystemerror_p.h>
#include <qt_windows.h>
#include <windns.h>
#include <memory.h>
+#ifndef DNS_ADDR_MAX_SOCKADDR_LENGTH
+// MinGW headers are missing almost all of this
+typedef struct Qt_DnsAddr {
+ CHAR MaxSa[32];
+ DWORD DnsAddrUserDword[8];
+} DNS_ADDR, *PDNS_ADDR;
+typedef struct Qt_DnsAddrArray {
+ DWORD MaxCount;
+ DWORD AddrCount;
+ DWORD Tag;
+ WORD Family;
+ WORD WordReserved;
+ DWORD Flags;
+ DWORD MatchFlag;
+ DWORD Reserved1;
+ DWORD Reserved2;
+ DNS_ADDR AddrArray[];
+} DNS_ADDR_ARRAY, *PDNS_ADDR_ARRAY;
+# ifndef DNS_QUERY_RESULTS_VERSION1
+typedef struct Qt_DNS_QUERY_RESULT {
+ ULONG Version;
+ DNS_STATUS QueryStatus;
+ ULONG64 QueryOptions;
+ PDNS_RECORD pQueryRecords;
+ PVOID Reserved;
+} DNS_QUERY_RESULT, *PDNS_QUERY_RESULT;
+typedef VOID WINAPI DNS_QUERY_COMPLETION_ROUTINE(PVOID pQueryContext,PDNS_QUERY_RESULT pQueryResults);
+typedef DNS_QUERY_COMPLETION_ROUTINE *PDNS_QUERY_COMPLETION_ROUTINE;
+# endif
+typedef struct Qt_DNS_QUERY_REQUEST {
+ ULONG Version;
+ PCWSTR QueryName;
+ WORD QueryType;
+ ULONG64 QueryOptions;
+ PDNS_ADDR_ARRAY pDnsServerList;
+ ULONG InterfaceIndex;
+ PDNS_QUERY_COMPLETION_ROUTINE pQueryCompletionCallback;
+ PVOID pQueryContext;
+} DNS_QUERY_REQUEST, *PDNS_QUERY_REQUEST;
+
+typedef void *PDNS_QUERY_CANCEL; // not really, but we don't need it
+extern "C" {
+DNS_STATUS WINAPI DnsQueryEx(PDNS_QUERY_REQUEST pQueryRequest,
+ PDNS_QUERY_RESULT pQueryResults,
+ PDNS_QUERY_CANCEL pCancelHandle);
+}
+#endif
+
QT_BEGIN_NAMESPACE
-void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestName, const QHostAddress &nameserver, QDnsLookupReply *reply)
+void QDnsLookupRunnable::query(QDnsLookupReply *reply)
{
// Perform DNS query.
- PDNS_RECORD dns_records = 0;
- const QString requestNameUtf16 = QString::fromUtf8(requestName.data(), requestName.size());
- IP4_ARRAY srvList;
- memset(&srvList, 0, sizeof(IP4_ARRAY));
+ alignas(DNS_ADDR_ARRAY) uchar dnsAddresses[sizeof(DNS_ADDR_ARRAY) + sizeof(DNS_ADDR)];
+ DNS_QUERY_REQUEST request = {};
+ request.Version = 1;
+ request.QueryName = reinterpret_cast<const wchar_t *>(requestName.constData());
+ request.QueryType = requestType;
+ request.QueryOptions = DNS_QUERY_STANDARD | DNS_QUERY_TREAT_AS_FQDN;
+
if (!nameserver.isNull()) {
- if (nameserver.protocol() == QAbstractSocket::IPv4Protocol) {
- // The below code is referenced from: http://support.microsoft.com/kb/831226
- srvList.AddrCount = 1;
- srvList.AddrArray[0] = htonl(nameserver.toIPv4Address());
- } else if (nameserver.protocol() == QAbstractSocket::IPv6Protocol) {
- // For supoprting IPv6 nameserver addresses, we'll need to switch
- // from DnsQuey() to DnsQueryEx() as it supports passing an IPv6
- // address in the nameserver list
- qWarning("%s", QDnsLookupPrivate::msgNoIpV6NameServerAdresses);
- reply->error = QDnsLookup::ResolverError;
- reply->errorString = tr(QDnsLookupPrivate::msgNoIpV6NameServerAdresses);
- return;
- }
- }
- const DNS_STATUS status = DnsQuery_W(reinterpret_cast<const wchar_t*>(requestNameUtf16.utf16()), requestType, DNS_QUERY_STANDARD, &srvList, &dns_records, NULL);
- switch (status) {
- case ERROR_SUCCESS:
- break;
- case DNS_ERROR_RCODE_FORMAT_ERROR:
- reply->error = QDnsLookup::InvalidRequestError;
- reply->errorString = tr("Server could not process query");
- return;
- case DNS_ERROR_RCODE_SERVER_FAILURE:
- reply->error = QDnsLookup::ServerFailureError;
- reply->errorString = tr("Server failure");
- return;
- case DNS_ERROR_RCODE_NAME_ERROR:
- reply->error = QDnsLookup::NotFoundError;
- reply->errorString = tr("Non existent domain");
- return;
- case DNS_ERROR_RCODE_REFUSED:
- reply->error = QDnsLookup::ServerRefusedError;
- reply->errorString = tr("Server refused to answer");
- return;
- default:
- reply->error = QDnsLookup::InvalidReplyError;
- reply->errorString = QSystemError(status, QSystemError::NativeError).toString();
- return;
+ memset(dnsAddresses, 0, sizeof(dnsAddresses));
+ request.pDnsServerList = new (dnsAddresses) DNS_ADDR_ARRAY;
+ auto addr = new (request.pDnsServerList->AddrArray) DNS_ADDR[1];
+ auto sa = new (addr[0].MaxSa) sockaddr;
+ request.pDnsServerList->MaxCount = sizeof(dnsAddresses);
+ request.pDnsServerList->AddrCount = 1;
+ // ### setting port 53 seems to cause some systems to fail
+ setSockaddr(sa, nameserver, port == DnsPort ? 0 : port);
+ request.pDnsServerList->Family = sa->sa_family;
}
+ DNS_QUERY_RESULT results = {};
+ results.Version = 1;
+ const DNS_STATUS status = DnsQueryEx(&request, &results, nullptr);
+ if (status >= DNS_ERROR_RCODE_FORMAT_ERROR && status <= DNS_ERROR_RCODE_LAST)
+ return reply->makeDnsRcodeError(status - DNS_ERROR_RCODE_FORMAT_ERROR + 1);
+ else if (status == ERROR_TIMEOUT)
+ return reply->makeTimeoutError();
+ else if (status != ERROR_SUCCESS)
+ return reply->makeResolverSystemError(status);
+
+ QStringView lastEncodedName;
+ QString cachedDecodedName;
+ auto extractAndCacheHost = [&](QStringView name) -> const QString & {
+ lastEncodedName = name;
+ cachedDecodedName = decodeLabel(name);
+ return cachedDecodedName;
+ };
+ auto extractMaybeCachedHost = [&](QStringView name) -> const QString & {
+ return lastEncodedName == name ? cachedDecodedName : extractAndCacheHost(name);
+ };
+
// Extract results.
- for (PDNS_RECORD ptr = dns_records; ptr != NULL; ptr = ptr->pNext) {
- const QString name = QUrl::fromAce( QString::fromWCharArray( ptr->pName ).toLatin1() );
+ for (PDNS_RECORD ptr = results.pQueryRecords; ptr != NULL; ptr = ptr->pNext) {
+ // warning: always assign name to the record before calling extractXxxHost() again
+ const QString &name = extractMaybeCachedHost(ptr->pName);
if (ptr->wType == QDnsLookup::A) {
QDnsHostAddressRecord record;
record.d->name = name;
@@ -83,12 +129,12 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ptr->dwTtl;
- record.d->value = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Cname.pNameHost).toLatin1());
+ record.d->value = extractAndCacheHost(ptr->Data.Cname.pNameHost);
reply->canonicalNameRecords.append(record);
} else if (ptr->wType == QDnsLookup::MX) {
QDnsMailExchangeRecord record;
record.d->name = name;
- record.d->exchange = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Mx.pNameExchange).toLatin1());
+ record.d->exchange = decodeLabel(QStringView(ptr->Data.Mx.pNameExchange));
record.d->preference = ptr->Data.Mx.wPreference;
record.d->timeToLive = ptr->dwTtl;
reply->mailExchangeRecords.append(record);
@@ -96,18 +142,18 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ptr->dwTtl;
- record.d->value = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Ns.pNameHost).toLatin1());
+ record.d->value = decodeLabel(QStringView(ptr->Data.Ns.pNameHost));
reply->nameServerRecords.append(record);
} else if (ptr->wType == QDnsLookup::PTR) {
QDnsDomainNameRecord record;
record.d->name = name;
record.d->timeToLive = ptr->dwTtl;
- record.d->value = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Ptr.pNameHost).toLatin1());
+ record.d->value = decodeLabel(QStringView(ptr->Data.Ptr.pNameHost));
reply->pointerRecords.append(record);
} else if (ptr->wType == QDnsLookup::SRV) {
QDnsServiceRecord record;
record.d->name = name;
- record.d->target = QUrl::fromAce(QString::fromWCharArray(ptr->Data.Srv.pNameTarget).toLatin1());
+ record.d->target = decodeLabel(QStringView(ptr->Data.Srv.pNameTarget));
record.d->port = ptr->Data.Srv.wPort;
record.d->priority = ptr->Data.Srv.wPriority;
record.d->timeToLive = ptr->dwTtl;
@@ -118,13 +164,13 @@ void QDnsLookupRunnable::query(const int requestType, const QByteArray &requestN
record.d->name = name;
record.d->timeToLive = ptr->dwTtl;
for (unsigned int i = 0; i < ptr->Data.Txt.dwStringCount; ++i) {
- record.d->values << QString::fromWCharArray((ptr->Data.Txt.pStringArray[i])).toLatin1();;
+ record.d->values << QStringView(ptr->Data.Txt.pStringArray[i]).toLatin1();
}
reply->textRecords.append(record);
}
}
- DnsRecordListFree(dns_records, DnsFreeRecordList);
+ DnsRecordListFree(results.pQueryRecords, DnsFreeRecordList);
}
QT_END_NAMESPACE
diff --git a/src/network/kernel/qhostaddress.cpp b/src/network/kernel/qhostaddress.cpp
index 8fb321bf20..0330fb091b 100644
--- a/src/network/kernel/qhostaddress.cpp
+++ b/src/network/kernel/qhostaddress.cpp
@@ -164,8 +164,12 @@ AddressClassification QHostAddressPrivate::classify() const
return BroadcastAddress;
return UnknownAddress;
}
+ if (((a & 0xff000000U) == 0x0a000000U) // 10.0.0.0/8
+ || ((a & 0xfff00000U) == 0xac100000U) // 172.16.0.0/12
+ || ((a & 0xffff0000U) == 0xc0a80000U)) // 192.168.0.0/16
+ return PrivateNetworkAddress;
- // Not testing for PrivateNetworkAddress and TestNetworkAddress
+ // Not testing for TestNetworkAddress
// since we don't need them yet.
return GlobalAddress;
}
@@ -673,7 +677,7 @@ QHostAddress::NetworkLayerProtocol QHostAddress::protocol() const
\l{QAbstractSocket::}{IPv6Protocol}.
If the protocol is
\l{QAbstractSocket::}{IPv4Protocol},
- then the address is returned an an IPv4 mapped IPv6 address. (RFC4291)
+ then the address is returned as an IPv4 mapped IPv6 address. (RFC4291)
\sa toString()
*/
@@ -702,7 +706,7 @@ QString QHostAddress::toString() const
} else if (d->protocol == QHostAddress::IPv6Protocol) {
QIPAddressUtils::toString(s, d->a6.c);
if (!d->scopeId.isEmpty())
- s.append(u'%' + d->scopeId);
+ s += u'%' + d->scopeId;
}
return s;
}
@@ -818,7 +822,7 @@ bool QHostAddress::isEqual(const QHostAddress &other, ConversionMode mode) const
return memcmp(&d->a6, &other.d->a6, sizeof(Q_IPV6ADDR)) == 0;
case QHostAddress::AnyIPProtocol:
return (mode & QHostAddress::ConvertUnspecifiedAddress)
- && (other.d->a6_64.c[0] == 0) && (other.d->a6_64.c[1] == 0);
+ && (d->a6_64.c[0] == 0) && (d->a6_64.c[1] == 0);
case QHostAddress::UnknownNetworkLayerProtocol:
return false;
}
@@ -1054,14 +1058,14 @@ QPair<QHostAddress, int> QHostAddress::parseSubnet(const QString &subnet)
// parse the address manually
auto parts = netStr.split(u'.');
- if (parts.isEmpty() || parts.count() > 4)
+ if (parts.isEmpty() || parts.size() > 4)
return invalid; // invalid IPv4 address
if (parts.constLast().isEmpty())
parts.removeLast();
quint32 addr = 0;
- for (int i = 0; i < parts.count(); ++i) {
+ for (int i = 0; i < parts.size(); ++i) {
bool ok;
uint byteValue = parts.at(i).toUInt(&ok);
if (!ok || byteValue > 255)
@@ -1070,9 +1074,9 @@ QPair<QHostAddress, int> QHostAddress::parseSubnet(const QString &subnet)
addr <<= 8;
addr += byteValue;
}
- addr <<= 8 * (4 - parts.count());
+ addr <<= 8 * (4 - parts.size());
if (netmask == -1) {
- netmask = 8 * parts.count();
+ netmask = 8 * parts.size();
} else if (netmask == 0) {
// special case here
// x86's instructions "shr" and "shl" do not operate when
@@ -1113,7 +1117,7 @@ bool QHostAddress::isLoopback() const
considered as global in new applications. This function returns true for
site-local addresses too.
- \sa isLoopback(), isSiteLocal(), isUniqueLocalUnicast()
+ \sa isLoopback(), isSiteLocal(), isUniqueLocalUnicast(), isPrivateUse()
*/
bool QHostAddress::isGlobal() const
{
@@ -1131,7 +1135,7 @@ bool QHostAddress::isGlobal() const
\l{https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml}{IANA
IPv6 Address Space} registry for more information.
- \sa isLoopback(), isGlobal(), isMulticast(), isSiteLocal(), isUniqueLocalUnicast()
+ \sa isLoopback(), isGlobal(), isMulticast(), isSiteLocal(), isUniqueLocalUnicast(), isPrivateUse()
*/
bool QHostAddress::isLinkLocal() const
{
@@ -1154,7 +1158,7 @@ bool QHostAddress::isLinkLocal() const
isGlobal() also returns true). Site-local addresses were replaced by Unique
Local Addresses (ULA).
- \sa isLoopback(), isGlobal(), isMulticast(), isLinkLocal(), isUniqueLocalUnicast()
+ \sa isLoopback(), isGlobal(), isMulticast(), isLinkLocal(), isUniqueLocalUnicast(), isPrivateUse()
*/
bool QHostAddress::isSiteLocal() const
{
@@ -1175,7 +1179,7 @@ bool QHostAddress::isSiteLocal() const
4193 says that, in practice, "applications may treat these addresses like
global scoped addresses." Only routers need care about the distinction.
- \sa isLoopback(), isGlobal(), isMulticast(), isLinkLocal(), isUniqueLocalUnicast()
+ \sa isLoopback(), isGlobal(), isMulticast(), isLinkLocal(), isUniqueLocalUnicast(), isPrivateUse()
*/
bool QHostAddress::isUniqueLocalUnicast() const
{
@@ -1188,7 +1192,7 @@ bool QHostAddress::isUniqueLocalUnicast() const
Returns \c true if the address is an IPv4 or IPv6 multicast address, \c
false otherwise.
- \sa isLoopback(), isGlobal(), isLinkLocal(), isSiteLocal(), isUniqueLocalUnicast()
+ \sa isLoopback(), isGlobal(), isLinkLocal(), isSiteLocal(), isUniqueLocalUnicast(), isPrivateUse()
*/
bool QHostAddress::isMulticast() const
{
@@ -1205,13 +1209,27 @@ bool QHostAddress::isMulticast() const
broadcast address. For that, please use \l QNetworkInterface to obtain the
broadcast addresses of the local machine.
- \sa isLoopback(), isGlobal(), isMulticast(), isLinkLocal(), isUniqueLocalUnicast()
+ \sa isLoopback(), isGlobal(), isMulticast(), isLinkLocal(), isUniqueLocalUnicast(), isPrivateUse()
*/
bool QHostAddress::isBroadcast() const
{
return d->classify() == BroadcastAddress;
}
+/*!
+ \since 6.6
+
+ Returns \c true if the address is an IPv6 unique local unicast address or
+ IPv4 address reserved for local networks by \l {RFC 1918}, \c false otherwise.
+
+ \sa isLoopback(), isGlobal(), isMulticast(), isLinkLocal(), isUniqueLocalUnicast(), isBroadcast()
+*/
+bool QHostAddress::isPrivateUse() const
+{
+ const AddressClassification classification = d->classify();
+ return (classification == PrivateNetworkAddress) || (classification == UniqueLocalAddress);
+}
+
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug d, const QHostAddress &address)
{
diff --git a/src/network/kernel/qhostaddress.h b/src/network/kernel/qhostaddress.h
index 456063d247..6aa045c959 100644
--- a/src/network/kernel/qhostaddress.h
+++ b/src/network/kernel/qhostaddress.h
@@ -20,7 +20,7 @@ QT_BEGIN_NAMESPACE
class QHostAddressPrivate;
-class Q_NETWORK_EXPORT QIPv6Address
+class QT6_ONLY(Q_NETWORK_EXPORT) QIPv6Address
{
public:
inline quint8 &operator [](int index) { return c[index]; }
@@ -127,6 +127,7 @@ public:
bool isUniqueLocalUnicast() const;
bool isMulticast() const;
bool isBroadcast() const;
+ bool isPrivateUse() const;
static QPair<QHostAddress, int> parseSubnet(const QString &subnet);
diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp
index 6a6519e9c1..62bb210ca1 100644
--- a/src/network/kernel/qhostinfo.cpp
+++ b/src/network/kernel/qhostinfo.cpp
@@ -72,6 +72,16 @@ Q_APPLICATION_STATIC(QHostInfoLookupManager, theHostInfoLookupManager)
}
+QHostInfoResult::QHostInfoResult(const QObject *receiver, QtPrivate::SlotObjUniquePtr slot)
+ : receiver{receiver ? receiver : this}, slotObj{std::move(slot)}
+{
+ Q_ASSERT(this->receiver);
+ moveToThread(this->receiver->thread());
+}
+
+QHostInfoResult::~QHostInfoResult()
+ = default;
+
/*
The calling thread is likely the one that executes the lookup via
QHostInfoRunnable. Unless we operate with a queued connection already,
@@ -80,8 +90,8 @@ Q_APPLICATION_STATIC(QHostInfoLookupManager, theHostInfoLookupManager)
the thread that made the call to lookupHost. That QHostInfoResult object
then calls the user code in the correct thread.
- The 'result' object deletes itself (via deleteLater) when the metacall
- event is received.
+ The 'result' object deletes itself (via deleteLater) when
+ finalizePostResultsReady is called.
*/
void QHostInfoResult::postResultsReady(const QHostInfo &info)
{
@@ -91,55 +101,33 @@ void QHostInfoResult::postResultsReady(const QHostInfo &info)
return;
}
// we used to have a context object, but it's already destroyed
- if (withContextObject && !receiver)
+ if (!receiver)
return;
- static const int signal_index = []() -> int {
- auto senderMetaObject = &QHostInfoResult::staticMetaObject;
- auto signal = &QHostInfoResult::resultsReady;
- int signal_index = -1;
- void *args[] = { &signal_index, &signal };
- senderMetaObject->static_metacall(QMetaObject::IndexOfMethod, 0, args);
- return signal_index + QMetaObjectPrivate::signalOffset(senderMetaObject);
- }();
-
// a long-living version of this
auto result = new QHostInfoResult(this);
Q_CHECK_PTR(result);
- const int nargs = 2;
- auto metaCallEvent = new QMetaCallEvent(slotObj, nullptr, signal_index, nargs);
- Q_CHECK_PTR(metaCallEvent);
- void **args = metaCallEvent->args();
- QMetaType *types = metaCallEvent->types();
- auto voidType = QMetaType::fromType<void>();
- auto hostInfoType = QMetaType::fromType<QHostInfo>();
- types[0] = voidType;
- types[1] = hostInfoType;
- args[0] = nullptr;
- args[1] = hostInfoType.create(&info);
- Q_CHECK_PTR(args[1]);
- qApp->postEvent(result, metaCallEvent);
+ QMetaObject::invokeMethod(result,
+ &QHostInfoResult::finalizePostResultsReady,
+ Qt::QueuedConnection,
+ info);
}
/*
- Receives the event posted by postResultsReady, and calls the functor.
+ Receives the info from postResultsReady, and calls the functor.
*/
-bool QHostInfoResult::event(QEvent *event)
+void QHostInfoResult::finalizePostResultsReady(const QHostInfo &info)
{
- if (event->type() == QEvent::MetaCall) {
- Q_ASSERT(slotObj);
- auto metaCallEvent = static_cast<QMetaCallEvent *>(event);
- auto args = metaCallEvent->args();
- // we didn't have a context object, or it's still alive
- if (!withContextObject || receiver)
- slotObj->call(const_cast<QObject*>(receiver.data()), args);
- slotObj->destroyIfLastRef();
-
- deleteLater();
- return true;
+ Q_ASSERT(slotObj);
+
+ // we used to have a context object, but it's already destroyed
+ if (receiver) {
+ void *args[] = { nullptr, const_cast<QHostInfo *>(&info) };
+ slotObj->call(const_cast<QObject *>(receiver.data()), args);
}
- return QObject::event(event);
+
+ deleteLater();
}
/*!
@@ -202,7 +190,7 @@ bool QHostInfoResult::event(QEvent *event)
static int nextId()
{
- static QBasicAtomicInt counter = Q_BASIC_ATOMIC_INITIALIZER(0);
+ Q_CONSTINIT static QBasicAtomicInt counter = Q_BASIC_ATOMIC_INITIALIZER(0);
return 1 + counter.fetchAndAddRelaxed(1);
}
@@ -233,10 +221,17 @@ static int nextId()
\note There is no guarantee on the order the signals will be emitted
if you start multiple requests with lookupHost().
+ \note In Qt versions prior to 6.7, this function took \a receiver as
+ (non-const) \c{QObject*}.
+
\sa abortHostLookup(), addresses(), error(), fromName()
*/
-int QHostInfo::lookupHost(const QString &name, QObject *receiver, const char *member)
+int QHostInfo::lookupHost(const QString &name, const QObject *receiver, const char *member)
{
+ if (!receiver || !member) {
+ qWarning("QHostInfo::lookupHost: both the receiver and the member to invoke must be non-null");
+ return -1;
+ }
return QHostInfo::lookupHostImpl(name, receiver, nullptr, member);
}
@@ -262,7 +257,7 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver, const char *me
*/
/*!
- \fn template<typename Functor> int QHostInfo::lookupHost(const QString &name, Functor functor)
+ \fn template<typename Functor> int QHostInfo::lookupHost(const QString &name, Functor &&functor)
\since 5.9
@@ -734,22 +729,25 @@ QString QHostInfo::localHostName()
\internal
Called by the various lookupHost overloads to perform the lookup.
- Signals either the functor encapuslated in the \a slotObj in the context
+ Signals either the functor encapuslated in the \a slotObjRaw in the context
of \a receiver, or the \a member slot of the \a receiver.
- \a receiver might be the nullptr, but only if a \a slotObj is provided.
+ \a receiver might be the nullptr, but only if a \a slotObjRaw is provided.
*/
int QHostInfo::lookupHostImpl(const QString &name,
const QObject *receiver,
- QtPrivate::QSlotObjectBase *slotObj,
+ QtPrivate::QSlotObjectBase *slotObjRaw,
const char *member)
{
+ QtPrivate::SlotObjUniquePtr slotObj{slotObjRaw};
#if defined QHOSTINFO_DEBUG
qDebug("QHostInfo::lookupHostImpl(\"%s\", %p, %p, %s)",
- name.toLatin1().constData(), receiver, slotObj, member ? member + 1 : 0);
+ name.toLatin1().constData(), receiver, slotObj.get(), member ? member + 1 : 0);
#endif
Q_ASSERT(!member != !slotObj); // one of these must be set, but not both
Q_ASSERT(receiver || slotObj);
+ Q_ASSERT(!member || receiver); // if member is set, also is receiver
+ const bool isUsingStringBasedSlot = static_cast<bool>(member);
if (!QAbstractEventDispatcher::instance(QThread::currentThread())) {
qWarning("QHostInfo::lookupHost() called with no event dispatcher");
@@ -765,10 +763,11 @@ int QHostInfo::lookupHostImpl(const QString &name,
hostInfo.setError(QHostInfo::HostNotFound);
hostInfo.setErrorString(QCoreApplication::translate("QHostInfo", "No host name given"));
- QHostInfoResult result(receiver, slotObj);
- if (receiver && member)
+ QHostInfoResult result(receiver, std::move(slotObj));
+ if (isUsingStringBasedSlot) {
QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)),
receiver, member, Qt::QueuedConnection);
+ }
result.postResultsReady(hostInfo);
return id;
@@ -782,10 +781,11 @@ int QHostInfo::lookupHostImpl(const QString &name,
QHostInfo hostInfo = QHostInfoAgent::lookup(name);
hostInfo.setLookupId(id);
- QHostInfoResult result(receiver, slotObj);
- if (receiver && member)
+ QHostInfoResult result(receiver, std::move(slotObj));
+ if (isUsingStringBasedSlot) {
QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)),
receiver, member, Qt::QueuedConnection);
+ }
result.postResultsReady(hostInfo);
#else
QHostInfoLookupManager *manager = theHostInfoLookupManager();
@@ -798,20 +798,22 @@ int QHostInfo::lookupHostImpl(const QString &name,
QHostInfo info = manager->cache.get(name, &valid);
if (valid) {
info.setLookupId(id);
- QHostInfoResult result(receiver, slotObj);
- if (receiver && member)
+ QHostInfoResult result(receiver, std::move(slotObj));
+ if (isUsingStringBasedSlot) {
QObject::connect(&result, SIGNAL(resultsReady(QHostInfo)),
receiver, member, Qt::QueuedConnection);
+ }
result.postResultsReady(info);
return id;
}
}
// cache is not enabled or it was not in the cache, do normal lookup
- QHostInfoRunnable *runnable = new QHostInfoRunnable(name, id, receiver, slotObj);
- if (receiver && member)
+ QHostInfoRunnable *runnable = new QHostInfoRunnable(name, id, receiver, std::move(slotObj));
+ if (isUsingStringBasedSlot) {
QObject::connect(&runnable->resultEmitter, SIGNAL(resultsReady(QHostInfo)),
receiver, member, Qt::QueuedConnection);
+ }
manager->scheduleLookup(runnable);
}
#endif // Q_OS_WASM
@@ -819,12 +821,15 @@ int QHostInfo::lookupHostImpl(const QString &name,
}
QHostInfoRunnable::QHostInfoRunnable(const QString &hn, int i, const QObject *receiver,
- QtPrivate::QSlotObjectBase *slotObj) :
- toBeLookedUp(hn), id(i), resultEmitter(receiver, slotObj)
+ QtPrivate::SlotObjUniquePtr slotObj)
+ : toBeLookedUp{hn}, id{i}, resultEmitter{receiver, std::move(slotObj)}
{
setAutoDelete(true);
}
+QHostInfoRunnable::~QHostInfoRunnable()
+ = default;
+
// the QHostInfoLookupManager will at some point call this via a QThreadPool
void QHostInfoRunnable::run()
{
@@ -934,7 +939,7 @@ void QHostInfoLookupManager::rescheduleWithMutexHeld()
if (!finishedLookups.isEmpty()) {
// remove ID from aborted if it is in there
- for (int i = 0; i < finishedLookups.length(); i++) {
+ for (int i = 0; i < finishedLookups.size(); i++) {
abortedLookups.removeAll(finishedLookups.at(i)->id);
}
@@ -962,7 +967,7 @@ void QHostInfoLookupManager::rescheduleWithMutexHeld()
isAlreadyRunning).second,
scheduledLookups.end());
- const int availableThreads = threadPool.maxThreadCount() - currentLookups.size();
+ const int availableThreads = std::max(threadPool.maxThreadCount(), 1) - currentLookups.size();
if (availableThreads > 0) {
int readyToStartCount = qMin(availableThreads, scheduledLookups.size());
auto it = scheduledLookups.begin();
@@ -1000,9 +1005,12 @@ void QHostInfoLookupManager::abortLookup(int id)
if (wasDeleted)
return;
+ if (id == -1)
+ return;
+
#if QT_CONFIG(thread)
// is postponed? delete and return
- for (int i = 0; i < postponedLookups.length(); i++) {
+ for (int i = 0; i < postponedLookups.size(); i++) {
if (postponedLookups.at(i)->id == id) {
delete postponedLookups.takeAt(i);
return;
@@ -1011,7 +1019,7 @@ void QHostInfoLookupManager::abortLookup(int id)
#endif
// is scheduled? delete and return
- for (int i = 0; i < scheduledLookups.length(); i++) {
+ for (int i = 0; i < scheduledLookups.size(); i++) {
if (scheduledLookups.at(i)->id == id) {
delete scheduledLookups.takeAt(i);
return;
diff --git a/src/network/kernel/qhostinfo.h b/src/network/kernel/qhostinfo.h
index 6ee8aff081..3942e41498 100644
--- a/src/network/kernel/qhostinfo.h
+++ b/src/network/kernel/qhostinfo.h
@@ -17,6 +17,7 @@ class QHostInfoPrivate;
class Q_NETWORK_EXPORT QHostInfo
{
+ Q_GADGET
public:
enum HostInfoError {
NoError,
@@ -26,7 +27,7 @@ public:
explicit QHostInfo(int lookupId = -1);
QHostInfo(const QHostInfo &d);
- QHostInfo(QHostInfo &&other) noexcept : d_ptr(qExchange(other.d_ptr, nullptr)) {}
+ QHostInfo(QHostInfo &&other) noexcept : d_ptr(std::exchange(other.d_ptr, nullptr)) {}
QHostInfo &operator=(const QHostInfo &d);
QHostInfo &operator=(QHostInfo &&other) noexcept { swap(other); return *this; }
~QHostInfo();
@@ -48,68 +49,42 @@ public:
void setLookupId(int id);
int lookupId() const;
+#if QT_NETWORK_REMOVED_SINCE(6, 7)
static int lookupHost(const QString &name, QObject *receiver, const char *member);
+#endif
+ static int lookupHost(const QString &name, const QObject *receiver, const char *member);
static void abortHostLookup(int lookupId);
static QHostInfo fromName(const QString &name);
static QString localHostName();
static QString localDomainName();
-#ifdef Q_CLANG_QDOC
- template<typename Functor>
- static int lookupHost(const QString &name, Functor functor);
+#ifdef Q_QDOC
template<typename Functor>
static int lookupHost(const QString &name, const QObject *context, Functor functor);
#else
- // lookupHost to a QObject slot
- template <typename Func>
+ // lookupHost to a callable (with context)
+ template <typename Functor>
static inline int lookupHost(const QString &name,
- const typename QtPrivate::FunctionPointer<Func>::Object *receiver,
- Func slot)
+ const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *receiver,
+ Functor &&func)
{
- typedef QtPrivate::FunctionPointer<Func> SlotType;
-
- typedef QtPrivate::FunctionPointer<void (*)(QHostInfo)> SignalType;
- static_assert(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount),
- "The slot requires more arguments than the signal provides.");
- static_assert((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments,
- typename SlotType::Arguments>::value),
- "Signal and slot arguments are not compatible.");
- static_assert((QtPrivate::AreArgumentsCompatible<typename SlotType::ReturnType,
- typename SignalType::ReturnType>::value),
- "Return type of the slot is not compatible "
- "with the return type of the signal.");
-
- auto slotObj = new QtPrivate::QSlotObject<Func, typename SlotType::Arguments, void>(slot);
- return lookupHostImpl(name, receiver, slotObj, nullptr);
+ using Prototype = void(*)(QHostInfo);
+ QtPrivate::AssertCompatibleFunctions<Prototype, Functor>();
+ return lookupHostImpl(name, receiver,
+ QtPrivate::makeCallableObject<Prototype>(std::forward<Functor>(func)),
+ nullptr);
}
+#endif // Q_QDOC
+#ifndef QT_NO_CONTEXTLESS_CONNECT
// lookupHost to a callable (without context)
- template <typename Func>
- static inline typename std::enable_if<!QtPrivate::FunctionPointer<Func>::IsPointerToMemberFunction &&
- !std::is_same<const char *, Func>::value, int>::type
- lookupHost(const QString &name, Func slot)
- {
- return lookupHost(name, nullptr, std::move(slot));
- }
-
- // lookupHost to a functor or function pointer (with context)
- template <typename Func1>
- static inline typename std::enable_if<!QtPrivate::FunctionPointer<Func1>::IsPointerToMemberFunction &&
- !std::is_same<const char*, Func1>::value, int>::type
- lookupHost(const QString &name, QObject *context, Func1 slot)
+ template <typename Functor>
+ static inline int lookupHost(const QString &name, Functor &&slot)
{
- typedef QtPrivate::FunctionPointer<Func1> SlotType;
-
- static_assert(int(SlotType::ArgumentCount) <= 1,
- "The slot must not require more than one argument");
-
- auto slotObj = new QtPrivate::QFunctorSlotObject<Func1, 1,
- typename QtPrivate::List<QHostInfo>,
- void>(std::move(slot));
- return lookupHostImpl(name, context, slotObj, nullptr);
+ return lookupHost(name, nullptr, std::forward<Functor>(slot));
}
-#endif // Q_QDOC
+#endif // QT_NO_CONTEXTLESS_CONNECT
private:
QHostInfoPrivate *d_ptr;
diff --git a/src/network/kernel/qhostinfo_p.h b/src/network/kernel/qhostinfo_p.h
index 22b6ccc017..b229eb1cd8 100644
--- a/src/network/kernel/qhostinfo_p.h
+++ b/src/network/kernel/qhostinfo_p.h
@@ -34,8 +34,6 @@
#include <QElapsedTimer>
#include <QCache>
-#include <QSharedPointer>
-
#include <atomic>
QT_BEGIN_NAMESPACE
@@ -45,26 +43,21 @@ class QHostInfoResult : public QObject
{
Q_OBJECT
public:
- QHostInfoResult(const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj)
- : receiver(receiver), slotObj(slotObj),
- withContextObject(slotObj && receiver)
- {
- if (receiver)
- moveToThread(receiver->thread());
- }
+ explicit QHostInfoResult(const QObject *receiver, QtPrivate::SlotObjUniquePtr slot);
+ ~QHostInfoResult() override;
void postResultsReady(const QHostInfo &info);
Q_SIGNALS:
void resultsReady(const QHostInfo &info);
-protected:
- bool event(QEvent *event) override;
+private Q_SLOTS:
+ void finalizePostResultsReady(const QHostInfo &info);
private:
- QHostInfoResult(const QHostInfoResult *other)
- : receiver(other->receiver), slotObj(other->slotObj),
- withContextObject(other->withContextObject)
+ QHostInfoResult(QHostInfoResult *other)
+ : receiver(other->receiver.get() != other ? other->receiver.get() : this),
+ slotObj{std::move(other->slotObj)}
{
// cleanup if the application terminates before results are delivered
connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit,
@@ -73,9 +66,10 @@ private:
moveToThread(other->thread());
}
+ // receiver is either a QObject provided by the user,
+ // or it's set to `this` (to emulate the behavior of the contextless connect())
QPointer<const QObject> receiver = nullptr;
- QtPrivate::QSlotObjectBase *slotObj = nullptr;
- const bool withContextObject = false;
+ QtPrivate::SlotObjUniquePtr slotObj;
};
class QHostInfoAgent
@@ -143,8 +137,10 @@ private:
class QHostInfoRunnable : public QRunnable
{
public:
- QHostInfoRunnable(const QString &hn, int i, const QObject *receiver,
- QtPrivate::QSlotObjectBase *slotObj);
+ explicit QHostInfoRunnable(const QString &hn, int i, const QObject *receiver,
+ QtPrivate::SlotObjUniquePtr slotObj);
+ ~QHostInfoRunnable() override;
+
void run() override;
QString toBeLookedUp;
diff --git a/src/network/kernel/qhostinfo_unix.cpp b/src/network/kernel/qhostinfo_unix.cpp
index 12d8c04d10..80d386a13d 100644
--- a/src/network/kernel/qhostinfo_unix.cpp
+++ b/src/network/kernel/qhostinfo_unix.cpp
@@ -3,140 +3,78 @@
//#define QHOSTINFO_DEBUG
-#include "qplatformdefs.h"
-
#include "qhostinfo_p.h"
-#include "private/qnativesocketengine_p.h"
-#include "qiodevice.h"
+
#include <qbytearray.h>
-#if QT_CONFIG(library)
-#include <qlibrary.h>
-#endif
-#include <qbasicatomic.h>
-#include <qurl.h>
#include <qfile.h>
-#include <private/qnet_unix_p.h>
-
-#include "QtCore/qapplicationstatic.h"
+#include <qplatformdefs.h>
+#include <qurl.h>
#include <sys/types.h>
#include <netdb.h>
-#include <arpa/inet.h>
-#if defined(Q_OS_VXWORKS)
-# include <hostLib.h>
-#else
-# include <resolv.h>
-#endif
+#include <netinet/in.h>
-#if defined(__GNU_LIBRARY__) && !defined(__UCLIBC__)
-# include <gnu/lib-names.h>
+#if QT_CONFIG(libresolv)
+# include <resolv.h>
#endif
-#if defined(Q_OS_FREEBSD) || QT_CONFIG(dlopen)
-# include <dlfcn.h>
+#ifndef _PATH_RESCONF
+# define _PATH_RESCONF "/etc/resolv.conf"
#endif
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-enum LibResolvFeature {
- NeedResInit,
- NeedResNInit
-};
-
-typedef struct __res_state *res_state_ptr;
-
-typedef int (*res_init_proto)(void);
-static res_init_proto local_res_init = nullptr;
-typedef int (*res_ninit_proto)(res_state_ptr);
-static res_ninit_proto local_res_ninit = nullptr;
-typedef void (*res_nclose_proto)(res_state_ptr);
-static res_nclose_proto local_res_nclose = nullptr;
-static res_state_ptr local_res = nullptr;
-
-#if QT_CONFIG(library) && !defined(Q_OS_QNX)
-namespace {
-struct LibResolv
-{
- enum {
-#ifdef RES_NORELOAD
- // If RES_NORELOAD is defined, then the libc is capable of watching
- // /etc/resolv.conf for changes and reloading as necessary. So accept
- // whatever is configured.
- ReinitNecessary = false
-#else
- ReinitNecessary = true
-#endif
- };
-
- QLibrary lib;
- LibResolv();
- ~LibResolv() { lib.unload(); }
-};
-}
-
-static QFunctionPointer resolveSymbol(QLibrary &lib, const char *sym)
-{
- if (lib.isLoaded())
- return lib.resolve(sym);
-
-#if defined(RTLD_DEFAULT) && (defined(Q_OS_FREEBSD) || QT_CONFIG(dlopen))
- return reinterpret_cast<QFunctionPointer>(dlsym(RTLD_DEFAULT, sym));
-#else
- return nullptr;
-#endif
-}
-
-LibResolv::LibResolv()
+static void maybeRefreshResolver()
{
-#ifdef LIBRESOLV_SO
- lib.setFileName(QStringLiteral(LIBRESOLV_SO));
- if (!lib.load())
+#if defined(RES_NORELOAD)
+ // If RES_NORELOAD is defined, then the libc is capable of watching
+ // /etc/resolv.conf for changes and reloading as necessary. So accept
+ // whatever is configured.
+ return;
+#elif defined(Q_OS_DARWIN)
+ // Apple's libsystem_info.dylib:getaddrinfo() uses the
+ // libsystem_dnssd.dylib to resolve hostnames. Using res_init() has no
+ // effect on it and is thread-unsafe.
+ return;
+#elif defined(Q_OS_FREEBSD)
+ // FreeBSD automatically refreshes:
+ // https://github.com/freebsd/freebsd-src/blob/b3fe5d932264445cbf9a1c4eab01afb6179b499b/lib/libc/resolv/res_state.c#L69
+ return;
+#elif defined(Q_OS_OPENBSD)
+ // OpenBSD automatically refreshes:
+ // https://github.com/ligurio/openbsd-src/blob/b1ce0da17da254cc15b8aff25b3d55d3c7a82cec/lib/libc/asr/asr.c#L367
+ return;
+#elif defined(Q_OS_QNX)
+ // res_init() is not thread-safe; executing it leads to state corruption.
+ // Whether it reloads resolv.conf on its own is unknown.
+ return;
#endif
- {
- lib.setFileName("resolv"_L1);
- lib.load();
- }
-
- // res_ninit is required for localDomainName()
- local_res_ninit = res_ninit_proto(resolveSymbol(lib, "__res_ninit"));
- if (!local_res_ninit)
- local_res_ninit = res_ninit_proto(resolveSymbol(lib, "res_ninit"));
- if (local_res_ninit) {
- // we must now find res_nclose
- local_res_nclose = res_nclose_proto(resolveSymbol(lib, "res_nclose"));
- if (!local_res_nclose)
- local_res_nclose = res_nclose_proto(resolveSymbol(lib, "__res_nclose"));
- if (!local_res_nclose)
- local_res_ninit = nullptr;
- }
-
- if (ReinitNecessary || !local_res_ninit) {
- local_res_init = res_init_proto(resolveSymbol(lib, "__res_init"));
- if (!local_res_init)
- local_res_init = res_init_proto(resolveSymbol(lib, "res_init"));
- if (local_res_init && !local_res_ninit) {
- // if we can't get a thread-safe context, we have to use the global _res state
- local_res = res_state_ptr(resolveSymbol(lib, "_res"));
+#if QT_CONFIG(libresolv)
+ // OSes known or thought to reach here: AIX, NetBSD, Solaris,
+ // Linux with MUSL (though res_init() does nothing and is unnecessary)
+
+ Q_CONSTINIT static QT_STATBUF lastStat = {};
+ Q_CONSTINIT static QBasicMutex mutex = {};
+ if (QT_STATBUF st; QT_STAT(_PATH_RESCONF, &st) == 0) {
+ QMutexLocker locker(&mutex);
+ bool refresh = false;
+ if ((_res.options & RES_INIT) == 0)
+ refresh = true;
+ else if (lastStat.st_ctime != st.st_ctime)
+ refresh = true; // file was updated
+ else if (lastStat.st_dev != st.st_dev || lastStat.st_ino != st.st_ino)
+ refresh = true; // file was replaced
+ if (refresh) {
+ lastStat = st;
+ res_init();
}
}
+#endif
}
-Q_APPLICATION_STATIC(LibResolv, libResolv)
-
-static void resolveLibrary(LibResolvFeature f)
-{
- if (LibResolv::ReinitNecessary || f == NeedResNInit)
- libResolv();
-}
-#else // QT_CONFIG(library) || Q_OS_QNX
-static void resolveLibrary(LibResolvFeature)
-{
-}
-#endif // QT_CONFIG(library) || Q_OS_QNX
-
QHostInfo QHostInfoAgent::fromName(const QString &hostName)
{
QHostInfo results;
@@ -146,12 +84,7 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
hostName.toLatin1().constData());
#endif
- // Load res_init on demand.
- resolveLibrary(NeedResInit);
-
- // If res_init is available, poll it.
- if (local_res_init)
- local_res_init();
+ maybeRefreshResolver();
QHostAddress address;
if (address.setAddress(hostName))
@@ -162,56 +95,49 @@ QHostInfo QHostInfoAgent::fromName(const QString &hostName)
QString QHostInfo::localDomainName()
{
-#if !defined(Q_OS_VXWORKS) && !defined(Q_OS_ANDROID)
- resolveLibrary(NeedResNInit);
- if (local_res_ninit) {
- // using thread-safe version
- res_state_ptr state = res_state_ptr(malloc(sizeof(*state)));
- Q_CHECK_PTR(state);
- memset(state, 0, sizeof(*state));
- local_res_ninit(state);
- QString domainName = QUrl::fromAce(state->defdname);
+#if QT_CONFIG(libresolv)
+ auto domainNameFromRes = [](res_state r) {
+ QString domainName;
+ if (r->defdname[0])
+ domainName = QUrl::fromAce(r->defdname);
if (domainName.isEmpty())
- domainName = QUrl::fromAce(state->dnsrch[0]);
- local_res_nclose(state);
- free(state);
-
+ domainName = QUrl::fromAce(r->dnsrch[0]);
return domainName;
+ };
+ std::remove_pointer_t<res_state> state = {};
+ if (res_ninit(&state) == 0) {
+ // using thread-safe version
+ auto guard = qScopeGuard([&] { res_nclose(&state); });
+ return domainNameFromRes(&state);
}
- if (local_res_init && local_res) {
- // using thread-unsafe version
+ // using thread-unsafe version
+ maybeRefreshResolver();
+ return domainNameFromRes(&_res);
+#endif // !QT_CONFIG(libresolv)
- local_res_init();
- QString domainName = QUrl::fromAce(local_res->defdname);
- if (domainName.isEmpty())
- domainName = QUrl::fromAce(local_res->dnsrch[0]);
- return domainName;
- }
-#endif
// nothing worked, try doing it by ourselves:
QFile resolvconf;
-#if defined(_PATH_RESCONF)
- resolvconf.setFileName(QFile::decodeName(_PATH_RESCONF));
-#else
- resolvconf.setFileName("/etc/resolv.conf"_L1);
-#endif
+ resolvconf.setFileName(_PATH_RESCONF ""_L1);
if (!resolvconf.open(QIODevice::ReadOnly))
return QString(); // failure
QString domainName;
while (!resolvconf.atEnd()) {
- QByteArray line = resolvconf.readLine().trimmed();
- if (line.startsWith("domain "))
- return QUrl::fromAce(line.mid(sizeof "domain " - 1).trimmed());
+ const QByteArray lineArray = resolvconf.readLine();
+ QByteArrayView line = QByteArrayView(lineArray).trimmed();
+ constexpr QByteArrayView domainWithSpace = "domain ";
+ if (line.startsWith(domainWithSpace))
+ return QUrl::fromAce(line.mid(domainWithSpace.size()).trimmed().toByteArray());
// in case there's no "domain" line, fall back to the first "search" entry
- if (domainName.isEmpty() && line.startsWith("search ")) {
- QByteArray searchDomain = line.mid(sizeof "search " - 1).trimmed();
+ constexpr QByteArrayView searchWithSpace = "search ";
+ if (domainName.isEmpty() && line.startsWith(searchWithSpace)) {
+ QByteArrayView searchDomain = line.mid(searchWithSpace.size()).trimmed();
int pos = searchDomain.indexOf(' ');
if (pos != -1)
searchDomain.truncate(pos);
- domainName = QUrl::fromAce(searchDomain);
+ domainName = QUrl::fromAce(searchDomain.toByteArray());
}
}
diff --git a/src/network/kernel/qnetconmonitor_darwin.mm b/src/network/kernel/qnetconmonitor_darwin.mm
index 639e267c05..60b3cd6581 100644
--- a/src/network/kernel/qnetconmonitor_darwin.mm
+++ b/src/network/kernel/qnetconmonitor_darwin.mm
@@ -1,7 +1,7 @@
// Copyright (C) 2019 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-#include "private/qnativesocketengine_p.h"
+#include "private/qnativesocketengine_p_p.h"
#include "private/qnetconmonitor_p.h"
#include "private/qobject_p.h"
diff --git a/src/network/kernel/qnetconmonitor_win.cpp b/src/network/kernel/qnetconmonitor_win.cpp
index df19ed5f29..bf6aff1e46 100644
--- a/src/network/kernel/qnetconmonitor_win.cpp
+++ b/src/network/kernel/qnetconmonitor_win.cpp
@@ -8,13 +8,15 @@
#include <QtCore/quuid.h>
#include <QtCore/qmetaobject.h>
+#include <QtCore/private/qfunctions_win_p.h>
+#include <QtCore/private/qsystemerror_p.h>
+
#include <QtNetwork/qnetworkinterface.h>
#include <objbase.h>
#include <netlistmgr.h>
#include <wrl/client.h>
#include <wrl/wrappers/corewrappers.h>
-#include <comdef.h>
#include <iphlpapi.h>
#include <algorithm>
@@ -26,12 +28,6 @@ QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcNetMon, "qt.network.monitor");
namespace {
-QString errorStringFromHResult(HRESULT hr)
-{
- _com_error error(hr);
- return QString::fromWCharArray(error.ErrorMessage());
-}
-
template<typename T>
bool QueryInterfaceImpl(IUnknown *from, REFIID riid, void **ppvObject)
{
@@ -124,6 +120,8 @@ public:
void setConnectivity(NLM_CONNECTIVITY newConnectivity);
private:
+ QComHelper comHelper;
+
ComPtr<QNetworkConnectionEvents> connectionEvents;
// We can assume we have access to internet/subnet when this class is created because
// connection has already been established to the peer:
@@ -136,7 +134,6 @@ private:
bool sameSubnet = false;
bool isLinkLocal = false;
bool monitoring = false;
- bool comInitFailed = false;
bool remoteIsIPv6 = false;
};
@@ -147,7 +144,7 @@ QNetworkConnectionEvents::QNetworkConnectionEvents(QNetworkConnectionMonitorPriv
IID_INetworkListManager, &networkListManager);
if (FAILED(hr)) {
qCDebug(lcNetMon) << "Could not get a NetworkListManager instance:"
- << errorStringFromHResult(hr);
+ << QSystemError::windowsComString(hr);
return;
}
@@ -159,7 +156,7 @@ QNetworkConnectionEvents::QNetworkConnectionEvents(QNetworkConnectionMonitorPriv
}
if (FAILED(hr)) {
qCDebug(lcNetMon) << "Failed to get connection point for network events:"
- << errorStringFromHResult(hr);
+ << QSystemError::windowsComString(hr);
}
}
@@ -170,11 +167,17 @@ QNetworkConnectionEvents::~QNetworkConnectionEvents()
ComPtr<INetworkConnection> QNetworkConnectionEvents::getNetworkConnectionFromAdapterGuid(QUuid guid)
{
+ if (!networkListManager) {
+ qCDebug(lcNetMon) << "Failed to enumerate network connections:"
+ << "NetworkListManager was not instantiated";
+ return nullptr;
+ }
+
ComPtr<IEnumNetworkConnections> connections;
auto hr = networkListManager->GetNetworkConnections(connections.GetAddressOf());
if (FAILED(hr)) {
qCDebug(lcNetMon) << "Failed to enumerate network connections:"
- << errorStringFromHResult(hr);
+ << QSystemError::windowsComString(hr);
return nullptr;
}
ComPtr<INetworkConnection> connection = nullptr;
@@ -182,7 +185,7 @@ ComPtr<INetworkConnection> QNetworkConnectionEvents::getNetworkConnectionFromAda
hr = connections->Next(1, connection.GetAddressOf(), nullptr);
if (FAILED(hr)) {
qCDebug(lcNetMon) << "Failed to get next network connection in enumeration:"
- << errorStringFromHResult(hr);
+ << QSystemError::windowsComString(hr);
break;
}
if (connection) {
@@ -190,7 +193,7 @@ ComPtr<INetworkConnection> QNetworkConnectionEvents::getNetworkConnectionFromAda
hr = connection->GetAdapterId(&adapterId);
if (FAILED(hr)) {
qCDebug(lcNetMon) << "Failed to get adapter ID from network connection:"
- << errorStringFromHResult(hr);
+ << QSystemError::windowsComString(hr);
continue;
}
if (guid == adapterId)
@@ -255,7 +258,8 @@ bool QNetworkConnectionEvents::setTarget(const QNetworkInterface &iface)
}
auto hr = connection->GetConnectionId(&guid);
if (FAILED(hr)) {
- qCDebug(lcNetMon) << "Failed to get the connection's GUID:" << errorStringFromHResult(hr);
+ qCDebug(lcNetMon) << "Failed to get the connection's GUID:"
+ << QSystemError::windowsComString(hr);
return false;
}
currentConnectionId = guid;
@@ -278,7 +282,7 @@ bool QNetworkConnectionEvents::startMonitoring()
auto hr = connectionPoint->Advise(this, &cookie);
if (FAILED(hr)) {
qCDebug(lcNetMon) << "Failed to subscribe to network connectivity events:"
- << errorStringFromHResult(hr);
+ << QSystemError::windowsComString(hr);
return false;
}
return true;
@@ -289,7 +293,7 @@ bool QNetworkConnectionEvents::stopMonitoring()
auto hr = connectionPoint->Unadvise(cookie);
if (FAILED(hr)) {
qCDebug(lcNetMon) << "Failed to unsubscribe from network connection events:"
- << errorStringFromHResult(hr);
+ << QSystemError::windowsComString(hr);
return false;
}
cookie = 0;
@@ -299,30 +303,25 @@ bool QNetworkConnectionEvents::stopMonitoring()
QNetworkConnectionMonitorPrivate::QNetworkConnectionMonitorPrivate()
{
- auto hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
- if (FAILED(hr)) {
- qCDebug(lcNetMon) << "Failed to initialize COM:" << errorStringFromHResult(hr);
- comInitFailed = true;
+ if (!comHelper.isValid())
return;
- }
connectionEvents = new QNetworkConnectionEvents(this);
}
QNetworkConnectionMonitorPrivate::~QNetworkConnectionMonitorPrivate()
{
- if (comInitFailed)
+ if (!comHelper.isValid())
return;
if (monitoring)
stopMonitoring();
connectionEvents.Reset();
- CoUninitialize();
}
bool QNetworkConnectionMonitorPrivate::setTargets(const QHostAddress &local,
const QHostAddress &remote)
{
- if (comInitFailed)
+ if (!comHelper.isValid())
return false;
QNetworkInterface iface = getInterfaceFromHostAddress(local);
diff --git a/src/network/kernel/qnetworkdatagram.cpp b/src/network/kernel/qnetworkdatagram.cpp
index e9e3a67edf..a97eb25a51 100644
--- a/src/network/kernel/qnetworkdatagram.cpp
+++ b/src/network/kernel/qnetworkdatagram.cpp
@@ -481,7 +481,7 @@ void QNetworkDatagram::makeReply_helper_inplace(const QByteArray &data)
void QNetworkDatagram::destroy(QNetworkDatagramPrivate *d)
{
- Q_ASSUME(d);
+ Q_ASSERT(d);
delete d;
}
diff --git a/src/network/kernel/qnetworkdatagram.h b/src/network/kernel/qnetworkdatagram.h
index 597448a0ea..dcc2f1102f 100644
--- a/src/network/kernel/qnetworkdatagram.h
+++ b/src/network/kernel/qnetworkdatagram.h
@@ -55,7 +55,7 @@ public:
QByteArray data() const;
void setData(const QByteArray &data);
-#if defined(Q_COMPILER_REF_QUALIFIERS) || defined(Q_CLANG_QDOC)
+#if defined(Q_COMPILER_REF_QUALIFIERS) || defined(Q_QDOC)
QNetworkDatagram makeReply(const QByteArray &payload) const &
{ return makeReply_helper(payload); }
QNetworkDatagram makeReply(const QByteArray &payload) &&
diff --git a/src/network/kernel/qnetworkinformation.cpp b/src/network/kernel/qnetworkinformation.cpp
index d48784f1cd..10d6b89e2c 100644
--- a/src/network/kernel/qnetworkinformation.cpp
+++ b/src/network/kernel/qnetworkinformation.cpp
@@ -26,7 +26,7 @@ struct QNetworkInformationDeleter
void operator()(QNetworkInformation *information) { delete information; }
};
-Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, qniLoader,
(QNetworkInformationBackendFactory_iid,
QStringLiteral("/networkinformation")))
@@ -47,14 +47,21 @@ static void networkInfoCleanup()
if (!instance)
return;
- auto needsReinvoke = instance->thread() && instance->thread() != QThread::currentThread();
- if (needsReinvoke) {
- QMetaObject::invokeMethod(dataHolder->instanceHolder.get(), []() { networkInfoCleanup(); });
- return;
- }
dataHolder->instanceHolder.reset();
}
+using namespace Qt::Literals::StringLiterals;
+
+class QNetworkInformationDummyBackend : public QNetworkInformationBackend {
+ Q_OBJECT
+public:
+ QString name() const override { return u"dummy"_s; }
+ QNetworkInformation::Features featuresSupported() const override
+ {
+ return {};
+ }
+};
+
class QNetworkInformationPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QNetworkInformation)
@@ -65,6 +72,7 @@ public:
static QNetworkInformation *create(QNetworkInformation::Features features);
static QNetworkInformation *create(QStringView name);
+ static QNetworkInformation *createDummy();
static QNetworkInformation *instance()
{
if (!dataHolder())
@@ -84,7 +92,7 @@ private:
bool QNetworkInformationPrivate::initializeList()
{
- if (!loader())
+ if (!qniLoader())
return false;
if (!dataHolder())
return false;
@@ -92,11 +100,11 @@ bool QNetworkInformationPrivate::initializeList()
QMutexLocker initLocker(&mutex);
#if QT_CONFIG(library)
- loader->update();
+ qniLoader->update();
#endif
// Instantiates the plugins (and registers the factories)
int index = 0;
- while (loader->instance(index))
+ while (qniLoader->instance(index))
++index;
initLocker.unlock();
@@ -187,8 +195,8 @@ QNetworkInformation *QNetworkInformationPrivate::create(QStringView name)
} else {
QString listNames;
listNames.reserve(8 * dataHolder->factories.count());
- for (const auto *factory : qAsConst(dataHolder->factories))
- listNames += factory->name() + QStringLiteral(", ");
+ for (const auto *factory : std::as_const(dataHolder->factories))
+ listNames += factory->name() + ", "_L1;
listNames.chop(2);
qDebug().nospace() << "Couldn't find " << name << " in list with names: { "
<< listNames << " }";
@@ -230,7 +238,7 @@ QNetworkInformation *QNetworkInformationPrivate::create(QNetworkInformation::Fea
return dataHolder->instanceHolder.get();
const auto supportsRequestedFeatures = [features](QNetworkInformationBackendFactory *factory) {
- return factory && (factory->featuresSupported() & features) == features;
+ return factory && factory->featuresSupported().testFlags(features);
};
for (auto it = dataHolder->factories.cbegin(), end = dataHolder->factories.cend(); it != end;
@@ -243,7 +251,7 @@ QNetworkInformation *QNetworkInformationPrivate::create(QNetworkInformation::Fea
} else {
QStringList names;
names.reserve(dataHolder->factories.count());
- for (const auto *factory : qAsConst(dataHolder->factories))
+ for (const auto *factory : std::as_const(dataHolder->factories))
names += factory->name();
qDebug() << "None of the following backends has all the requested features:"
<< names << features;
@@ -271,6 +279,20 @@ QNetworkInformation *QNetworkInformationPrivate::create(QNetworkInformation::Fea
return nullptr;
}
+QNetworkInformation *QNetworkInformationPrivate::createDummy()
+{
+ if (!dataHolder())
+ return nullptr;
+
+ QMutexLocker locker(&dataHolder->instanceMutex);
+ if (dataHolder->instanceHolder)
+ return dataHolder->instanceHolder.get();
+
+ QNetworkInformationBackend *backend = new QNetworkInformationDummyBackend;
+ dataHolder->instanceHolder.reset(new QNetworkInformation(backend));
+ return dataHolder->instanceHolder.get();
+}
+
/*!
\class QNetworkInformationBackend
\internal (Semi-private)
@@ -492,6 +514,14 @@ QNetworkInformation::QNetworkInformation(QNetworkInformationBackend *backend)
&QNetworkInformation::transportMediumChanged);
connect(backend, &QNetworkInformationBackend::isMeteredChanged, this,
&QNetworkInformation::isMeteredChanged);
+
+ QThread *main = nullptr;
+
+ if (QCoreApplication::instance())
+ main = QCoreApplication::instance()->thread();
+
+ if (main && thread() != main)
+ moveToThread(main);
}
/*!
@@ -599,6 +629,12 @@ QNetworkInformation::Features QNetworkInformation::supportedFeatures() const
Attempts to load the platform-default backend.
+ \note Starting with 6.7 this tries to load any backend that supports
+ \l{QNetworkInformation::Feature::Reachability}{Reachability} if the
+ platform-default backend is not available or fails to load.
+ If this also fails it will fall back to a backend that only returns
+ the default values for all properties.
+
This platform-to-plugin mapping is as follows:
\table
@@ -619,12 +655,14 @@ QNetworkInformation::Features QNetworkInformation::supportedFeatures() const
\li networkmanager
\endtable
- This function is provided for convenience where the default for a given
- platform is good enough. If you are not using the default plugins you must
- use one of the other load() overloads.
+ This function is provided for convenience where the logic earlier
+ is good enough. If you require a specific plugin then you should call
+ loadBackendByName() or loadBackendByFeatures() directly instead.
- Returns \c true if it managed to load the backend or if it was already
- loaded. Returns \c false otherwise.
+ Determines a suitable backend to load and returns \c true if this backend
+ is already loaded or on successful loading of it. Returns \c false if any
+ other backend has already been loaded, or if loading of the selected
+ backend fails.
\sa instance(), load()
*/
@@ -640,9 +678,15 @@ bool QNetworkInformation::loadDefaultBackend()
#elif defined(Q_OS_LINUX)
index = QNetworkInformationBackend::PluginNamesLinuxIndex;
#endif
- if (index == -1)
- return false;
- return loadBackendByName(QNetworkInformationBackend::PluginNames[index]);
+ if (index != -1 && loadBackendByName(QNetworkInformationBackend::PluginNames[index]))
+ return true;
+ // We assume reachability is the most commonly wanted feature, and try to
+ // load the backend that advertises the most features including that:
+ if (loadBackendByFeatures(Feature::Reachability))
+ return true;
+
+ // Fall back to the dummy backend
+ return loadBackendByName(u"dummy");
}
/*!
@@ -658,6 +702,9 @@ bool QNetworkInformation::loadDefaultBackend()
*/
bool QNetworkInformation::loadBackendByName(QStringView backend)
{
+ if (backend == u"dummy")
+ return QNetworkInformationPrivate::createDummy() != nullptr;
+
auto loadedBackend = QNetworkInformationPrivate::create(backend);
return loadedBackend && loadedBackend->backendName().compare(backend, Qt::CaseInsensitive) == 0;
}
@@ -724,3 +771,4 @@ QT_END_NAMESPACE
#include "moc_qnetworkinformation.cpp"
#include "moc_qnetworkinformation_p.cpp"
+#include "qnetworkinformation.moc"
diff --git a/src/network/kernel/qnetworkinformation.h b/src/network/kernel/qnetworkinformation.h
index 818da98aad..4e70a7faf2 100644
--- a/src/network/kernel/qnetworkinformation.h
+++ b/src/network/kernel/qnetworkinformation.h
@@ -23,6 +23,7 @@ class Q_NETWORK_EXPORT QNetworkInformation : public QObject
NOTIFY isBehindCaptivePortalChanged)
Q_PROPERTY(TransportMedium transportMedium READ transportMedium NOTIFY transportMediumChanged)
Q_PROPERTY(bool isMetered READ isMetered NOTIFY isMeteredChanged)
+ Q_CLASSINFO("RegisterEnumClassesUnscoped", "false")
public:
enum class Reachability {
Unknown,
diff --git a/src/network/kernel/qnetworkinformation_p.h b/src/network/kernel/qnetworkinformation_p.h
index 40c12391c5..504955a6e1 100644
--- a/src/network/kernel/qnetworkinformation_p.h
+++ b/src/network/kernel/qnetworkinformation_p.h
@@ -20,6 +20,7 @@
#include <QtNetwork/qnetworkinformation.h>
#include <QtCore/qloggingcategory.h>
+#include <QtCore/qreadwritelock.h>
QT_BEGIN_NAMESPACE
@@ -48,10 +49,29 @@ public:
virtual QString name() const = 0;
virtual QNetworkInformation::Features featuresSupported() const = 0;
- Reachability reachability() const { return m_reachability; }
- bool behindCaptivePortal() const { return m_behindCaptivePortal; }
- TransportMedium transportMedium() const { return m_transportMedium; }
- bool isMetered() const { return m_metered; }
+ Reachability reachability() const
+ {
+ QReadLocker locker(&m_lock);
+ return m_reachability;
+ }
+
+ bool behindCaptivePortal() const
+ {
+ QReadLocker locker(&m_lock);
+ return m_behindCaptivePortal;
+ }
+
+ TransportMedium transportMedium() const
+ {
+ QReadLocker locker(&m_lock);
+ return m_transportMedium;
+ }
+
+ bool isMetered() const
+ {
+ QReadLocker locker(&m_lock);
+ return m_metered;
+ }
Q_SIGNALS:
void reachabilityChanged(Reachability reachability);
@@ -62,37 +82,46 @@ Q_SIGNALS:
protected:
void setReachability(QNetworkInformation::Reachability reachability)
{
+ QWriteLocker locker(&m_lock);
if (m_reachability != reachability) {
m_reachability = reachability;
+ locker.unlock();
emit reachabilityChanged(reachability);
}
}
void setBehindCaptivePortal(bool behindPortal)
{
+ QWriteLocker locker(&m_lock);
if (m_behindCaptivePortal != behindPortal) {
m_behindCaptivePortal = behindPortal;
+ locker.unlock();
emit behindCaptivePortalChanged(behindPortal);
}
}
void setTransportMedium(TransportMedium medium)
{
+ QWriteLocker locker(&m_lock);
if (m_transportMedium != medium) {
m_transportMedium = medium;
+ locker.unlock();
emit transportMediumChanged(medium);
}
}
void setMetered(bool isMetered)
{
+ QWriteLocker locker(&m_lock);
if (m_metered != isMetered) {
m_metered = isMetered;
+ locker.unlock();
emit isMeteredChanged(isMetered);
}
}
private:
+ mutable QReadWriteLock m_lock;
Reachability m_reachability = Reachability::Unknown;
TransportMedium m_transportMedium = TransportMedium::Unknown;
bool m_behindCaptivePortal = false;
diff --git a/src/network/kernel/qnetworkinterface.cpp b/src/network/kernel/qnetworkinterface.cpp
index 8277f57a55..f03e85c7f6 100644
--- a/src/network/kernel/qnetworkinterface.cpp
+++ b/src/network/kernel/qnetworkinterface.cpp
@@ -873,7 +873,7 @@ QList<QHostAddress> QNetworkInterface::allAddresses()
if ((p->flags & QNetworkInterface::IsUp) == 0)
continue;
- for (const QNetworkAddressEntry &entry : qAsConst(p->addressEntries))
+ for (const QNetworkAddressEntry &entry : std::as_const(p->addressEntries))
result += entry.ip();
}
diff --git a/src/network/kernel/qnetworkinterface_linux.cpp b/src/network/kernel/qnetworkinterface_linux.cpp
index 4c92e01c23..67ed8006bb 100644
--- a/src/network/kernel/qnetworkinterface_linux.cpp
+++ b/src/network/kernel/qnetworkinterface_linux.cpp
@@ -111,7 +111,10 @@ struct NetlinkSocket
template <typename Lambda> struct ProcessNetlinkRequest
{
using FunctionTraits = QtPrivate::FunctionPointer<decltype(&Lambda::operator())>;
- using FirstArgument = typename FunctionTraits::Arguments::Car;
+ using FirstArgumentPointer = typename FunctionTraits::Arguments::Car;
+ using FirstArgument = std::remove_pointer_t<FirstArgumentPointer>;
+ static_assert(std::is_pointer_v<FirstArgumentPointer>);
+ static_assert(std::is_aggregate_v<FirstArgument>);
static int expectedTypeForRequest(int rtype)
{
@@ -136,7 +139,7 @@ template <typename Lambda> struct ProcessNetlinkRequest
if (!NLMSG_OK(hdr, quint32(len)))
return;
- auto arg = reinterpret_cast<FirstArgument>(NLMSG_DATA(hdr));
+ auto arg = static_cast<FirstArgument *>(NLMSG_DATA(hdr));
size_t payloadLen = NLMSG_PAYLOAD(hdr, 0);
// is this a multipart message?
@@ -156,7 +159,7 @@ template <typename Lambda> struct ProcessNetlinkRequest
// NLMSG_NEXT also updates the len variable
hdr = NLMSG_NEXT(hdr, len);
- arg = reinterpret_cast<FirstArgument>(NLMSG_DATA(hdr));
+ arg = static_cast<FirstArgument *>(NLMSG_DATA(hdr));
payloadLen = NLMSG_PAYLOAD(hdr, 0);
} while (NLMSG_OK(hdr, quint32(len)));
@@ -186,7 +189,7 @@ void processNetlinkRequest(int sock, struct nlmsghdr *hdr, char *buf, size_t buf
uint QNetworkInterfaceManager::interfaceIndexFromName(const QString &name)
{
uint index = 0;
- if (name.length() >= IFNAMSIZ)
+ if (name.size() >= IFNAMSIZ)
return index;
int socket = qt_safe_socket(AF_INET, SOCK_DGRAM, 0);
@@ -309,7 +312,7 @@ static void getAddresses(int sock, char *buf, QList<QNetworkInterfacePrivate *>
// find the interface this is relevant to
QNetworkInterfacePrivate *iface = nullptr;
- for (auto candidate : qAsConst(result)) {
+ for (auto candidate : std::as_const(result)) {
if (candidate->index != int(ifa->ifa_index))
continue;
iface = candidate;
diff --git a/src/network/kernel/qnetworkinterface_unix.cpp b/src/network/kernel/qnetworkinterface_unix.cpp
index 51a266b2e3..c0a7d9e00d 100644
--- a/src/network/kernel/qnetworkinterface_unix.cpp
+++ b/src/network/kernel/qnetworkinterface_unix.cpp
@@ -17,11 +17,7 @@
# include "qdatetime.h"
#endif
-#if defined(QT_LINUXBASE)
-# define QT_NO_GETIFADDRS
-#endif
-
-#ifndef QT_NO_GETIFADDRS
+#if QT_CONFIG(getifaddrs)
# include <ifaddrs.h>
#endif
@@ -56,30 +52,38 @@ static QHostAddress addressFromSockaddr(sockaddr *sa, int ifindex = 0, const QSt
}
}
return address;
+}
+
+template <typename Req> [[maybe_unused]]
+static auto &ifreq_index(Req &req, std::enable_if_t<sizeof(std::declval<Req>().ifr_index) != 0, int> = 0)
+{
+ return req.ifr_index;
+}
+template <typename Req> [[maybe_unused]]
+static auto &ifreq_index(Req &req, std::enable_if_t<sizeof(std::declval<Req>().ifr_ifindex) != 0, int> = 0)
+{
+ return req.ifr_ifindex;
}
uint QNetworkInterfaceManager::interfaceIndexFromName(const QString &name)
{
-#ifndef QT_NO_IPV6IFNAME
- return ::if_nametoindex(name.toLatin1());
+#if QT_CONFIG(ipv6ifname)
+ return ::if_nametoindex(name.toLatin1().constData());
#elif defined(SIOCGIFINDEX)
struct ifreq req;
int socket = qt_safe_socket(AF_INET, SOCK_STREAM, 0);
if (socket < 0)
return 0;
- QByteArray name8bit = name.toLatin1();
+ const QByteArray name8bit = name.toLatin1();
memset(&req, 0, sizeof(ifreq));
- memcpy(req.ifr_name, name8bit, qMin<int>(name8bit.length() + 1, sizeof(req.ifr_name) - 1));
+ if (!name8bit.isNull())
+ memcpy(req.ifr_name, name8bit.data(), qMin(size_t(name8bit.length()) + 1, sizeof(req.ifr_name) - 1));
uint id = 0;
if (qt_safe_ioctl(socket, SIOCGIFINDEX, &req) >= 0)
-# if QT_CONFIG(ifr_index)
- id = req.ifr_index;
-# else
- id = req.ifr_ifindex;
-# endif
+ id = ifreq_index(req);
qt_safe_close(socket);
return id;
#else
@@ -90,7 +94,7 @@ uint QNetworkInterfaceManager::interfaceIndexFromName(const QString &name)
QString QNetworkInterfaceManager::interfaceNameFromIndex(uint index)
{
-#ifndef QT_NO_IPV6IFNAME
+#if QT_CONFIG(ipv6ifname)
char buf[IF_NAMESIZE];
if (::if_indextoname(index, buf))
return QString::fromLatin1(buf);
@@ -99,12 +103,7 @@ QString QNetworkInterfaceManager::interfaceNameFromIndex(uint index)
int socket = qt_safe_socket(AF_INET, SOCK_STREAM, 0);
if (socket >= 0) {
memset(&req, 0, sizeof(ifreq));
-# if QT_CONFIG(ifr_index)
- req.ifr_index = index;
-# else
- req.ifr_ifindex = index;
-# endif
-
+ ifreq_index(req) = index;
if (qt_safe_ioctl(socket, SIOCGIFNAME, &req) >= 0) {
qt_safe_close(socket);
return QString::fromLatin1(req.ifr_name);
@@ -124,13 +123,13 @@ static int getMtu(int socket, struct ifreq *req)
return 0;
}
-#ifdef QT_NO_GETIFADDRS
+#if !QT_CONFIG(getifaddrs)
// getifaddrs not available
static QSet<QByteArray> interfaceNames(int socket)
{
QSet<QByteArray> result;
-#ifdef QT_NO_IPV6IFNAME
+#if !QT_CONFIG(ipv6ifname)
QByteArray storageBuffer;
struct ifconf interfaceList;
static const int STORAGEBUFFER_GROWTH = 256;
@@ -185,15 +184,11 @@ static QNetworkInterfacePrivate *findInterface(int socket, QList<QNetworkInterfa
QNetworkInterfacePrivate *iface = nullptr;
int ifindex = 0;
-#if !defined(QT_NO_IPV6IFNAME) || defined(SIOCGIFINDEX)
+#if QT_CONFIG(ipv6ifname) || defined(SIOCGIFINDEX)
// Get the interface index
# ifdef SIOCGIFINDEX
if (qt_safe_ioctl(socket, SIOCGIFINDEX, &req) >= 0)
-# if QT_CONFIG(ifr_index)
- ifindex = req.ifr_index;
-# else
- ifindex = req.ifr_ifindex;
-# endif
+ ifindex = ifreq_index(req);
# else
ifindex = if_nametoindex(req.ifr_name);
# endif
@@ -241,7 +236,8 @@ static QList<QNetworkInterfacePrivate *> interfaceListing()
for ( ; it != names.constEnd(); ++it) {
ifreq req;
memset(&req, 0, sizeof(ifreq));
- memcpy(req.ifr_name, *it, qMin<int>(it->length() + 1, sizeof(req.ifr_name) - 1));
+ if (!it->isNull())
+ memcpy(req.ifr_name, it->constData(), qMin(size_t(it->length()) + 1, sizeof(req.ifr_name) - 1));
QNetworkInterfacePrivate *iface = findInterface(socket, interfaces, req);
@@ -252,7 +248,8 @@ static QList<QNetworkInterfacePrivate *> interfaceListing()
iface->name = QString::fromLatin1(req.ifr_name);
// reset the name:
- memcpy(req.ifr_name, oldName, qMin<int>(oldName.length() + 1, sizeof(req.ifr_name) - 1));
+ if (!oldName.isNull())
+ memcpy(req.ifr_name, oldName.constData(), qMin(size_t(oldName.length()) + 1, sizeof(req.ifr_name) - 1));
} else
#endif
{
@@ -459,7 +456,8 @@ static QList<QNetworkInterfacePrivate *> createInterfaces(ifaddrs *rawList)
// ensure both structs start with the name field, of size IFNAMESIZ
static_assert(sizeof(mediareq.ifm_name) == sizeof(req.ifr_name));
- Q_ASSERT(&mediareq.ifm_name == &req.ifr_name);
+ static_assert(offsetof(struct ifmediareq, ifm_name) == 0);
+ static_assert(offsetof(struct ifreq, ifr_name) == 0);
// on NetBSD we use AF_LINK and sockaddr_dl
// scan the list for that family
diff --git a/src/network/kernel/qnetworkproxy.cpp b/src/network/kernel/qnetworkproxy.cpp
index a00b0031ac..62bba24bce 100644
--- a/src/network/kernel/qnetworkproxy.cpp
+++ b/src/network/kernel/qnetworkproxy.cpp
@@ -1466,7 +1466,7 @@ void QNetworkProxyFactory::setApplicationProxyFactory(QNetworkProxyFactory *fact
Internet Explorer's settings and use them.
On \macos, this function will obtain the proxy settings using the
- SystemConfiguration framework from Apple. It will apply the FTP,
+ CFNetwork framework from Apple. It will apply the FTP,
HTTP and HTTPS proxy configurations for queries that contain the
protocol tag "ftp", "http" and "https", respectively. If the SOCKS
proxy is enabled in that configuration, this function will use the
@@ -1489,9 +1489,6 @@ void QNetworkProxyFactory::setApplicationProxyFactory(QNetworkProxyFactory *fact
listed here.
\list
- \li On \macos, this function will ignore the Proxy Auto Configuration
- settings, since it cannot execute the associated ECMAScript code.
-
\li On Windows platforms, this function may take several seconds to
execute depending on the configuration of the user's system.
\endlist
diff --git a/src/network/kernel/qnetworkproxy.h b/src/network/kernel/qnetworkproxy.h
index c39fff5318..d04bd9ee13 100644
--- a/src/network/kernel/qnetworkproxy.h
+++ b/src/network/kernel/qnetworkproxy.h
@@ -77,6 +77,7 @@ class QNetworkProxyPrivate;
class Q_NETWORK_EXPORT QNetworkProxy
{
+ Q_GADGET
public:
enum ProxyType {
DefaultProxy,
diff --git a/src/network/kernel/qnetworkproxy_android.cpp b/src/network/kernel/qnetworkproxy_android.cpp
index 2e1fb50234..3d37266b70 100644
--- a/src/network/kernel/qnetworkproxy_android.cpp
+++ b/src/network/kernel/qnetworkproxy_android.cpp
@@ -24,7 +24,7 @@ Q_GLOBAL_STATIC(ProxyInfoObject, proxyInfoInstance)
static const char networkClass[] = "org/qtproject/qt/android/network/QtNetwork";
-Q_DECLARE_JNI_TYPE(ProxyInfo, "Landroid/net/ProxyInfo;")
+Q_DECLARE_JNI_CLASS(ProxyInfo, "android/net/ProxyInfo")
Q_DECLARE_JNI_TYPE(JStringArray, "[Ljava/lang/String;")
ProxyInfoObject::ProxyInfoObject()
diff --git a/src/network/kernel/qnetworkproxy_mac.cpp b/src/network/kernel/qnetworkproxy_darwin.cpp
index aa691090cc..d2bd4958dd 100644
--- a/src/network/kernel/qnetworkproxy_mac.cpp
+++ b/src/network/kernel/qnetworkproxy_darwin.cpp
@@ -14,6 +14,7 @@
#include <QtCore/QUrl>
#include <QtCore/qendian.h>
#include <QtCore/qstringlist.h>
+#include <QtCore/qsystemdetection.h>
#include "private/qcore_mac_p.h"
/*
@@ -32,11 +33,11 @@
* \li Bypass list (by default: *.local, 169.254/16)
* \endlist
*
- * The matching configuration can be obtained by calling SCDynamicStoreCopyProxies
- * (from <SystemConfiguration/SCDynamicStoreCopySpecific.h>). See
+ * The matching configuration can be obtained by calling CFNetworkCopySystemProxySettings()
+ * (from <CFNetwork/CFProxySupport.h>). See
* Apple's documentation:
*
- * http://developer.apple.com/DOCUMENTATION/Networking/Reference/SysConfig/SCDynamicStoreCopySpecific/CompositePage.html#//apple_ref/c/func/SCDynamicStoreCopyProxies
+ * https://developer.apple.com/documentation/cfnetwork/1426754-cfnetworkcopysystemproxysettings?language=objc
*
*/
@@ -46,13 +47,19 @@ using namespace Qt::StringLiterals;
static bool isHostExcluded(CFDictionaryRef dict, const QString &host)
{
+ Q_ASSERT(dict);
+
if (host.isEmpty())
return true;
+#ifndef Q_OS_IOS
+ // On iOS all those keys are not available, and worse so - entries
+ // for HTTPS are not in the dictionary, but instead in some nested dictionary
+ // with undocumented keys/object types.
bool isSimple = !host.contains(u'.') && !host.contains(u':');
CFNumberRef excludeSimples;
if (isSimple &&
- (excludeSimples = (CFNumberRef)CFDictionaryGetValue(dict, kSCPropNetProxiesExcludeSimpleHostnames))) {
+ (excludeSimples = (CFNumberRef)CFDictionaryGetValue(dict, kCFNetworkProxiesExcludeSimpleHostnames))) {
int enabled;
if (CFNumberGetValue(excludeSimples, kCFNumberIntType, &enabled) && enabled)
return true;
@@ -63,7 +70,7 @@ static bool isHostExcluded(CFDictionaryRef dict, const QString &host)
// not a simple host name
// does it match the list of exclusions?
- CFArrayRef exclusionList = (CFArrayRef)CFDictionaryGetValue(dict, kSCPropNetProxiesExceptionsList);
+ CFArrayRef exclusionList = (CFArrayRef)CFDictionaryGetValue(dict, kCFNetworkProxiesExceptionsList);
if (!exclusionList)
return false;
@@ -81,7 +88,9 @@ static bool isHostExcluded(CFDictionaryRef dict, const QString &host)
return true;
}
}
-
+#else
+ Q_UNUSED(dict);
+#endif // Q_OS_IOS
// host was not excluded
return false;
}
@@ -112,7 +121,6 @@ static QNetworkProxy proxyFromDictionary(CFDictionaryRef dict, QNetworkProxy::Pr
return QNetworkProxy();
}
-
static QNetworkProxy proxyFromDictionary(CFDictionaryRef dict)
{
QNetworkProxy::ProxyType proxyType = QNetworkProxy::DefaultProxy;
@@ -178,16 +186,43 @@ QCFType<CFStringRef> stringByAddingPercentEscapes(CFStringRef originalPath)
return escaped.toCFString();
}
-} // anon namespace
+#ifdef Q_OS_IOS
+QList<QNetworkProxy> proxiesForQueryUrl(CFDictionaryRef dict, const QUrl &url)
+{
+ Q_ASSERT(dict);
+
+ const QCFType<CFURLRef> cfUrl = url.toCFURL();
+ const QCFType<CFArrayRef> proxies = CFNetworkCopyProxiesForURL(cfUrl, dict);
+ Q_ASSERT(proxies);
+
+ QList<QNetworkProxy> result;
+ const auto count = CFArrayGetCount(proxies);
+ if (!count) // Could be no proper proxy or host excluded.
+ return result;
+
+ for (CFIndex i = 0; i < count; ++i) {
+ const void *obj = CFArrayGetValueAtIndex(proxies, i);
+ if (CFGetTypeID(obj) != CFDictionaryGetTypeID())
+ continue;
+ const QNetworkProxy proxy = proxyFromDictionary(static_cast<CFDictionaryRef>(obj));
+ if (proxy.type() == QNetworkProxy::NoProxy || proxy.type() == QNetworkProxy::DefaultProxy)
+ continue;
+ result << proxy;
+ }
+
+ return result;
+}
+#endif // Q_OS_IOS
+} // unnamed namespace.
QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query)
{
QList<QNetworkProxy> result;
// obtain a dictionary to the proxy settings:
- const QCFType<CFDictionaryRef> dict = SCDynamicStoreCopyProxies(NULL);
+ const QCFType<CFDictionaryRef> dict = CFNetworkCopySystemProxySettings();
if (!dict) {
- qWarning("QNetworkProxyFactory::systemProxyForQuery: SCDynamicStoreCopyProxies returned NULL");
+ qWarning("QNetworkProxyFactory::systemProxyForQuery: CFNetworkCopySystemProxySettings returned nullptr");
return result; // failed
}
@@ -196,13 +231,13 @@ QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query)
// is there a PAC enabled? If so, use it first.
CFNumberRef pacEnabled;
- if ((pacEnabled = (CFNumberRef)CFDictionaryGetValue(dict, kSCPropNetProxiesProxyAutoConfigEnable))) {
+ if ((pacEnabled = (CFNumberRef)CFDictionaryGetValue(dict, kCFNetworkProxiesProxyAutoConfigEnable))) {
int enabled;
if (CFNumberGetValue(pacEnabled, kCFNumberIntType, &enabled) && enabled) {
// PAC is enabled
// kSCPropNetProxiesProxyAutoConfigURLString returns the URL string
// as entered in the system proxy configuration dialog
- CFStringRef pacLocationSetting = (CFStringRef)CFDictionaryGetValue(dict, kSCPropNetProxiesProxyAutoConfigURLString);
+ CFStringRef pacLocationSetting = (CFStringRef)CFDictionaryGetValue(dict, kCFNetworkProxiesProxyAutoConfigURLString);
auto cfPacLocation = stringByAddingPercentEscapes(pacLocationSetting);
QCFType<CFDataRef> pacData;
QCFType<CFURLRef> pacUrl = CFURLCreateWithString(kCFAllocatorDefault, cfPacLocation, NULL);
@@ -252,53 +287,77 @@ QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query)
}
}
- // no PAC, decide which proxy we're looking for based on the query
- bool isHttps = false;
- QString protocol = query.protocolTag().toLower();
-
+ // No PAC, decide which proxy we're looking for based on the query
// try the protocol-specific proxy
+ const QString protocol = query.protocolTag();
QNetworkProxy protocolSpecificProxy;
- if (protocol == "ftp"_L1) {
- protocolSpecificProxy =
- proxyFromDictionary(dict, QNetworkProxy::FtpCachingProxy,
- kSCPropNetProxiesFTPEnable,
- kSCPropNetProxiesFTPProxy,
- kSCPropNetProxiesFTPPort);
- } else if (protocol == "http"_L1) {
+ if (protocol.compare("http"_L1, Qt::CaseInsensitive) == 0) {
protocolSpecificProxy =
proxyFromDictionary(dict, QNetworkProxy::HttpProxy,
- kSCPropNetProxiesHTTPEnable,
- kSCPropNetProxiesHTTPProxy,
- kSCPropNetProxiesHTTPPort);
- } else if (protocol == "https"_L1) {
+ kCFNetworkProxiesHTTPEnable,
+ kCFNetworkProxiesHTTPProxy,
+ kCFNetworkProxiesHTTPPort);
+ }
+
+
+#ifdef Q_OS_IOS
+ if (protocolSpecificProxy.type() != QNetworkProxy::DefaultProxy
+ && protocolSpecificProxy.type() != QNetworkProxy::DefaultProxy) {
+ // HTTP proxy is enabled (on iOS there is no separate HTTPS, though
+ // 'dict' contains deeply buried entries which are the same as HTTP.
+ result << protocolSpecificProxy;
+ }
+
+ // TODO: check query.queryType()? It's possible, the exclude list
+ // did exclude it but above we added a proxy because HTTP proxy
+ // is found. We'll deal with such a situation later, since now NMI.
+ const auto proxiesForUrl = proxiesForQueryUrl(dict, query.url());
+ for (const auto &proxy : proxiesForUrl) {
+ if (!result.contains(proxy))
+ result << proxy;
+ }
+#else
+ bool isHttps = false;
+ if (protocol.compare("ftp"_L1, Qt::CaseInsensitive) == 0) {
+ protocolSpecificProxy =
+ proxyFromDictionary(dict, QNetworkProxy::FtpCachingProxy,
+ kCFNetworkProxiesFTPEnable,
+ kCFNetworkProxiesFTPProxy,
+ kCFNetworkProxiesFTPPort);
+ } else if (protocol.compare("https"_L1, Qt::CaseInsensitive) == 0) {
isHttps = true;
protocolSpecificProxy =
proxyFromDictionary(dict, QNetworkProxy::HttpProxy,
- kSCPropNetProxiesHTTPSEnable,
- kSCPropNetProxiesHTTPSProxy,
- kSCPropNetProxiesHTTPSPort);
+ kCFNetworkProxiesHTTPSEnable,
+ kCFNetworkProxiesHTTPSProxy,
+ kCFNetworkProxiesHTTPSPort);
}
+
if (protocolSpecificProxy.type() != QNetworkProxy::DefaultProxy)
result << protocolSpecificProxy;
// let's add SOCKSv5 if present too
QNetworkProxy socks5 = proxyFromDictionary(dict, QNetworkProxy::Socks5Proxy,
- kSCPropNetProxiesSOCKSEnable,
- kSCPropNetProxiesSOCKSProxy,
- kSCPropNetProxiesSOCKSPort);
+ kCFNetworkProxiesSOCKSEnable,
+ kCFNetworkProxiesSOCKSProxy,
+ kCFNetworkProxiesSOCKSPort);
if (socks5.type() != QNetworkProxy::DefaultProxy)
result << socks5;
// let's add the HTTPS proxy if present (and if we haven't added
// yet)
if (!isHttps) {
- QNetworkProxy https = proxyFromDictionary(dict, QNetworkProxy::HttpProxy,
- kSCPropNetProxiesHTTPSEnable,
- kSCPropNetProxiesHTTPSProxy,
- kSCPropNetProxiesHTTPSPort);
+ QNetworkProxy https;
+ https = proxyFromDictionary(dict, QNetworkProxy::HttpProxy,
+ kCFNetworkProxiesHTTPSEnable,
+ kCFNetworkProxiesHTTPSProxy,
+ kCFNetworkProxiesHTTPSPort);
+
+
if (https.type() != QNetworkProxy::DefaultProxy && https != protocolSpecificProxy)
result << https;
}
+#endif // !Q_OS_IOS
return result;
}
diff --git a/src/network/kernel/qnetworkproxy_libproxy.cpp b/src/network/kernel/qnetworkproxy_libproxy.cpp
index 46066b86f7..248a8d2456 100644
--- a/src/network/kernel/qnetworkproxy_libproxy.cpp
+++ b/src/network/kernel/qnetworkproxy_libproxy.cpp
@@ -12,6 +12,7 @@
#include <QtCore/QUrl>
#include <QtCore/private/qeventdispatcher_unix_p.h>
#include <QtCore/private/qthread_p.h>
+#include <QtCore/qapplicationstatic.h>
#include <proxy.h>
#include <dlfcn.h>
@@ -72,7 +73,7 @@ private:
Data *request;
};
-Q_GLOBAL_STATIC(QLibProxyWrapper, libProxyWrapper);
+Q_APPLICATION_STATIC(QLibProxyWrapper, libProxyWrapper)
QLibProxyWrapper::QLibProxyWrapper()
{
diff --git a/src/network/kernel/qnetworkproxy_win.cpp b/src/network/kernel/qnetworkproxy_win.cpp
index eb8b6ab6b1..a2daa62e84 100644
--- a/src/network/kernel/qnetworkproxy_win.cpp
+++ b/src/network/kernel/qnetworkproxy_win.cpp
@@ -295,9 +295,9 @@ public:
}
void clear() {
- for (HANDLE event : qAsConst(m_watchEvents))
+ for (HANDLE event : std::as_const(m_watchEvents))
CloseHandle(event);
- for (HKEY key : qAsConst(m_registryHandles))
+ for (HKEY key : std::as_const(m_registryHandles))
RegCloseKey(key);
m_watchEvents.clear();
diff --git a/src/network/kernel/qt_attribution.json b/src/network/kernel/qt_attribution.json
deleted file mode 100644
index 90151bb085..0000000000
--- a/src/network/kernel/qt_attribution.json
+++ /dev/null
@@ -1,28 +0,0 @@
-{
- "Id": "psl",
- "Name": "The Public Suffix List",
- "QDocModule": "qtnetwork",
- "Description": "The Public Suffix List is an initiative of Mozilla,
-but is maintained as a community resource. It is available for use in any software,
-but was originally created to meet the needs of browser manufacturers.
-It allows browsers to, for example:
-
-- Avoid privacy-damaging \"supercookies\" being set for high-level domain name suffixes
-
-- Highlight the most important part of a domain name in the user interface
-
-- Accurately sort history entries by site",
-
- "Files": "qurltlds_p.h",
- "QtUsage": "Used in Qt Network to avoid setting \"supercookies\" in the cookie jar
-supported by Qt (by the QNetworkCookieJar class).",
-
- "Homepage": "Consult https://github.com/publicsuffix/list for the sha1 but download from ...",
- "Homepage": "http://publicsuffix.org/",
- "Version": "d17a65633b0286833727ef21e897a22564695ef5, fetched on 2022-05-23",
- "License": "Mozilla Public License 2.0",
- "LicenseFile": "PSL-LICENSE.txt",
- "LicenseId": "MPL-2.0",
- "Copyright": "The list was originally provided by Jo Hermans <jo.hermans@gmail.com>.
-It is now maintained on github (https://github.com/publicsuffix/list)."
-}
diff --git a/src/network/kernel/qtldurl.cpp b/src/network/kernel/qtldurl.cpp
index 06a7df50ba..a7aceddb18 100644
--- a/src/network/kernel/qtldurl.cpp
+++ b/src/network/kernel/qtldurl.cpp
@@ -7,9 +7,7 @@
#if QT_CONFIG(topleveldomain)
-#include "qurl.h"
#include "QtCore/qfile.h"
-#include "QtCore/qfileinfo.h"
#include "QtCore/qloggingcategory.h"
#include "QtCore/qstandardpaths.h"
#include "QtCore/qstring.h"
@@ -19,7 +17,7 @@
#endif
#if QT_CONFIG(publicsuffix_qt)
-# include "qurltlds_p.h"
+# include "psl_data.cpp"
#endif
// Defined in src/3rdparty/libpsl/src/lookup_string_in_fixed_set.c
@@ -214,4 +212,4 @@ Q_NETWORK_EXPORT bool qIsEffectiveTLD(QStringView domain)
QT_END_NAMESPACE
-#endif
+#endif // QT_CONFIG(topleveldomain)
diff --git a/src/network/kernel/qtldurl_p.h b/src/network/kernel/qtldurl_p.h
index f051fba4df..86b163f161 100644
--- a/src/network/kernel/qtldurl_p.h
+++ b/src/network/kernel/qtldurl_p.h
@@ -16,7 +16,6 @@
//
#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include "QtCore/qurl.h"
#include "QtCore/qstring.h"
QT_REQUIRE_CONFIG(topleveldomain);
@@ -31,4 +30,4 @@ inline bool qIsEffectiveTLD(const QString &domain)
QT_END_NAMESPACE
-#endif // QDATAURL_P_H
+#endif // QTLDURL_P_H
diff --git a/src/network/kernel/qtnetworkglobal_p.h b/src/network/kernel/qtnetworkglobal_p.h
index ff2b75e3a9..b90e675cb4 100644
--- a/src/network/kernel/qtnetworkglobal_p.h
+++ b/src/network/kernel/qtnetworkglobal_p.h
@@ -18,7 +18,6 @@
#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/private/qglobal_p.h>
#include <QtNetwork/private/qtnetwork-config_p.h>
-#include <QtNetwork/private/qtnetworkexports_p.h>
QT_BEGIN_NAMESPACE
diff --git a/src/network/kernel/qurltlds_p.h b/src/network/kernel/qurltlds_p.h
deleted file mode 100644
index 0639247e19..0000000000
--- a/src/network/kernel/qurltlds_p.h
+++ /dev/null
@@ -1,4477 +0,0 @@
-/* This file has been generated by psl-make-dafsa. DO NOT EDIT!
-
-The byte array encodes effective tld names. See psl-make-dafsa source for documentation.*/
-
-static constexpr unsigned char kDafsa[53647] = {
- 0x40, 0x42, 0x4a, 0xa2, 0x40, 0xc1, 0x42, 0xc1, 0x50, 0xa1, 0x43, 0xaf,
- 0x43, 0x58, 0x43, 0x5d, 0x49, 0xab, 0x51, 0x6f, 0x44, 0xcb, 0x40, 0x7b,
- 0x47, 0x14, 0x47, 0x05, 0x49, 0x33, 0x4c, 0x44, 0x45, 0x5b, 0x4a, 0xc3,
- 0x42, 0x80, 0x49, 0x25, 0x47, 0xfb, 0x47, 0x1e, 0x48, 0x13, 0x45, 0xef,
- 0x46, 0x57, 0x4d, 0xb2, 0x49, 0xa0, 0x4c, 0xe9, 0x0e, 0x03, 0x03, 0x0d,
- 0x03, 0x0e, 0x12, 0x19, 0xc0, 0x46, 0x9f, 0x22, 0x08, 0x09, 0x10, 0x40,
- 0x8d, 0x33, 0x40, 0xb2, 0x40, 0xd0, 0x41, 0x67, 0x40, 0x79, 0x40, 0x83,
- 0x0a, 0x41, 0xf6, 0x04, 0x40, 0x88, 0x41, 0x3f, 0x23, 0x0a, 0x0a, 0x40,
- 0x55, 0x40, 0xe2, 0x0f, 0x88, 0x6d, 0x55, 0x5c, 0x1f, 0xea, 0xe0, 0xce,
- 0xfe, 0x6c, 0x42, 0x7c, 0x1f, 0x6c, 0xc4, 0xe0, 0xb0, 0x62, 0x6b, 0x4b,
- 0x77, 0x9f, 0x02, 0x85, 0x6c, 0xfb, 0xe0, 0xcf, 0xeb, 0x6b, 0xc4, 0xe0,
- 0xcf, 0x30, 0xe9, 0x0a, 0x08, 0x09, 0x05, 0x16, 0x07, 0x14, 0x0f, 0x18,
- 0x8c, 0x79, 0x7f, 0x1f, 0x65, 0x45, 0xd0, 0xc2, 0x55, 0x73, 0x65, 0x1f,
- 0x65, 0x4f, 0xd6, 0xe0, 0xcf, 0xde, 0x6b, 0x58, 0x9f, 0xc2, 0x18, 0x66,
- 0x59, 0x9f, 0x03, 0xc1, 0x38, 0xe6, 0x04, 0xe0, 0x3f, 0xf6, 0x60, 0x7c,
- 0x1f, 0x69, 0x47, 0x4c, 0x1f, 0x66, 0xcb, 0xc4, 0x51, 0x64, 0x50, 0x1f,
- 0x65, 0xce, 0xc9, 0xa8, 0xe3, 0x02, 0x86, 0x5f, 0x1f, 0x65, 0xd3, 0xc8,
- 0xb3, 0x5e, 0x1f, 0x65, 0x48, 0x69, 0x1f, 0x66, 0xf5, 0xe0, 0xc6, 0x95,
- 0xdd, 0x02, 0x86, 0x59, 0x1f, 0xe5, 0xe0, 0xc9, 0x6d, 0x52, 0x1f, 0x66,
- 0xe3, 0xdd, 0xe8, 0xdb, 0x02, 0x8e, 0x7b, 0x1f, 0x68, 0x68, 0x4a, 0x1f,
- 0x67, 0x5b, 0x48, 0x1f, 0x67, 0xe7, 0xc3, 0x87, 0x46, 0x1f, 0x65, 0xdb,
- 0xe0, 0xc7, 0x3d, 0x55, 0x77, 0x9f, 0x02, 0x84, 0x69, 0xc7, 0xc2, 0x3c,
- 0xe5, 0xc2, 0x36, 0x40, 0x5a, 0x1f, 0x68, 0xf2, 0xe0, 0xce, 0xfd, 0xe8,
- 0x06, 0x08, 0x07, 0x0b, 0x06, 0x85, 0x74, 0x6d, 0x1f, 0x67, 0xc9, 0xe0,
- 0xce, 0xee, 0x70, 0x77, 0x1f, 0x66, 0xed, 0xc3, 0x75, 0x6f, 0x7a, 0x1f,
- 0x65, 0x5f, 0x7a, 0x1f, 0x64, 0xfa, 0xc3, 0x22, 0x4c, 0x68, 0x1f, 0xe5,
- 0xc2, 0x09, 0x47, 0x7a, 0x9f, 0xc2, 0x8f, 0x41, 0x54, 0x1f, 0x69, 0xc0,
- 0xc3, 0x10, 0xe7, 0x0c, 0x07, 0x1d, 0x13, 0x16, 0x14, 0x04, 0x12, 0x11,
- 0x07, 0x09, 0x87, 0x7e, 0x64, 0x1f, 0x69, 0xe6, 0xd1, 0xed, 0x7d, 0x51,
- 0x9f, 0x02, 0x91, 0xe7, 0x03, 0x07, 0x83, 0x7b, 0xdc, 0x56, 0x73, 0xe0,
- 0xb9, 0xb5, 0xf5, 0xcf, 0x1b, 0xeb, 0xc1, 0xf4, 0xe5, 0x03, 0xc2, 0x3e,
- 0xdd, 0xc7, 0xc9, 0x7b, 0x44, 0x1f, 0xe7, 0x02, 0x89, 0x7b, 0xc7, 0x03,
- 0xd9, 0x90, 0x1f, 0xe6, 0xc0, 0xc2, 0x79, 0xd4, 0xd9, 0x8a, 0x76, 0x72,
- 0x9f, 0x02, 0x86, 0x68, 0x77, 0xef, 0xe0, 0xa4, 0x3c, 0xe7, 0x02, 0x84,
- 0x7b, 0xdc, 0xd9, 0x78, 0x75, 0xe1, 0xc2, 0x66, 0x75, 0x44, 0x1f, 0xe7,
- 0x02, 0x84, 0x7b, 0xc7, 0xd9, 0x6a, 0x79, 0x54, 0xae, 0x42, 0x79, 0x60,
- 0x8f, 0xd7, 0xde, 0xec, 0x6e, 0xc7, 0xc2, 0xdc, 0xe7, 0x02, 0x87, 0x7b,
- 0x1f, 0x65, 0xca, 0xe0, 0xce, 0xc3, 0x4b, 0x1f, 0x67, 0x54, 0xf0, 0xe0,
- 0xce, 0xb6, 0x66, 0x4f, 0x9f, 0x02, 0x86, 0xe5, 0x41, 0x1e, 0xe0, 0xc7,
- 0x5d, 0x64, 0x7a, 0xd5, 0xe0, 0xce, 0xa5, 0x65, 0x5e, 0x1f, 0x65, 0x65,
- 0xc8, 0x83, 0x5f, 0x73, 0x1f, 0x65, 0x77, 0xdd, 0xe0, 0xce, 0x95, 0x46,
- 0x4a, 0x1f, 0x66, 0xdc, 0xd1, 0x55, 0x42, 0x79, 0x1f, 0xe7, 0xc2, 0x8e,
- 0xe6, 0x12, 0x0d, 0x04, 0x07, 0x0c, 0x09, 0x07, 0x07, 0x07, 0x06, 0x07,
- 0x19, 0x11, 0x11, 0x09, 0x08, 0x0b, 0x87, 0x7e, 0x73, 0x1f, 0xe9, 0x02,
- 0x84, 0xd7, 0xe0, 0xce, 0x70, 0xd6, 0xc7, 0x28, 0x7b, 0xcb, 0xc2, 0x2e,
- 0x78, 0x78, 0x1f, 0x66, 0xc8, 0xc3, 0xce, 0x77, 0x61, 0x1f, 0x69, 0x69,
- 0x6c, 0x1f, 0x69, 0xd4, 0xe0, 0xce, 0x5b, 0x72, 0x56, 0x1f, 0x67, 0x78,
- 0xc4, 0xe0, 0xce, 0x48, 0x60, 0x43, 0x1f, 0x66, 0xdc, 0xdc, 0x0b, 0x5d,
- 0x71, 0x1f, 0x64, 0xfa, 0xd1, 0x01, 0x5c, 0x7a, 0x1f, 0x66, 0xde, 0xc7,
- 0x2c, 0x5b, 0x78, 0x1f, 0xe7, 0xc3, 0xd4, 0x57, 0x76, 0x1f, 0x65, 0xf0,
- 0xc1, 0xfc, 0x56, 0x70, 0x9f, 0x03, 0x04, 0x86, 0x69, 0xd7, 0xc8, 0x0d,
- 0x66, 0x7d, 0xdf, 0xe0, 0xce, 0x16, 0x65, 0x4a, 0x60, 0x1f, 0x65, 0xdd,
- 0xe0, 0xce, 0x17, 0xd5, 0x02, 0x87, 0x59, 0x1f, 0x68, 0x42, 0xf2, 0xc1,
- 0xa6, 0x4e, 0x1f, 0x68, 0x42, 0xf2, 0xd8, 0x90, 0x54, 0x7f, 0x1f, 0xe5,
- 0x02, 0x87, 0x7a, 0xdc, 0x41, 0x95, 0xe0, 0xcd, 0x6c, 0xca, 0xe0, 0xcd,
- 0xf5, 0x4b, 0x5b, 0x1f, 0x68, 0x41, 0xd8, 0xe0, 0xce, 0xf2, 0x49, 0x4b,
- 0x1f, 0x66, 0xdc, 0xe0, 0xcd, 0x0d, 0x48, 0x51, 0x1f, 0x67, 0x48, 0x71,
- 0x1f, 0x64, 0xfd, 0xc2, 0x3e, 0x45, 0x48, 0x1f, 0x65, 0xd6, 0xc6, 0xc1,
- 0x44, 0x5b, 0x9f, 0x02, 0x85, 0x67, 0xdf, 0xe0, 0xca, 0xd2, 0x65, 0x6a,
- 0xdb, 0xe0, 0xcd, 0xb8, 0xe5, 0x16, 0x0b, 0x09, 0x09, 0x07, 0x17, 0x14,
- 0x03, 0x15, 0x09, 0x08, 0x23, 0x07, 0x09, 0x15, 0x1b, 0x0d, 0x0e, 0x09,
- 0x0d, 0x28, 0x88, 0xfe, 0x02, 0x82, 0xf3, 0x89, 0x6e, 0x1f, 0x65, 0xcd,
- 0xc1, 0x6c, 0x7a, 0x43, 0x1f, 0x65, 0x73, 0xf6, 0xe0, 0xcd, 0x8d, 0x79,
- 0x7f, 0x1f, 0x64, 0x78, 0xdc, 0xe0, 0xce, 0x94, 0x73, 0x76, 0x1f, 0x66,
- 0xe0, 0xd7, 0x76, 0xf2, 0x03, 0x08, 0x83, 0x69, 0x1f, 0x66, 0x49, 0xcb,
- 0xe0, 0xcd, 0x71, 0xe1, 0xc0, 0xb0, 0x50, 0x1f, 0x69, 0x58, 0xdc, 0xe0,
- 0xcd, 0x66, 0x71, 0x71, 0x9f, 0x02, 0x84, 0x66, 0xe2, 0xdb, 0x27, 0xe5,
- 0x02, 0x85, 0x7d, 0xe2, 0xe0, 0xcd, 0x55, 0xcf, 0xda, 0x65, 0xef, 0xc0,
- 0x90, 0xee, 0x02, 0x86, 0x76, 0x1f, 0x69, 0xdb, 0xc7, 0x37, 0x6e, 0x1f,
- 0xe5, 0x02, 0x82, 0xf4, 0x82, 0x5f, 0xce, 0xe0, 0xcd, 0x3a, 0x68, 0x71,
- 0x1f, 0x64, 0x79, 0xd0, 0xe0, 0xce, 0x41, 0x65, 0x48, 0x1f, 0x68, 0xc9,
- 0xe0, 0xcd, 0x28, 0xe4, 0x02, 0x8c, 0x69, 0x1f, 0x64, 0x78, 0x7b, 0x1f,
- 0x66, 0x55, 0xd9, 0xe0, 0xce, 0x2a, 0x67, 0x9f, 0x03, 0x06, 0x83, 0x69,
- 0x58, 0xea, 0xe0, 0xcd, 0x0f, 0x66, 0xcb, 0x93, 0x65, 0x48, 0xc6, 0xe0,
- 0xcd, 0x06, 0x5f, 0x7c, 0x1f, 0x67, 0xce, 0xc0, 0x5b, 0x5c, 0x68, 0x1f,
- 0x67, 0x7a, 0xff, 0xe0, 0xce, 0x06, 0x58, 0x49, 0x1f, 0x69, 0x47, 0xcc,
- 0x04, 0xe0, 0xcd, 0xf9, 0x1f, 0x65, 0x64, 0x67, 0x1f, 0x69, 0x45, 0x52,
- 0x1f, 0xe5, 0x94, 0x55, 0x46, 0x9f, 0x02, 0x8b, 0xe6, 0x02, 0x85, 0x65,
- 0xed, 0xe0, 0xa2, 0x25, 0xe0, 0xc4, 0xe4, 0xe5, 0x02, 0x85, 0x7a, 0xd7,
- 0xe0, 0xcd, 0xd9, 0xdf, 0xc6, 0x6f, 0x52, 0x4c, 0x1f, 0x66, 0x6d, 0x4c,
- 0x1f, 0x65, 0x71, 0xf1, 0xe0, 0xcc, 0xb9, 0x4f, 0x70, 0x9f, 0x02, 0x85,
- 0x67, 0xc1, 0xe0, 0xc2, 0x7b, 0x66, 0xf9, 0xc3, 0x02, 0x4d, 0x43, 0x1f,
- 0x68, 0x51, 0xc9, 0xe0, 0xcc, 0xa2, 0x4c, 0x57, 0x1f, 0x66, 0x75, 0x77,
- 0x1f, 0x69, 0x41, 0xd3, 0xe0, 0xcc, 0x95, 0xc5, 0x03, 0x06, 0x97, 0x75,
- 0x1f, 0x65, 0xfa, 0xd9, 0xed, 0x6c, 0x9f, 0x02, 0x84, 0x67, 0xdb, 0xc4,
- 0xf7, 0x65, 0x4f, 0xf8, 0x04, 0xe0, 0xcd, 0x89, 0xae, 0x21, 0x60, 0x8f,
- 0xd7, 0xe0, 0x39, 0xb1, 0x6b, 0x1f, 0x65, 0xcd, 0xe0, 0xc3, 0x5a, 0x41,
- 0x65, 0x1f, 0x65, 0xfa, 0xe0, 0xcb, 0x9b, 0x40, 0x4b, 0x1f, 0x64, 0x7a,
- 0x7a, 0xae, 0x04, 0xe0, 0x8f, 0xd7, 0x1f, 0x69, 0x66, 0x59, 0x1f, 0xe6,
- 0xe0, 0x3c, 0x7c, 0xe4, 0x05, 0x08, 0x12, 0x09, 0x95, 0x7f, 0x61, 0x1f,
- 0x66, 0xc1, 0xe0, 0xcc, 0x69, 0xfd, 0x02, 0x87, 0x5b, 0x1f, 0x65, 0xf1,
- 0xe0, 0xac, 0x91, 0x50, 0x1f, 0x68, 0x73, 0xc0, 0xe0, 0xcc, 0x31, 0x7c,
- 0x41, 0x1f, 0x64, 0x78, 0xda, 0xe0, 0xcd, 0x38, 0xfa, 0x02, 0x88, 0x6c,
- 0x1f, 0x69, 0x43, 0xfd, 0xe0, 0xcc, 0x1d, 0x5a, 0x1f, 0x69, 0x69, 0x6c,
- 0x1f, 0x69, 0xc0, 0xc4, 0x87, 0xf8, 0x04, 0x1f, 0x07, 0x88, 0x6d, 0x9f,
- 0x03, 0x0a, 0x8b, 0x66, 0x56, 0x47, 0x1f, 0x67, 0x7d, 0xd1, 0xe0, 0xcd,
- 0x0f, 0xe5, 0x02, 0x85, 0x5c, 0xcb, 0xe0, 0xcd, 0x07, 0xdb, 0xc5, 0x51,
- 0x64, 0xff, 0xe0, 0xcb, 0xf9, 0x6a, 0x1f, 0x64, 0x7a, 0xfa, 0xd6, 0x7c,
- 0x56, 0x1f, 0x67, 0x55, 0xcc, 0xe0, 0xcc, 0xf0, 0x49, 0x1f, 0x69, 0x47,
- 0xcd, 0xe0, 0xcb, 0xd8, 0xe3, 0x04, 0x24, 0xc0, 0x4e, 0xc3, 0x02, 0x8e,
- 0x5d, 0x1f, 0x63, 0x42, 0x64, 0x1f, 0x63, 0x43, 0x73, 0x1f, 0x63, 0xc3,
- 0xc4, 0x14, 0x55, 0x1f, 0x63, 0x42, 0x61, 0x1f, 0x63, 0x43, 0x43, 0x1f,
- 0x63, 0x42, 0x77, 0x1f, 0x63, 0x43, 0xe7, 0xc0, 0x4a, 0xc2, 0x06, 0x06,
- 0x0b, 0x08, 0x0f, 0x90, 0x7b, 0x1f, 0x63, 0x43, 0xfc, 0x9d, 0x79, 0x1f,
- 0x63, 0x43, 0x48, 0x1f, 0x63, 0xc2, 0xe0, 0xc3, 0x53, 0x73, 0x1f, 0x63,
- 0x43, 0xe0, 0xe0, 0xcc, 0x9f, 0x70, 0x1f, 0x63, 0x43, 0x7c, 0x1f, 0x63,
- 0x42, 0x70, 0x1f, 0x63, 0xc3, 0xe0, 0xca, 0xc0, 0x6f, 0x1f, 0x63, 0x43,
- 0x69, 0x1f, 0x63, 0x42, 0x66, 0x1f, 0x63, 0x43, 0xc9, 0xe0, 0xcc, 0x80,
- 0x62, 0x1f, 0x63, 0x43, 0x5e, 0x1f, 0x63, 0x42, 0x7e, 0x1f, 0x63, 0xc3,
- 0xe0, 0xca, 0x9e, 0x41, 0x7f, 0x1f, 0x63, 0x42, 0x53, 0x1f, 0x63, 0xc1,
- 0xe0, 0xca, 0xbb, 0x61, 0x43, 0x52, 0x1f, 0x61, 0x43, 0xd4, 0xe0, 0xcc,
- 0x5b, 0xe0, 0x0f, 0x0c, 0x13, 0x40, 0x7e, 0x11, 0x13, 0x0f, 0x13, 0x40,
- 0x5b, 0x0e, 0x10, 0x0e, 0xaa, 0x7a, 0x65, 0x1f, 0x60, 0x7a, 0x72, 0x1f,
- 0x60, 0xfa, 0xe0, 0xcc, 0x3e, 0xf9, 0x03, 0xc0, 0x78, 0x40, 0x1f, 0x60,
- 0x78, 0x59, 0x1f, 0x60, 0x79, 0x47, 0x1f, 0x60, 0x78, 0xd5, 0xc0, 0x67,
- 0xf8, 0x06, 0x13, 0x12, 0x16, 0x16, 0x9c, 0x6d, 0x1f, 0x60, 0x78, 0x47,
- 0x1f, 0x60, 0x78, 0x44, 0x1f, 0x60, 0x79, 0x4c, 0x1f, 0x60, 0x78, 0xc1,
- 0xc0, 0x49, 0x68, 0x1f, 0x60, 0x78, 0x76, 0x1f, 0x60, 0x78, 0x41, 0x1f,
- 0x60, 0x78, 0x69, 0x1f, 0x60, 0x78, 0xf2, 0xba, 0x63, 0x1f, 0x60, 0x78,
- 0x71, 0x1f, 0x60, 0x78, 0x50, 0x1f, 0x60, 0x78, 0x5a, 0x1f, 0x60, 0x78,
- 0x72, 0x1f, 0x60, 0x78, 0xe5, 0xa4, 0x58, 0x1f, 0x60, 0x78, 0x78, 0x1f,
- 0x60, 0x78, 0x63, 0x1f, 0x60, 0x78, 0x41, 0x1f, 0x60, 0x78, 0x74, 0x1f,
- 0x60, 0x78, 0xc8, 0x8e, 0x57, 0x1f, 0x60, 0x78, 0x6b, 0x1f, 0x60, 0x78,
- 0x72, 0x1f, 0x60, 0x78, 0x63, 0x2e, 0x1f, 0x60, 0x79, 0x44, 0x1f, 0x60,
- 0x78, 0x57, 0x1f, 0x60, 0xf8, 0xe0, 0xc2, 0x64, 0x44, 0x1f, 0x60, 0x78,
- 0x6d, 0x1f, 0xe0, 0xe0, 0x6c, 0xaa, 0x76, 0x7d, 0x1f, 0x60, 0x76, 0x42,
- 0x1f, 0x60, 0x76, 0x5a, 0x1f, 0x60, 0x77, 0xcf, 0xe0, 0xcb, 0x9d, 0x74,
- 0x6d, 0x1f, 0x60, 0x74, 0x7e, 0x1f, 0x60, 0x74, 0x70, 0x1f, 0x60, 0x74,
- 0x64, 0x1f, 0x60, 0xf4, 0xc3, 0x6a, 0x72, 0x6d, 0x1f, 0x60, 0x72, 0x7e,
- 0x1f, 0x60, 0x72, 0x70, 0x1f, 0xe0, 0xe0, 0xbc, 0x9c, 0x70, 0x6d, 0x1f,
- 0x60, 0x70, 0x7e, 0x1f, 0x60, 0x70, 0x70, 0x1f, 0x60, 0x70, 0x64, 0x1f,
- 0x60, 0xf1, 0xc0, 0xeb, 0xee, 0x02, 0xaa, 0x5a, 0x1f, 0x60, 0x6e, 0x7f,
- 0x1f, 0x60, 0x6e, 0x59, 0x1f, 0x60, 0x6f, 0x4d, 0x1f, 0x60, 0x6e, 0x55,
- 0x1f, 0x60, 0x6e, 0x6a, 0x1f, 0x60, 0x6f, 0x4d, 0x1f, 0x60, 0x6e, 0x6a,
- 0x1f, 0x60, 0x6f, 0x42, 0x1f, 0x60, 0x6e, 0x70, 0x1f, 0x60, 0xef, 0xc0,
- 0xbe, 0x47, 0x1f, 0x60, 0xee, 0x02, 0x92, 0x72, 0x1f, 0x60, 0x6e, 0x59,
- 0x1f, 0x60, 0x6f, 0x4d, 0x1f, 0x60, 0x6e, 0x55, 0x1f, 0x60, 0xef, 0xc2,
- 0x65, 0x68, 0x1f, 0x60, 0x6f, 0x4d, 0x1f, 0x60, 0x6e, 0x64, 0x1f, 0x60,
- 0x6e, 0x7f, 0x1f, 0x60, 0x6e, 0x6f, 0x1f, 0x60, 0xee, 0xc0, 0x54, 0x6c,
- 0x6d, 0x1f, 0x60, 0x6c, 0x7e, 0x1f, 0x60, 0x6c, 0x70, 0x1f, 0xe0, 0xd9,
- 0x0a, 0x6a, 0x6d, 0x1f, 0x60, 0x6a, 0x7e, 0x1f, 0x60, 0x6a, 0x70, 0x1f,
- 0x60, 0xea, 0xe0, 0xc9, 0x3a, 0x68, 0x6d, 0x1f, 0x60, 0x68, 0x7e, 0x1f,
- 0x60, 0x68, 0x70, 0x1f, 0xe0, 0xce, 0x60, 0xe6, 0x02, 0x93, 0x6d, 0x1f,
- 0x60, 0x66, 0x7e, 0x1f, 0xe0, 0x02, 0x82, 0xe7, 0x82, 0x66, 0x70, 0x1f,
- 0x60, 0xe6, 0xe0, 0xc9, 0x16, 0x6c, 0x1f, 0x60, 0x66, 0x7e, 0x1f, 0x60,
- 0x66, 0x42, 0x1f, 0x60, 0x66, 0x72, 0x1f, 0x60, 0x66, 0xfe, 0xe0, 0xca,
- 0xb7, 0xe4, 0x04, 0x11, 0x26, 0x8c, 0x78, 0x1f, 0x60, 0x64, 0x42, 0x1f,
- 0x60, 0x64, 0x57, 0x1f, 0x60, 0x64, 0x60, 0x1f, 0xe0, 0xcc, 0x6e, 0x6d,
- 0x1f, 0x60, 0x64, 0x7e, 0x1f, 0x60, 0x64, 0x70, 0x1f, 0xe0, 0x02, 0x88,
- 0x65, 0x4b, 0x1f, 0x60, 0xe4, 0xe0, 0xc8, 0xd7, 0x64, 0xe4, 0x04, 0xe0,
- 0xca, 0x83, 0x1f, 0x60, 0x64, 0x6e, 0x1f, 0x60, 0x65, 0xcd, 0xe0, 0xca,
- 0x7b, 0x68, 0x1f, 0x60, 0x65, 0x47, 0x1f, 0x60, 0x64, 0xdf, 0xe0, 0xca,
- 0x6f, 0x55, 0x1f, 0x60, 0x65, 0x49, 0x1f, 0xe0, 0xe0, 0xc5, 0x5f, 0x5a,
- 0xc0, 0xc0, 0xd9, 0xd9, 0x06, 0x14, 0x0d, 0x34, 0x1a, 0x86, 0x7e, 0x1f,
- 0x58, 0x67, 0x9f, 0x02, 0x83, 0x5a, 0xe9, 0x83, 0x59, 0x43, 0x1f, 0x58,
- 0x73, 0x1f, 0x58, 0xea, 0xc1, 0x27, 0x47, 0x1f, 0x59, 0x45, 0x1f, 0x58,
- 0x71, 0x1f, 0x58, 0x67, 0x9f, 0xc1, 0x3b, 0x45, 0x9f, 0x02, 0xac, 0xd9,
- 0x02, 0x9a, 0x48, 0x9f, 0x02, 0x87, 0x59, 0x42, 0x1f, 0xd8, 0xe0, 0xc8,
- 0x86, 0x58, 0x71, 0x1f, 0x59, 0x4a, 0x1f, 0x58, 0x6a, 0x1f, 0x58, 0x67,
- 0x1f, 0x59, 0xc6, 0x88, 0x44, 0x1f, 0x59, 0x4a, 0x1f, 0x58, 0x73, 0x1f,
- 0x59, 0x4a, 0x1f, 0xd8, 0xe0, 0xca, 0x08, 0x58, 0xf5, 0xc1, 0x1a, 0x43,
- 0x9f, 0x02, 0x86, 0x59, 0x48, 0x1f, 0xd9, 0xc2, 0xd7, 0x58, 0x67, 0x1f,
- 0x58, 0x6b, 0x1f, 0x59, 0x48, 0x1f, 0x59, 0x44, 0x1f, 0x59, 0xca, 0xc0,
- 0x6e, 0x42, 0x1f, 0x58, 0xf7, 0xc0, 0xfa, 0x41, 0x1f, 0x59, 0x44, 0x1f,
- 0x58, 0x73, 0x1f, 0x58, 0xf7, 0xc0, 0xfa, 0xd8, 0x06, 0x15, 0x0a, 0x16,
- 0x0c, 0xa5, 0x79, 0x9f, 0x02, 0x84, 0x59, 0xc5, 0xc0, 0xab, 0x58, 0x71,
- 0x1f, 0xd8, 0x04, 0xe0, 0xc8, 0xb4, 0x67, 0x1f, 0xd9, 0xc1, 0x9d, 0x74,
- 0x1f, 0x58, 0x68, 0x1f, 0x59, 0x43, 0x9f, 0xc0, 0xba, 0x73, 0x1f, 0x59,
- 0x48, 0x1f, 0xd8, 0x02, 0x8b, 0x71, 0x1f, 0x59, 0x4a, 0x1f, 0xd8, 0x60,
- 0xc8, 0x22, 0xc1, 0x7f, 0xef, 0xc0, 0x7e, 0x6a, 0x1f, 0x59, 0x48, 0x1f,
- 0x59, 0x46, 0x1f, 0xd8, 0xe0, 0xc7, 0xbe, 0x68, 0x9f, 0x03, 0x08, 0x89,
- 0x5a, 0x7e, 0x1f, 0x58, 0x67, 0x1f, 0xd8, 0x96, 0x59, 0x4a, 0x1f, 0x58,
- 0x6a, 0x1f, 0xd9, 0xc0, 0x74, 0x58, 0x67, 0x1f, 0xd8, 0x02, 0x86, 0x72,
- 0x1f, 0x58, 0xe7, 0xc0, 0x84, 0xf1, 0xc0, 0xbf, 0x67, 0x9f, 0x04, 0x03,
- 0xc0, 0x93, 0x5b, 0xcc, 0x86, 0xd9, 0x03, 0x0f, 0x89, 0x4a, 0x1f, 0x58,
- 0x71, 0x1f, 0x58, 0x67, 0x1f, 0x59, 0xc6, 0x4d, 0x93, 0xe0, 0xbb, 0xbf,
- 0x45, 0x1f, 0x58, 0x67, 0x1f, 0x58, 0xf1, 0xc0, 0x97, 0x44, 0x9f, 0x02,
- 0x95, 0xd9, 0x02, 0x86, 0x4a, 0x1f, 0x59, 0xc5, 0xc0, 0x66, 0x45, 0x1f,
- 0x58, 0x7a, 0x1f, 0x58, 0x71, 0x1f, 0xd8, 0xe0, 0xc8, 0x23, 0xd8, 0x05,
- 0x0c, 0x25, 0x0f, 0x8b, 0x79, 0x1f, 0x59, 0x44, 0x1f, 0x59, 0x4a, 0x1f,
- 0x58, 0xe7, 0xc0, 0x48, 0x73, 0x1f, 0x58, 0x79, 0x1f, 0x59, 0x48, 0x1f,
- 0x58, 0x6f, 0x9f, 0x02, 0x8a, 0x5b, 0x4c, 0x9f, 0x02, 0x8f, 0x5b, 0xc3,
- 0xe0, 0xc9, 0x05, 0x59, 0x4a, 0x9f, 0x02, 0x85, 0x59, 0xc7, 0xe0, 0xc8,
- 0xfb, 0xd8, 0xe0, 0xc7, 0x77, 0x6c, 0x1f, 0x58, 0x72, 0x1f, 0x58, 0x67,
- 0x1f, 0x58, 0x66, 0x1f, 0xd8, 0xe0, 0xa8, 0x30, 0x68, 0x1f, 0x58, 0x6d,
- 0x1f, 0x58, 0x71, 0x1f, 0x59, 0xca, 0x88, 0x67, 0x1f, 0x58, 0x71, 0x1f,
- 0x58, 0x6f, 0x1f, 0x59, 0xc6, 0xe0, 0xc8, 0xd0, 0xd8, 0x03, 0x10, 0x92,
- 0x71, 0x1f, 0x58, 0x67, 0x1f, 0x59, 0x45, 0x1f, 0x59, 0x43, 0x1f, 0x59,
- 0xc8, 0xe0, 0xc8, 0xbc, 0x6a, 0x1f, 0x58, 0x75, 0x1f, 0x58, 0x67, 0x1f,
- 0x59, 0x44, 0x1f, 0x58, 0x67, 0x1f, 0xd8, 0xe0, 0xc7, 0x00, 0x68, 0x1f,
- 0x59, 0x48, 0x1f, 0x58, 0x78, 0x1f, 0x58, 0x68, 0x1f, 0x59, 0xca, 0xe0,
- 0xc8, 0x9a, 0xd7, 0x02, 0x8a, 0x67, 0x1f, 0x57, 0x55, 0x1f, 0x57, 0xdd,
- 0xe0, 0xc8, 0x8d, 0x59, 0x1f, 0x57, 0x68, 0x1f, 0x57, 0x55, 0x1f, 0x57,
- 0x69, 0x1f, 0x57, 0x5c, 0x1f, 0x57, 0x59, 0x1f, 0x57, 0xdd, 0xe0, 0xc6,
- 0x4d, 0x55, 0x70, 0x1f, 0x55, 0x61, 0x1f, 0xd5, 0xe0, 0xc8, 0x05, 0x52,
- 0x5b, 0x1f, 0x50, 0x70, 0x1f, 0xd0, 0xe0, 0xc6, 0x89, 0xd1, 0x04, 0x03,
- 0x0d, 0xb0, 0xcf, 0xc1, 0x10, 0x43, 0x1f, 0xd0, 0x03, 0xc0, 0x51, 0x7a,
- 0x1f, 0x51, 0xc0, 0xe0, 0xc8, 0x4e, 0x41, 0x9f, 0x03, 0xc1, 0x16, 0xd0,
- 0x03, 0x06, 0x89, 0x7f, 0x1f, 0x50, 0xf1, 0xc0, 0xf4, 0x7e, 0x1f, 0x51,
- 0x47, 0x1f, 0x50, 0xf8, 0xc0, 0xeb, 0x70, 0x1f, 0xd0, 0x02, 0x8c, 0x7c,
- 0x1f, 0x50, 0x70, 0x1f, 0x51, 0x40, 0x1f, 0x50, 0xf0, 0xc0, 0xda, 0x79,
- 0x1f, 0x51, 0xc2, 0xe0, 0xc8, 0x1e, 0x40, 0x1f, 0xd1, 0x02, 0x84, 0xc4,
- 0xe0, 0xc8, 0x15, 0x43, 0x1f, 0x51, 0xc1, 0xe0, 0xc8, 0x0e, 0xd0, 0x09,
- 0x02, 0x2c, 0x31, 0x2c, 0x0d, 0x07, 0x0c, 0x9d, 0xff, 0xa8, 0x7e, 0x9f,
- 0x02, 0x8f, 0x51, 0x40, 0x1f, 0x50, 0xf3, 0x04, 0xe0, 0xc7, 0xf2, 0x2e,
- 0x1f, 0xd1, 0x40, 0xa8, 0x95, 0xd0, 0x03, 0x0c, 0x83, 0x7d, 0x1f, 0x50,
- 0x7b, 0x1f, 0x50, 0x70, 0x1f, 0x50, 0x79, 0x9f, 0xaa, 0xf4, 0xc0, 0xa6,
- 0x71, 0x1f, 0x51, 0xc0, 0xc0, 0xa0, 0x7c, 0x9f, 0x02, 0x87, 0x51, 0x41,
- 0x1f, 0x50, 0xfa, 0xc0, 0x80, 0xd0, 0x03, 0x16, 0x86, 0x7e, 0x9f, 0x02,
- 0x8d, 0x51, 0x41, 0x1f, 0x50, 0x7a, 0x1f, 0x50, 0x72, 0x1f, 0xd0, 0xe0,
- 0xc6, 0xba, 0x50, 0xfd, 0xe0, 0xc7, 0xb1, 0x7a, 0x1f, 0xd0, 0xe0, 0xc6,
- 0x87, 0x78, 0x1f, 0x51, 0xc0, 0xc0, 0x5a, 0x7a, 0x9f, 0x02, 0x8a, 0x51,
- 0x40, 0x1f, 0x51, 0x4b, 0x1f, 0x50, 0xfc, 0xc0, 0x4c, 0xd0, 0x02, 0x89,
- 0x7e, 0x1f, 0x50, 0xfc, 0x40, 0x43, 0xe0, 0xc7, 0x4a, 0x70, 0x1f, 0x51,
- 0x42, 0x1f, 0x50, 0x7e, 0x1f, 0x50, 0x7b, 0x1f, 0x50, 0x78, 0x1f, 0xd0,
- 0xe0, 0xc5, 0x9c, 0x78, 0x1f, 0x50, 0x7a, 0x1f, 0x50, 0x7e, 0x1f, 0x50,
- 0xfc, 0xe0, 0xc5, 0x42, 0x75, 0x1f, 0x51, 0xce, 0xe0, 0xc7, 0x65, 0x74,
- 0x1f, 0x50, 0x75, 0x1f, 0x51, 0x42, 0x1f, 0xd0, 0xe0, 0xc0, 0x9a, 0x71,
- 0x1f, 0xd0, 0x05, 0x11, 0xe0, 0xc5, 0x6a, 0x78, 0x1f, 0x50, 0x77, 0x2e,
- 0x1f, 0x51, 0x40, 0x1f, 0x51, 0x43, 0x1f, 0x51, 0xc1, 0xe0, 0xc7, 0x3c,
- 0x75, 0x1f, 0xd0, 0x9c, 0x70, 0x1f, 0x50, 0x7a, 0x2e, 0x1f, 0x51, 0x41,
- 0x1f, 0x51, 0x40, 0x1f, 0xd0, 0xe0, 0xa6, 0x74, 0x4e, 0x75, 0x9f, 0x02,
- 0x85, 0x4f, 0xc5, 0xe0, 0xc7, 0x22, 0x4e, 0xfb, 0xe0, 0xc7, 0x1d, 0x44,
- 0x4d, 0x1f, 0x43, 0xe1, 0xe0, 0xaf, 0x69, 0xc3, 0x03, 0x28, 0xa8, 0xf8,
- 0x05, 0x08, 0x04, 0x06, 0x8c, 0xf9, 0x60, 0x46, 0x9b, 0x11, 0xe0, 0x75,
- 0x64, 0xf6, 0xe0, 0x53, 0x75, 0x73, 0x74, 0xf2, 0xe0, 0x47, 0xb7, 0xf2,
- 0x04, 0xe0, 0xba, 0xc9, 0xf3, 0x60, 0x99, 0x44, 0xe0, 0x28, 0x7a, 0xeb,
- 0xe0, 0x91, 0xa8, 0xe5, 0x0a, 0x08, 0x05, 0x0c, 0x60, 0x98, 0x05, 0xe0,
- 0x20, 0x3d, 0xf3, 0x60, 0xbc, 0x17, 0x47, 0x3d, 0xc2, 0x92, 0xed, 0x60,
- 0xbf, 0xf7, 0x84, 0xec, 0x07, 0x60, 0x84, 0x88, 0xe0, 0x41, 0x4a, 0xe7,
- 0xe0, 0x49, 0x40, 0xeb, 0xe0, 0xc1, 0x7e, 0xe1, 0x02, 0x8b, 0xec, 0x02,
- 0x84, 0xf4, 0xe0, 0x86, 0x96, 0xe1, 0xe0, 0xc1, 0x2f, 0x6b, 0x1f, 0x45,
- 0xcb, 0xe0, 0xc1, 0x75, 0xfa, 0x13, 0x0e, 0x04, 0x08, 0x0d, 0x04, 0x04,
- 0x12, 0x0d, 0x0c, 0x53, 0x7f, 0x60, 0x87, 0x63, 0x60, 0x29, 0x7f, 0x9f,
- 0xf5, 0x02, 0x85, 0x73, 0xe8, 0xe0, 0xba, 0xae, 0x65, 0x72, 0xe9, 0xe0,
- 0xb1, 0xcb, 0xf4, 0xe0, 0xab, 0xa3, 0x70, 0xae, 0x60, 0x87, 0xce, 0xe0,
- 0x23, 0xd1, 0xef, 0x06, 0x60, 0xb1, 0x0b, 0xcb, 0xdc, 0x6d, 0x62, 0xe9,
- 0xe0, 0xb8, 0xa4, 0xec, 0xe0, 0xa7, 0x7f, 0xe9, 0xe0, 0xc5, 0x6d, 0xe8,
- 0x02, 0x86, 0x79, 0x74, 0x6f, 0x6d, 0xf9, 0x86, 0x69, 0x74, 0x6f, 0x6d,
- 0x69, 0xf2, 0xe0, 0xab, 0x74, 0x67, 0x6f, 0xf2, 0x04, 0xe0, 0xb8, 0x89,
- 0x7a, 0x65, 0xec, 0xe0, 0xb1, 0xda, 0xe5, 0x04, 0xe0, 0xc5, 0x57, 0x6e,
- 0x74, 0x73, 0xf5, 0xe0, 0x70, 0x1d, 0xe1, 0x0a, 0x06, 0x19, 0x08, 0x08,
- 0x05, 0x0a, 0xe0, 0x27, 0x2f, 0xf2, 0x60, 0xba, 0x13, 0xcb, 0x16, 0xf0,
- 0x03, 0x05, 0x84, 0x74, 0xef, 0xe0, 0xb3, 0x3f, 0xf0, 0xe0, 0xa4, 0x20,
- 0x6f, 0x72, 0x69, 0x7a, 0x68, 0x7a, 0xe8, 0x58, 0xc0, 0xe0, 0x21, 0xe4,
- 0x6d, 0xe1, 0x60, 0x86, 0x4e, 0xe0, 0x3b, 0xcc, 0x6b, 0x6f, 0x70, 0x61,
- 0xee, 0xe0, 0x8a, 0x52, 0x67, 0xe1, 0xe0, 0xb5, 0x67, 0x63, 0x68, 0x70,
- 0x6f, 0x6d, 0x6f, 0xf2, 0xe0, 0xc2, 0x08, 0xae, 0x60, 0x86, 0x7e, 0x60,
- 0x3d, 0x31, 0x42, 0x1f, 0x9c, 0xf9, 0x10, 0x40, 0x4d, 0x40, 0xd5, 0x09,
- 0x04, 0x2a, 0x60, 0x99, 0x25, 0x60, 0x2a, 0x35, 0xc0, 0x49, 0xf5, 0x0d,
- 0x07, 0x0b, 0x0d, 0x0d, 0x0a, 0x04, 0x60, 0x7f, 0x90, 0xe0, 0x44, 0x61,
- 0x7a, 0xe1, 0x60, 0x75, 0x27, 0xd8, 0x32, 0x73, 0xf5, 0x04, 0xe0, 0xbf,
- 0x47, 0x68, 0xe1, 0xe0, 0x54, 0x7d, 0xf2, 0x04, 0xe0, 0xbb, 0xfa, 0x69,
- 0x68, 0x6f, 0x6e, 0xea, 0xe0, 0x8d, 0xd2, 0xeb, 0x04, 0xe0, 0xaa, 0x45,
- 0x75, 0x68, 0x61, 0x73, 0xe8, 0xe0, 0x7a, 0xe6, 0x67, 0x61, 0x77, 0xe1,
- 0x60, 0x75, 0xf4, 0xe0, 0x4a, 0xfd, 0xe6, 0xe0, 0xb4, 0xab, 0x61, 0xf3,
- 0xe0, 0xbb, 0xd5, 0xef, 0x0d, 0x0e, 0x0a, 0x2a, 0x11, 0x16, 0x0c, 0x05,
- 0x3d, 0x09, 0xe0, 0x77, 0xc9, 0xf5, 0x04, 0xe0, 0xc5, 0x77, 0xf4, 0x04,
- 0xe0, 0x90, 0x58, 0xf5, 0xe0, 0xc3, 0xd6, 0x74, 0x73, 0x75, 0x6b, 0x61,
- 0x69, 0xe4, 0xe0, 0xc4, 0x22, 0xf3, 0x02, 0xa1, 0x68, 0xe9, 0x08, 0x0b,
- 0x04, 0x60, 0x6f, 0xe7, 0xdd, 0x89, 0x6e, 0xef, 0x04, 0xe0, 0xbe, 0x0c,
- 0x67, 0xe1, 0xe0, 0x7b, 0x4e, 0xeb, 0xe0, 0x49, 0xe2, 0x64, 0x61, 0x2e,
- 0xf3, 0x60, 0xbb, 0xe1, 0x88, 0x65, 0x6d, 0xe9, 0xe0, 0xa9, 0x3b, 0xf2,
- 0x05, 0x07, 0xe0, 0x88, 0xba, 0xeb, 0x60, 0x50, 0x57, 0xe0, 0x72, 0xb2,
- 0xe9, 0xe0, 0x74, 0x1d, 0xee, 0x05, 0x04, 0xe0, 0x8d, 0x56, 0xe5, 0xe0,
- 0x4b, 0x59, 0xe1, 0x04, 0xe0, 0x84, 0x62, 0xe7, 0x60, 0x36, 0x4e, 0xe0,
- 0x8a, 0xf9, 0xed, 0x02, 0x85, 0x69, 0xf4, 0xe0, 0x8a, 0x01, 0xe2, 0xe0,
- 0x62, 0xc0, 0x6c, 0xe1, 0xe0, 0x2e, 0x1e, 0xeb, 0x03, 0x21, 0x88, 0xef,
- 0x06, 0x04, 0x10, 0xe0, 0x4a, 0xb9, 0xfa, 0xe0, 0x6e, 0x04, 0xf3, 0x04,
- 0xe0, 0x80, 0x13, 0x68, 0x69, 0x62, 0x61, 0x68, 0x69, 0x6b, 0xe1, 0xe0,
- 0x6d, 0xbd, 0x68, 0x61, 0xed, 0xe0, 0x53, 0xe4, 0x6b, 0x61, 0x69, 0x63,
- 0xe8, 0xe0, 0x6b, 0x4b, 0xe1, 0x07, 0x60, 0x7a, 0x2c, 0xe0, 0x46, 0x96,
- 0x69, 0x63, 0x68, 0x69, 0xe2, 0xe0, 0x83, 0x59, 0xe9, 0x04, 0xe0, 0x6c,
- 0x74, 0xf4, 0xe0, 0x74, 0x20, 0x64, 0x6f, 0x62, 0xe1, 0xe0, 0x59, 0xe2,
- 0xee, 0x04, 0xe0, 0xb2, 0x26, 0xe8, 0xe0, 0xa9, 0x04, 0xeb, 0xe0, 0xc3,
- 0xa3, 0x62, 0x6f, 0xae, 0x05, 0x06, 0x08, 0x08, 0x86, 0x74, 0x72, 0xe1,
- 0xe0, 0xc4, 0x5f, 0x73, 0x63, 0x69, 0x65, 0xee, 0xe0, 0xa5, 0x0a, 0x72,
- 0x65, 0x76, 0x69, 0xe5, 0xe0, 0xb0, 0xee, 0x70, 0x61, 0xf2, 0xe0, 0x55,
- 0x6a, 0x66, 0x61, 0xe9, 0xe0, 0x7c, 0xe1, 0xe1, 0x11, 0x0e, 0x17, 0x20,
- 0x07, 0x12, 0x40, 0x92, 0x0b, 0x10, 0x09, 0x0f, 0x04, 0x16, 0xe0, 0xa7,
- 0x41, 0x77, 0xe1, 0x04, 0xe0, 0x6d, 0x6e, 0x74, 0xe1, 0x60, 0x4e, 0xa4,
- 0xe0, 0x68, 0xab, 0xf4, 0x02, 0x8f, 0x73, 0xf5, 0x02, 0x87, 0x73, 0x68,
- 0x69, 0xf2, 0xe0, 0xba, 0xe0, 0xeb, 0xe0, 0x83, 0x31, 0x6f, 0xed, 0xe0,
- 0xbf, 0xc4, 0xf3, 0x05, 0x10, 0xe0, 0x7a, 0x73, 0xf5, 0x07, 0x04, 0x60,
- 0xbf, 0x46, 0xc0, 0x61, 0xef, 0xe0, 0x7a, 0x7b, 0xe4, 0xe0, 0x57, 0x0a,
- 0x68, 0xe9, 0x04, 0xe0, 0x8c, 0x62, 0xf2, 0xe0, 0xbe, 0xef, 0xef, 0x60,
- 0x6d, 0x71, 0xe0, 0x55, 0x7a, 0xee, 0x02, 0x89, 0x64, 0x65, 0xf8, 0x60,
- 0x71, 0xd4, 0xe0, 0x52, 0x46, 0xe1, 0x60, 0x35, 0x8f, 0xd8, 0x6b, 0xed,
- 0x04, 0xe0, 0x66, 0x6d, 0xe1, 0x09, 0x05, 0x04, 0x25, 0x07, 0x1f, 0x06,
- 0x06, 0x98, 0x7a, 0xef, 0xe0, 0x59, 0x21, 0xf8, 0xe0, 0x9e, 0x7a, 0xf4,
- 0x02, 0x85, 0x73, 0xf5, 0xe0, 0x68, 0x40, 0xef, 0x03, 0x07, 0x87, 0x74,
- 0x61, 0x6b, 0xe1, 0xe0, 0x44, 0x0d, 0x6b, 0x6f, 0x72, 0xe9, 0xe0, 0x6e,
- 0x62, 0xae, 0x04, 0xe0, 0xbf, 0x30, 0xeb, 0x60, 0xbd, 0x6a, 0xc2, 0x79,
- 0x73, 0x68, 0x69, 0xee, 0xe0, 0x8b, 0xb1, 0xee, 0x02, 0x8b, 0xef, 0x02,
- 0x84, 0xf5, 0xe0, 0x2e, 0x12, 0xe2, 0xe0, 0x7f, 0xb4, 0xe1, 0x02, 0x89,
- 0x73, 0x68, 0x69, 0xae, 0x60, 0xa7, 0x56, 0xdb, 0x50, 0x6b, 0xe1, 0xe0,
- 0x8b, 0xfa, 0x6d, 0x6f, 0xf4, 0xe0, 0x24, 0xe5, 0x6b, 0x69, 0xf4, 0xe0,
- 0xbf, 0xac, 0xe7, 0x04, 0xe0, 0xc2, 0x23, 0xe1, 0x04, 0xe0, 0xbd, 0x27,
- 0x74, 0x61, 0xae, 0x60, 0xb9, 0x1b, 0x42, 0xb2, 0x40, 0xf7, 0x44, 0x48,
- 0xc1, 0x77, 0x64, 0x61, 0xae, 0x60, 0x8c, 0x02, 0x60, 0x2c, 0xa6, 0xc0,
- 0x6b, 0xec, 0x02, 0x84, 0xf4, 0xe0, 0x9f, 0xf9, 0xe9, 0xe0, 0xab, 0x07,
- 0xeb, 0x02, 0x89, 0x75, 0x6d, 0x6f, 0xae, 0x60, 0xbe, 0x7e, 0xc3, 0xd4,
- 0x61, 0xe7, 0xd4, 0x54, 0xe9, 0x04, 0xe0, 0x6b, 0x66, 0xf4, 0xe0, 0xb8,
- 0x87, 0xe8, 0x05, 0x04, 0xe0, 0x22, 0xc6, 0xe9, 0xe0, 0x53, 0xdc, 0x61,
- 0xe2, 0xe0, 0x57, 0xf6, 0x65, 0xf3, 0xd9, 0x7b, 0x63, 0xe8, 0x04, 0xe0,
- 0xba, 0xc9, 0xe9, 0x02, 0x88, 0x79, 0x6f, 0xae, 0x60, 0xbc, 0x6b, 0xc5,
- 0x8b, 0x6d, 0xe1, 0xe0, 0x50, 0xc8, 0x62, 0xf5, 0x60, 0x71, 0xe5, 0xe0,
- 0x4d, 0x3d, 0xf8, 0x0f, 0x06, 0x09, 0x07, 0x50, 0x45, 0x04, 0x09, 0x06,
- 0x0e, 0x05, 0x08, 0xe0, 0x72, 0x72, 0xf9, 0x60, 0xaa, 0x1e, 0xd7, 0x1b,
- 0xf8, 0x04, 0xe0, 0xbc, 0x4d, 0xae, 0xe0, 0xb0, 0xe3, 0x73, 0x34, 0x61,
- 0x6c, 0xec, 0xda, 0xb5, 0xee, 0x03, 0xda, 0xc6, 0x2d, 0xad, 0x3a, 0x1f,
- 0x40, 0x43, 0x21, 0x2e, 0x40, 0xd6, 0x40, 0x40, 0x40, 0xab, 0x41, 0x05,
- 0x40, 0xcc, 0x27, 0x31, 0x40, 0x48, 0x40, 0x92, 0x41, 0xba, 0x40, 0xa4,
- 0x40, 0xd8, 0x40, 0x53, 0x2f, 0x40, 0xd1, 0x40, 0x91, 0x40, 0xb7, 0x3e,
- 0x40, 0x50, 0x40, 0xc9, 0x41, 0x08, 0x38, 0x3d, 0x40, 0x56, 0x09, 0x25,
- 0x40, 0x61, 0x40, 0x4e, 0x3a, 0x13, 0xc0, 0x5f, 0xfa, 0x02, 0x96, 0xe6,
- 0x02, 0x84, 0x72, 0xb1, 0xcb, 0x1a, 0x30, 0xe1, 0x02, 0x84, 0x76, 0xf8,
- 0xcc, 0x36, 0x6f, 0x36, 0x34, 0xe1, 0xe0, 0x96, 0xec, 0x62, 0x78, 0x30,
- 0xb2, 0xce, 0xb0, 0xf9, 0x05, 0x11, 0x14, 0x0a, 0x87, 0x73, 0x74, 0x72,
- 0x65, 0x2d, 0x73, 0x6c, 0x69, 0x64, 0x72, 0x65, 0x2d, 0x75, 0xea, 0xe0,
- 0x33, 0x0b, 0xe7, 0x02, 0x88, 0x62, 0x69, 0x32, 0x61, 0xed, 0xe0, 0x86,
- 0x3e, 0x61, 0x72, 0x64, 0x65, 0x6e, 0x2d, 0xf0, 0xc9, 0x2d, 0x66, 0x72,
- 0x6f, 0x34, 0x69, 0x36, 0xb7, 0xe0, 0xc1, 0x86, 0x65, 0x72, 0x2d, 0xfa,
- 0xe0, 0xb8, 0x8f, 0x39, 0x61, 0x33, 0xe1, 0xe0, 0xa1, 0xaa, 0xf8, 0x02,
- 0x98, 0x6b, 0x63, 0xb2, 0x02, 0x8b, 0x64, 0x6c, 0x33, 0x61, 0x35, 0x65,
- 0x65, 0xb0, 0xe0, 0xc1, 0x44, 0x61, 0x6c, 0x33, 0x68, 0x79, 0xe5, 0xcd,
- 0x7b, 0x68, 0x71, 0x35, 0xb2, 0xcf, 0x41, 0xf7, 0x03, 0x0a, 0x88, 0x67,
- 0xe2, 0x02, 0x83, 0xec, 0xc4, 0x66, 0xe8, 0xce, 0x99, 0x63, 0x76, 0x73,
- 0x32, 0x32, 0xe4, 0xc9, 0x9c, 0x34, 0xf2, 0x02, 0x86, 0x73, 0x34, 0xb0,
- 0xe0, 0xc0, 0x69, 0x38, 0x35, 0x65, 0x6c, 0x38, 0x66, 0x68, 0x75, 0x35,
- 0x64, 0xee, 0xe0, 0xaa, 0x31, 0xf6, 0x07, 0x06, 0x19, 0x19, 0x06, 0x1f,
- 0xb9, 0x75, 0x71, 0x38, 0xb6, 0xcf, 0x05, 0xf2, 0x04, 0x09, 0xc4, 0x55,
- 0x67, 0x67, 0x74, 0x2d, 0x78, 0xf1, 0xe0, 0x8e, 0x4d, 0x65, 0x2d, 0x65,
- 0x69, 0x6b, 0x65, 0x72, 0x2d, 0xeb, 0xc7, 0x54, 0x6c, 0x65, 0x72, 0x2d,
- 0x71, 0x6f, 0x61, 0xae, 0x03, 0xd6, 0x1a, 0x78, 0x6e, 0x2d, 0x2d, 0x73,
- 0x74, 0x66, 0x6f, 0x6c, 0x64, 0x2d, 0xb9, 0xcc, 0x67, 0x68, 0x71, 0xf5,
- 0xe0, 0xbe, 0xbf, 0xe7, 0x04, 0x06, 0x09, 0x86, 0x75, 0x34, 0x30, 0xb2,
- 0xcd, 0xc5, 0x73, 0x79, 0x2d, 0x71, 0x6f, 0x61, 0xb0, 0xc7, 0x0b, 0x61,
- 0x6e, 0x2d, 0xf1, 0xcb, 0xb5, 0x2d, 0x79, 0xe9, 0xcb, 0xed, 0xe5, 0x03,
- 0x0d, 0x9e, 0x73, 0x74, 0x76, 0x67, 0x79, 0x2d, 0x69, 0x78, 0x61, 0xb6,
- 0xe0, 0xba, 0x67, 0x72, 0x6d, 0x67, 0x65, 0x6e, 0x73, 0x62, 0x65, 0x72,
- 0x61, 0xf4, 0x02, 0x89, 0x75, 0x6e, 0x67, 0x2d, 0x70, 0xf7, 0xe0, 0xb8,
- 0x33, 0x65, 0x72, 0x2d, 0x63, 0xf4, 0xe0, 0xb8, 0x2b, 0x67, 0x72, 0x73,
- 0x68, 0x65, 0x69, 0x2d, 0xe3, 0xc9, 0x1a, 0xe1, 0x03, 0x04, 0xac, 0x72,
- 0xe4, 0xc9, 0xa0, 0x6c, 0x6c, 0xe5, 0x03, 0x02, 0x8b, 0xe4, 0x9b, 0x61,
- 0x6f, 0x73, 0x74, 0x65, 0x2d, 0x65, 0xb7, 0xe0, 0xbb, 0xb8, 0xad, 0x02,
- 0x8c, 0x64, 0x2d, 0x61, 0x6f, 0x73, 0x74, 0x65, 0x2d, 0x65, 0xe8, 0xd5,
- 0x13, 0x61, 0x6f, 0x73, 0x74, 0x65, 0x2d, 0x65, 0xe2, 0xd5, 0x09, 0x64,
- 0xf3, 0xc9, 0x70, 0xf5, 0x04, 0x08, 0x12, 0x8d, 0x75, 0x77, 0x75, 0x35,
- 0xb8, 0xe0, 0xbf, 0xfa, 0xee, 0x02, 0x86, 0x75, 0x70, 0xb4, 0xe0, 0xbf,
- 0x86, 0x6a, 0x72, 0x67, 0x61, 0x2d, 0xf2, 0xe0, 0xbb, 0xec, 0x69, 0xf3,
- 0x02, 0x84, 0x7a, 0xb3, 0xc3, 0x6a, 0x74, 0x32, 0xb2, 0xcd, 0xcb, 0x63,
- 0x30, 0xe1, 0x02, 0x85, 0x79, 0x34, 0xe1, 0xca, 0x8b, 0x74, 0x76, 0xae,
- 0x4d, 0x23, 0x60, 0x76, 0x4e, 0xde, 0xec, 0xf4, 0x09, 0x09, 0x40, 0x66,
- 0x08, 0x0f, 0x05, 0x0a, 0x86, 0x79, 0x73, 0x76, 0x72, 0x2d, 0xf6, 0xe0,
- 0xb4, 0xc7, 0xf2, 0x07, 0x07, 0x09, 0x40, 0x4b, 0xc8, 0x80, 0x6f, 0x6d,
- 0x73, 0x2d, 0xfa, 0xcb, 0xca, 0x67, 0x73, 0x74, 0x61, 0x64, 0x2d, 0xf2,
- 0xc7, 0x85, 0x65, 0x6e, 0x74, 0x69, 0xee, 0x03, 0x0e, 0x9b, 0x73, 0xe4,
- 0x03, 0xcb, 0x8b, 0x2d, 0x74, 0x69, 0x72, 0x6f, 0x6c, 0x2d, 0xb6, 0xa8,
- 0xef, 0x02, 0x99, 0x2d, 0x73, 0xe4, 0x02, 0x88, 0x74, 0x69, 0x72, 0x6f,
- 0x6c, 0x2d, 0xf3, 0xa3, 0x2d, 0x74, 0x69, 0x72, 0x6f, 0x6c, 0x2d, 0x63,
- 0xb3, 0xd4, 0x69, 0x2d, 0x73, 0xe4, 0x02, 0x8a, 0x74, 0x69, 0x72, 0x6f,
- 0x6c, 0x2d, 0x37, 0xf6, 0xd4, 0x5a, 0x2d, 0x74, 0x69, 0x72, 0x6f, 0x6c,
- 0x2d, 0x72, 0xfa, 0xd4, 0x4f, 0xe1, 0xc8, 0xf1, 0x6f, 0x72, 0x31, 0x33,
- 0xb1, 0xe0, 0xbf, 0x6b, 0xee, 0x02, 0x87, 0x73, 0x62, 0x65, 0x72, 0xe7,
- 0xc7, 0x23, 0x30, 0x61, 0xe7, 0xc9, 0xf1, 0x6a, 0x6d, 0xe5, 0xc8, 0x5e,
- 0x69, 0x71, 0x34, 0x39, 0x78, 0x71, 0xf9, 0xe0, 0xbe, 0xb4, 0x63, 0x6b,
- 0xf7, 0xe0, 0xbf, 0x7d, 0x36, 0x30, 0x62, 0xb5, 0xc2, 0x82, 0xf3, 0x0d,
- 0x26, 0x3c, 0x25, 0x07, 0x08, 0x2b, 0x04, 0x0f, 0x0c, 0x13, 0xc4, 0x5c,
- 0xf4, 0x02, 0x8c, 0x72, 0x65, 0x2d, 0x74, 0x6f, 0x74, 0x65, 0x6e, 0x2d,
- 0xfa, 0xc0, 0x6e, 0x6a, 0x72, 0x64, 0x61, 0xec, 0x02, 0x8c, 0x73, 0x68,
- 0x61, 0x6c, 0x73, 0x65, 0x6e, 0x2d, 0xf3, 0xe0, 0x30, 0x9e, 0x2d, 0xf3,
- 0xc6, 0xd1, 0xf2, 0x04, 0x04, 0x07, 0x86, 0x75, 0xed, 0xca, 0x4a, 0x72,
- 0x65, 0x69, 0x73, 0xe1, 0xc6, 0xbf, 0x66, 0x6f, 0x6c, 0xe4, 0xc6, 0x10,
- 0xad, 0x04, 0x0c, 0x06, 0x85, 0x76, 0x61, 0x72, 0x61, 0x6e, 0x67, 0x65,
- 0x72, 0x2d, 0xe7, 0xc0, 0xa4, 0x6f, 0x64, 0x61, 0xec, 0xc6, 0xa2, 0x66,
- 0x72, 0xef, 0xc4, 0xd8, 0x61, 0x75, 0x72, 0x64, 0x61, 0x6c, 0x2d, 0xec,
- 0xc5, 0x4a, 0xee, 0x04, 0x06, 0x04, 0x8e, 0x73, 0x61, 0x2d, 0xf2, 0xc9,
- 0xd9, 0x65, 0xf3, 0xc1, 0x4d, 0x64, 0x72, 0x65, 0x2d, 0x6c, 0x61, 0x6e,
- 0x64, 0x2d, 0x30, 0xe3, 0xe0, 0x30, 0x42, 0x61, 0x73, 0x65, 0x2d, 0xee,
- 0xe0, 0xb3, 0x9a, 0xed, 0x03, 0xc8, 0x8e, 0xec, 0xc7, 0xab, 0xec, 0x03,
- 0xc9, 0xeb, 0x61, 0xf4, 0xc0, 0xd5, 0xeb, 0x03, 0x11, 0x8d, 0xee, 0x02,
- 0x88, 0x6c, 0x61, 0x6e, 0x64, 0x2d, 0xe6, 0xca, 0x33, 0x69, 0x74, 0x2d,
- 0xf9, 0xc9, 0xf9, 0xea, 0x02, 0x85, 0x6b, 0x2d, 0xf3, 0xc9, 0x93, 0x65,
- 0x72, 0xf6, 0xc0, 0xef, 0x69, 0x65, 0x72, 0x76, 0x2d, 0xf5, 0xe0, 0xba,
- 0x51, 0x67, 0xee, 0xc7, 0x81, 0xe5, 0x02, 0x87, 0x73, 0x35, 0x35, 0xb4,
- 0xe0, 0xbf, 0x74, 0x72, 0x61, 0xec, 0xc7, 0xb9, 0x64, 0x74, 0x69, 0x72,
- 0x6f, 0x6c, 0x2d, 0x6e, 0xb2, 0xe0, 0xb9, 0xc2, 0x61, 0x6e, 0xe4, 0x03,
- 0xca, 0x48, 0x6e, 0x65, 0x73, 0x73, 0x6a, 0x65, 0x6e, 0x2d, 0x6f, 0xe7,
- 0xe0, 0x2f, 0xce, 0x39, 0xe2, 0xcc, 0x17, 0xf2, 0x0d, 0x10, 0x0b, 0x16,
- 0x05, 0x08, 0x06, 0x07, 0x11, 0x0d, 0x32, 0x0a, 0x96, 0xf9, 0x02, 0x86,
- 0x72, 0x76, 0x69, 0xeb, 0xc5, 0x3e, 0x6b, 0x65, 0x6e, 0x2d, 0xf6, 0xca,
- 0x1e, 0x76, 0x63, 0x31, 0x65, 0x30, 0x61, 0x6d, 0xb3, 0xe0, 0xbe, 0x49,
- 0xf3, 0x02, 0x8e, 0xf4, 0x02, 0x86, 0x61, 0x2d, 0xe6, 0xe0, 0xb2, 0xf5,
- 0x2d, 0xb0, 0xe0, 0xb5, 0x39, 0x6b, 0x6f, 0xe7, 0xc4, 0x4b, 0x72, 0x6f,
- 0xf3, 0xc9, 0x3e, 0x6f, 0x76, 0x75, 0x38, 0xb8, 0xe0, 0xb5, 0xad, 0x6e,
- 0x79, 0x33, 0xb1, 0xcb, 0xb5, 0x6d, 0x73, 0x6b, 0x6f, 0xe7, 0xc4, 0xfc,
- 0xec, 0x02, 0x89, 0x69, 0x6e, 0x67, 0x65, 0x6e, 0x2d, 0xed, 0xc9, 0x7c,
- 0x61, 0x6e, 0xe4, 0xc4, 0x20, 0x69, 0xf3, 0x02, 0x83, 0xf2, 0xc9, 0xf4,
- 0x61, 0x2d, 0xb5, 0xe0, 0xb4, 0xfc, 0xe8, 0x04, 0x12, 0x07, 0x88, 0xf4,
- 0x03, 0x05, 0x83, 0x36, 0xb1, 0xe0, 0xba, 0xcc, 0xb3, 0xcb, 0xb2, 0x32,
- 0x37, 0xfa, 0xe0, 0xbd, 0xaf, 0x71, 0x76, 0x39, 0xb6, 0xe0, 0xbe, 0xb7,
- 0x6f, 0x6c, 0x74, 0x2d, 0xed, 0xe0, 0xb2, 0x8d, 0x6b, 0x6b, 0x65, 0x72,
- 0x76, 0x6a, 0x75, 0x2d, 0x30, 0xb1, 0xc1, 0x81, 0x65, 0x6e, 0x6e, 0x65,
- 0x73, 0x79, 0x2d, 0xf6, 0xc5, 0x4d, 0xe4, 0x03, 0x06, 0x86, 0x79, 0x2d,
- 0x30, 0xee, 0xc8, 0xcf, 0x65, 0x2d, 0xf5, 0xe0, 0x3e, 0x96, 0x61, 0x6c,
- 0x2d, 0xf0, 0xc8, 0x86, 0xe1, 0xc9, 0x97, 0xf1, 0x05, 0x07, 0x07, 0x08,
- 0x86, 0x78, 0xe1, 0x40, 0xa7, 0xe0, 0xbc, 0x14, 0x71, 0x71, 0x74, 0x31,
- 0xb1, 0xc8, 0x2c, 0x63, 0x6b, 0x61, 0x31, 0xf0, 0xe0, 0xb3, 0x20, 0x39,
- 0x6a, 0x79, 0xe2, 0xc9, 0x1b, 0x37, 0x63, 0xe5, 0xc0, 0x8a, 0xf0, 0x04,
- 0x0e, 0x0e, 0x88, 0x73, 0xf3, 0x02, 0x83, 0xf9, 0xc9, 0x79, 0x75, 0x33,
- 0x33, 0xec, 0xe0, 0xbd, 0x37, 0x6f, 0x72, 0x73, 0x67, 0x75, 0x2d, 0x73,
- 0x74, 0x61, 0x32, 0xb6, 0xe0, 0x7f, 0x00, 0x67, 0x62, 0x73, 0x30, 0xe4,
- 0xe0, 0xbd, 0x26, 0x31, 0xe1, 0x60, 0x25, 0x51, 0xe0, 0x97, 0x5b, 0xef,
- 0x07, 0x08, 0x0b, 0x0a, 0x05, 0x0e, 0x8a, 0x74, 0x75, 0x37, 0x39, 0xb6,
- 0xe0, 0xbc, 0x64, 0xf3, 0x02, 0x85, 0x79, 0x72, 0xef, 0xc6, 0xe3, 0xf4,
- 0xc4, 0x61, 0x70, 0x70, 0x65, 0x67, 0x72, 0x64, 0x2d, 0xe9, 0xc8, 0x98,
- 0x67, 0x62, 0xf0, 0xc1, 0x76, 0x64, 0x30, 0xe1, 0x02, 0x85, 0x71, 0x33,
- 0xe2, 0xc7, 0x79, 0x6c, 0xe7, 0xca, 0x0a, 0x33, 0xe3, 0x03, 0xcb, 0x14,
- 0x79, 0x78, 0xb2, 0xca, 0xf8, 0x31, 0x61, 0xe3, 0x49, 0x30, 0xad, 0xee,
- 0x0a, 0x08, 0x0c, 0x1f, 0x0a, 0x10, 0x04, 0x08, 0x12, 0x87, 0x79, 0x71,
- 0x79, 0x32, 0xb6, 0xe0, 0xbc, 0xc8, 0x76, 0x75, 0x6f, 0x74, 0x6e, 0x61,
- 0x2d, 0x68, 0xf7, 0xe0, 0xb8, 0x91, 0xf4, 0x02, 0x8b, 0x74, 0x65, 0x72,
- 0x79, 0x2d, 0x62, 0x79, 0xe1, 0xe0, 0xbc, 0xbf, 0xf3, 0x02, 0x87, 0x71,
- 0x31, 0x37, 0xe7, 0xe0, 0xbc, 0x9a, 0x6f, 0x30, 0x69, 0x71, 0xf8, 0xc6,
- 0x23, 0x72, 0x79, 0x2d, 0x79, 0x6c, 0x61, 0xb5, 0xe0, 0xb7, 0x8f, 0x71,
- 0x76, 0x37, 0xe6, 0x04, 0xe0, 0xbd, 0x8e, 0x73, 0x30, 0x30, 0x65, 0xed,
- 0xe0, 0xbc, 0x83, 0xef, 0xe0, 0x7d, 0xe5, 0x6e, 0x78, 0x33, 0x38, 0xb8,
- 0xe0, 0xbc, 0x77, 0x6d, 0x65, 0x73, 0x6a, 0x65, 0x76, 0x75, 0x65, 0x6d,
- 0x69, 0x65, 0x2d, 0x74, 0x63, 0xe2, 0xe0, 0xb8, 0x3a, 0x69, 0x74, 0x32,
- 0x32, 0xb5, 0xc9, 0xb6, 0x67, 0xe2, 0x03, 0x04, 0x85, 0xf2, 0xe0, 0xb6,
- 0x9c, 0x65, 0x39, 0xe5, 0xc8, 0x7a, 0x63, 0x35, 0x61, 0xfa, 0xe0, 0xbb,
- 0x9a, 0xed, 0x0d, 0x0a, 0x05, 0x11, 0x0a, 0x21, 0x1a, 0x16, 0x0b, 0x10,
- 0x41, 0x00, 0x8e, 0x78, 0x74, 0x71, 0x31, 0xed, 0x44, 0xab, 0xe0, 0xb8,
- 0x8e, 0x75, 0x6f, 0xf3, 0xc7, 0x8f, 0x74, 0x74, 0x61, 0x2d, 0x76, 0x72,
- 0x6a, 0x6a, 0x61, 0x74, 0x2d, 0x6b, 0x37, 0xe1, 0xe0, 0x7d, 0xe8, 0x73,
- 0x79, 0x2d, 0x75, 0x6c, 0x61, 0xb0, 0xe0, 0x7e, 0x05, 0xef, 0x03, 0x02,
- 0x89, 0xf4, 0xa7, 0x73, 0x6a, 0x65, 0x6e, 0x2d, 0xe5, 0xe0, 0x85, 0xda,
- 0xf2, 0x02, 0x88, 0x69, 0x2d, 0x71, 0x73, 0xe1, 0xe0, 0x82, 0xd7, 0x65,
- 0x6b, 0x65, 0x2d, 0xea, 0xc7, 0xe5, 0xec, 0x03, 0x06, 0x86, 0x73, 0x65,
- 0x6c, 0xf6, 0xc2, 0xb3, 0x69, 0x2d, 0xf4, 0xe0, 0x3c, 0xf2, 0x61, 0x74,
- 0x76, 0x75, 0x6f, 0x70, 0x6d, 0xe9, 0xc7, 0x63, 0xeb, 0x03, 0x07, 0x86,
- 0x72, 0x75, 0x34, 0xb5, 0xe0, 0xbb, 0x5d, 0x31, 0x62, 0x75, 0xb4, 0xc7,
- 0x87, 0x30, 0x61, 0xf8, 0xc4, 0x40, 0x6a, 0x6e, 0x64, 0x61, 0x6c, 0x65,
- 0x6e, 0x2d, 0xb6, 0xc7, 0x5d, 0x69, 0xf8, 0x02, 0x86, 0x38, 0x39, 0xb1,
- 0xe0, 0xb2, 0x8e, 0x30, 0x38, 0xb2, 0xe0, 0xb2, 0x88, 0x67, 0xe2, 0x0d,
- 0x08, 0x15, 0x14, 0x06, 0x09, 0x05, 0x10, 0x1c, 0x0b, 0x40, 0x68, 0x87,
- 0x78, 0x34, 0x63, 0x64, 0xb0, 0xe0, 0x32, 0xac, 0xf4, 0x03, 0x05, 0x86,
- 0x78, 0xb2, 0xe0, 0xb3, 0x37, 0x66, 0x38, 0xe6, 0xe0, 0xba, 0xcd, 0x33,
- 0x64, 0xe8, 0xe0, 0xba, 0xca, 0x71, 0x6c, 0x79, 0x37, 0xe3, 0x02, 0x85,
- 0x76, 0xe1, 0xe0, 0xb9, 0x49, 0x30, 0x61, 0x36, 0x37, 0xe6, 0xe0, 0x74,
- 0xb2, 0x70, 0x6c, 0xb2, 0xe0, 0x63, 0xd0, 0x69, 0x34, 0x65, 0x63, 0x65,
- 0xf8, 0xe0, 0xbb, 0x60, 0x67, 0x75, 0xb8, 0xc7, 0x8b, 0x65, 0x72, 0x70,
- 0x34, 0x61, 0x35, 0x64, 0x34, 0xe1, 0x04, 0xe0, 0xbb, 0x90, 0xb8, 0xc6,
- 0x0d, 0xe3, 0x03, 0x08, 0x87, 0x70, 0x71, 0x36, 0x67, 0x70, 0xe1, 0xc7,
- 0xd7, 0x61, 0x37, 0x64, 0xfa, 0xe0, 0xba, 0xd3, 0x30, 0x61, 0x39, 0x61,
- 0x7a, 0xe3, 0xe0, 0xbc, 0x2a, 0x62, 0x68, 0x31, 0xe1, 0x04, 0xe0, 0xbc,
- 0x20, 0xb7, 0xc4, 0x4d, 0xe1, 0x07, 0x07, 0x16, 0x0a, 0x06, 0x10, 0x88,
- 0x79, 0x68, 0x37, 0xe7, 0xe0, 0xba, 0xb4, 0x69, 0x39, 0xe1, 0x02, 0x88,
- 0x7a, 0x67, 0x71, 0x70, 0xb6, 0xe0, 0xba, 0x5a, 0x35, 0x65, 0x76, 0x61,
- 0x30, 0xb0, 0xe0, 0xb2, 0xa7, 0x68, 0x31, 0x61, 0x33, 0x68, 0x6a, 0xeb,
- 0xe0, 0xad, 0x12, 0x62, 0x32, 0xe2, 0xe0, 0xba, 0x36, 0xe1, 0x02, 0x85,
- 0x6d, 0x37, 0xe1, 0xc4, 0x8f, 0x6b, 0x63, 0x37, 0x64, 0xf6, 0xe0, 0xb1,
- 0xb8, 0x37, 0x63, 0x30, 0x62, 0x62, 0xee, 0xc6, 0xf7, 0x33, 0xe1, 0x02,
- 0x91, 0x34, 0xe6, 0x02, 0x82, 0xf2, 0x83, 0x31, 0x36, 0xe1, 0x04, 0xe0,
- 0xbb, 0xbf, 0xae, 0xe0, 0x72, 0xb0, 0x33, 0x65, 0xea, 0xe0, 0xba, 0x94,
- 0x39, 0x61, 0x77, 0xe2, 0xe0, 0xb1, 0x8e, 0x32, 0x64, 0xe4, 0xe0, 0xb0,
- 0x51, 0xe5, 0x02, 0x88, 0x72, 0x6b, 0x65, 0x72, 0x2d, 0xeb, 0xc6, 0x8f,
- 0xec, 0xc6, 0xb4, 0x33, 0x63, 0x68, 0x30, 0x6a, 0xb3, 0xc8, 0xa6, 0xec,
- 0x0e, 0x03, 0x0c, 0x13, 0x05, 0x07, 0x09, 0x06, 0x15, 0x19, 0x08, 0x08,
- 0x12, 0x84, 0xf5, 0xc4, 0x4e, 0xf4, 0x02, 0x84, 0x65, 0xee, 0xc5, 0xb1,
- 0x2d, 0x6c, 0xe9, 0xc5, 0xe0, 0xf2, 0x02, 0x88, 0x65, 0x6e, 0x73, 0x6b,
- 0x6f, 0xe7, 0xc1, 0xf0, 0x64, 0x61, 0x6c, 0x2d, 0xf3, 0xe0, 0xaf, 0x41,
- 0x6f, 0x61, 0xe2, 0xc5, 0xbc, 0x6e, 0x73, 0x2d, 0xf1, 0xe0, 0x86, 0x48,
- 0x69, 0x6e, 0x64, 0x73, 0x2d, 0xf0, 0xe0, 0xaf, 0x2c, 0x68, 0x70, 0x70,
- 0xe9, 0xc2, 0x4b, 0xe7, 0x02, 0x87, 0x72, 0x64, 0x2d, 0x70, 0xef, 0xc5,
- 0xa8, 0x62, 0x62, 0x61, 0x74, 0x31, 0x61, 0x64, 0xb8, 0xe0, 0xb9, 0x8a,
- 0xe5, 0x02, 0x88, 0x73, 0x75, 0x6e, 0x64, 0x2d, 0xe8, 0xc6, 0x18, 0x61,
- 0x67, 0x61, 0x76, 0x69, 0x69, 0x6b, 0x61, 0x2d, 0x35, 0xb2, 0xe0, 0x2b,
- 0x98, 0x64, 0x69, 0x6e, 0x67, 0x65, 0xee, 0xc1, 0xc4, 0x63, 0x76, 0x72,
- 0x33, 0x32, 0xe4, 0xc4, 0x8f, 0xe1, 0x02, 0x84, 0x6e, 0xe7, 0xc5, 0x93,
- 0x68, 0x65, 0x61, 0x64, 0x6a, 0x75, 0x2d, 0xb7, 0xe0, 0x83, 0xcb, 0x31,
- 0xe1, 0xc7, 0x2e, 0x2d, 0xb1, 0xc2, 0x29, 0xeb, 0x0b, 0x1c, 0x08, 0x3f,
- 0x16, 0x0e, 0x1e, 0x09, 0x0c, 0x07, 0x85, 0xf6, 0x03, 0x0a, 0x85, 0x6e,
- 0x61, 0x6e, 0x67, 0x65, 0x6e, 0x2d, 0xeb, 0xc2, 0x7c, 0x69, 0x74, 0xf3,
- 0xc5, 0x24, 0x66, 0x6a, 0x6f, 0x72, 0x64, 0x2d, 0xee, 0xc5, 0x5d, 0x73,
- 0x6e, 0x65, 0x73, 0x2d, 0xf5, 0xc5, 0xb0, 0xf2, 0x04, 0x10, 0x09, 0x8d,
- 0x6a, 0x6f, 0x68, 0x6b, 0x61, 0x2d, 0x68, 0x77, 0x61, 0x62, 0x34, 0x39,
- 0xea, 0xe0, 0xb9, 0xb8, 0x65, 0x68, 0x61, 0x6d, 0x6e, 0x2d, 0xe4, 0xc5,
- 0x37, 0x64, 0x73, 0x68, 0x65, 0x72, 0x61, 0x64, 0x2d, 0x6d, 0xb8, 0xe0,
- 0xb5, 0x65, 0xe1, 0x02, 0x89, 0x6e, 0x67, 0x68, 0x6b, 0x65, 0x2d, 0xe2,
- 0xc2, 0x2f, 0x67, 0x65, 0x72, 0x2d, 0xe7, 0xe0, 0x83, 0x54, 0xf0, 0x02,
- 0x86, 0x75, 0x74, 0xb3, 0xe0, 0xb8, 0xf9, 0xf2, 0x02, 0x86, 0x79, 0x35,
- 0xb7, 0xe0, 0xb8, 0xbb, 0x77, 0xb1, 0xc7, 0x9f, 0x6f, 0x6c, 0x75, 0x6f,
- 0x6b, 0x74, 0x61, 0x2d, 0x37, 0x79, 0x61, 0xb5, 0xc2, 0x13, 0xec, 0x02,
- 0x97, 0xf4, 0x04, 0x06, 0x05, 0x82, 0x79, 0x35, 0xf8, 0xe0, 0xb9, 0x40,
- 0x78, 0xb9, 0xe0, 0xb9, 0x15, 0xf0, 0x83, 0x37, 0x38, 0xb7, 0xc7, 0x31,
- 0x62, 0xf5, 0xc2, 0x3d, 0x66, 0x6a, 0x6f, 0x72, 0x64, 0x2d, 0xe9, 0xc5,
- 0x26, 0x63, 0x72, 0x78, 0x37, 0x37, 0x64, 0x31, 0x78, 0xb4, 0xe0, 0xb9,
- 0x25, 0x62, 0x72, 0x71, 0xb7, 0xe0, 0xb9, 0x13, 0x61, 0x72, 0xed, 0xc5,
- 0x0b, 0x37, 0x79, 0x6e, 0xb9, 0xc6, 0xed, 0xea, 0x06, 0x08, 0x09, 0x20,
- 0xc6, 0x05, 0x76, 0x72, 0x31, 0x38, 0xb9, 0xe0, 0xb8, 0x4f, 0x72, 0x70,
- 0x65, 0x6c, 0x61, 0x6e, 0xe4, 0xc0, 0x85, 0xec, 0x02, 0x89, 0x73, 0x74,
- 0x65, 0x72, 0x2d, 0xe2, 0xe0, 0x82, 0xc7, 0xf1, 0x02, 0x89, 0x36, 0x31,
- 0x75, 0x39, 0x77, 0xb7, 0xe0, 0xb0, 0x95, 0x34, 0x38, 0x30, 0x6e, 0xb2,
- 0xe0, 0xa6, 0x0c, 0x31, 0xe1, 0x03, 0x04, 0x8e, 0xed, 0xe0, 0xb8, 0xcd,
- 0xe5, 0x02, 0x85, 0x6c, 0x38, 0xe2, 0xc6, 0x4f, 0xe6, 0x46, 0x4c, 0xe0,
- 0xb3, 0x80, 0x64, 0xf0, 0xc6, 0x45, 0xe9, 0x04, 0x10, 0x06, 0x88, 0x6f,
- 0x30, 0x61, 0x37, 0xe9, 0x04, 0xe0, 0xb9, 0xb5, 0xae, 0x60, 0x7c, 0x24,
- 0xe0, 0x39, 0xb1, 0x6e, 0x64, 0x65, 0xf2, 0xc3, 0xfd, 0x6d, 0x72, 0x35,
- 0x31, 0xb3, 0xe0, 0xb8, 0x05, 0x31, 0x62, 0x36, 0x62, 0x31, 0x61, 0x36,
- 0x61, 0xb2, 0xe0, 0xb8, 0xbc, 0xe8, 0x0f, 0x13, 0x08, 0x05, 0x10, 0x0c,
- 0x0e, 0x0d, 0x0e, 0x0e, 0x10, 0x08, 0x0a, 0x14, 0x92, 0xf9, 0x02, 0x8a,
- 0x6c, 0x61, 0x6e, 0x64, 0x65, 0x74, 0x2d, 0xb5, 0xc4, 0x1c, 0x61, 0x6e,
- 0x67, 0x65, 0xf2, 0xa4, 0x78, 0x74, 0x38, 0x31, 0xb4, 0xe0, 0xb8, 0x91,
- 0x70, 0x6d, 0xe9, 0xc0, 0x68, 0xef, 0x02, 0x89, 0x6c, 0x74, 0x6c, 0x65,
- 0x6e, 0x2d, 0xe8, 0xc3, 0xef, 0x62, 0xec, 0xc4, 0x6f, 0x6e, 0x65, 0x66,
- 0x6f, 0x73, 0x73, 0x2d, 0x71, 0xb1, 0xe0, 0xb4, 0x1a, 0x6d, 0x6d, 0x72,
- 0x66, 0x65, 0x61, 0x73, 0x74, 0x61, 0x2d, 0x73, 0xb4, 0xc3, 0xa6, 0x6b,
- 0x6b, 0x69, 0x6e, 0x65, 0x6e, 0x2d, 0x35, 0x77, 0xe1, 0xe0, 0x92, 0x88,
- 0x67, 0x65, 0x62, 0x6f, 0x73, 0x74, 0x61, 0x64, 0x2d, 0x67, 0xb3, 0xe0,
- 0xb3, 0xf1, 0x65, 0x72, 0x79, 0x2d, 0x69, 0x72, 0x61, 0xae, 0x60, 0x29,
- 0x7f, 0xe0, 0x7b, 0x69, 0x63, 0x65, 0x73, 0x75, 0x6f, 0x6c, 0x6f, 0x2d,
- 0x37, 0x79, 0x61, 0x33, 0xb5, 0xe0, 0x29, 0x81, 0x62, 0x6d, 0x65, 0x72,
- 0x2d, 0xf8, 0xc3, 0x5c, 0x33, 0x63, 0x75, 0x7a, 0x6b, 0x31, 0x64, 0xe9,
- 0xc6, 0x14, 0x32, 0x62, 0xf2, 0x02, 0x88, 0x6a, 0x39, 0xe3, 0x40, 0xb1,
- 0xe0, 0xb8, 0x36, 0x65, 0x67, 0x33, 0xe5, 0xe0, 0xae, 0xb7, 0x31, 0xe1,
- 0x03, 0x05, 0x84, 0x6c, 0x69, 0xfa, 0xc5, 0x53, 0x68, 0xee, 0xc5, 0x4f,
- 0xe5, 0xe0, 0x35, 0x25, 0x2d, 0x32, 0xe6, 0xe0, 0xb3, 0x95, 0xe7, 0x0a,
- 0x23, 0x13, 0x07, 0x06, 0x05, 0x1a, 0x10, 0x03, 0x89, 0x6e, 0x73, 0x74,
- 0x69, 0xe7, 0x02, 0x8d, 0x6c, 0x69, 0x65, 0x66, 0x65, 0x72, 0x6e, 0x2d,
- 0x77, 0xef, 0xe0, 0x6e, 0x62, 0x62, 0x65, 0x73, 0x74, 0x65, 0x6c, 0x6c,
- 0x65, 0x6e, 0x2d, 0x7a, 0xf6, 0xe0, 0x6e, 0x53, 0x6d, 0xf1, 0x02, 0x89,
- 0x77, 0x35, 0x61, 0xae, 0x44, 0xaf, 0xe0, 0x76, 0x4e, 0x30, 0x35, 0x30,
- 0xe9, 0xc2, 0x09, 0x6c, 0x73, 0x2d, 0x65, 0xec, 0xc2, 0xe7, 0x6b, 0x33,
- 0x61, 0xf4, 0xc0, 0xa5, 0x6a, 0x76, 0xe9, 0xc1, 0x44, 0xe9, 0x02, 0x8b,
- 0x76, 0x75, 0x6f, 0x74, 0x6e, 0x61, 0x2d, 0xb8, 0xe0, 0x81, 0x37, 0x6c,
- 0x64, 0x65, 0x73, 0x6b, 0x6c, 0x2d, 0x67, 0xb0, 0xe0, 0xb3, 0x28, 0x67,
- 0x61, 0x76, 0x69, 0x69, 0x6b, 0x61, 0x2d, 0x38, 0x79, 0x61, 0x34, 0xb7,
- 0xe0, 0x79, 0x37, 0xe5, 0xc5, 0x0f, 0x63, 0x6b, 0x72, 0x33, 0x66, 0xb0,
- 0xe0, 0xae, 0x1a, 0x32, 0x78, 0x78, 0x34, 0xb8, 0xe0, 0xac, 0xf1, 0xe6,
- 0x0a, 0x18, 0x15, 0x07, 0x1a, 0x18, 0x10, 0x22, 0x04, 0x88, 0xfa, 0x02,
- 0x8c, 0x79, 0x73, 0x38, 0x64, 0x36, 0x39, 0x75, 0x76, 0xe7, 0xe0, 0xb6,
- 0x60, 0x63, 0x32, 0x63, 0x39, 0x65, 0xb2, 0xe0, 0xac, 0xce, 0xf2, 0x03,
- 0x07, 0x86, 0x79, 0x61, 0x2d, 0xe8, 0xe0, 0xab, 0xe4, 0x6e, 0x61, 0x2d,
- 0xf7, 0xc2, 0x03, 0x64, 0xe5, 0xc2, 0x32, 0x70, 0x63, 0x72, 0x6a, 0xb9,
- 0xc5, 0x28, 0x6f, 0x72, 0xec, 0x02, 0x8a, 0x63, 0x65, 0x73, 0x65, 0x6e,
- 0x61, 0x2d, 0xe3, 0xc1, 0x82, 0x2d, 0x63, 0x65, 0x73, 0x65, 0x6e, 0x61,
- 0x2d, 0xe6, 0xc1, 0x82, 0xec, 0x03, 0x07, 0x87, 0x77, 0x33, 0x35, 0xb1,
- 0xe0, 0xb6, 0xf6, 0x6f, 0x72, 0x2d, 0xea, 0xe0, 0xab, 0xa7, 0x2d, 0x7a,
- 0xe9, 0xe0, 0xb2, 0x93, 0xea, 0x02, 0x85, 0x71, 0x37, 0xb2, 0xc2, 0xe0,
- 0x6f, 0x72, 0x64, 0x2d, 0xec, 0xe0, 0xab, 0x91, 0xe9, 0x02, 0x9b, 0xf1,
- 0x04, 0x05, 0x05, 0x83, 0x7a, 0xb9, 0xe0, 0xb5, 0xd4, 0x73, 0xb8, 0xe0,
- 0xb5, 0xcf, 0xb6, 0xc0, 0xd5, 0x32, 0x32, 0x38, 0x63, 0x35, 0xe8, 0xe0,
- 0xb5, 0xc3, 0x6e, 0xee, 0xc2, 0x7e, 0x68, 0xe2, 0xd8, 0xa1, 0x63, 0x74,
- 0x34, 0x32, 0xb9, 0xe0, 0xb5, 0xb6, 0x36, 0x71, 0x78, 0x35, 0xb3, 0xe0,
- 0xb6, 0x48, 0xe5, 0x06, 0x0d, 0x07, 0x07, 0x0e, 0x8a, 0x76, 0x65, 0x6e,
- 0x69, 0x2d, 0x30, 0x71, 0x61, 0x30, 0xb1, 0xe0, 0x91, 0xc8, 0x6c, 0x71,
- 0x71, 0x31, 0xb6, 0xc4, 0x1d, 0x68, 0x71, 0x7a, 0x35, 0xb6, 0xc4, 0x9b,
- 0x66, 0xf6, 0x02, 0x86, 0x79, 0x38, 0xb8, 0xe0, 0xb6, 0x47, 0x6e, 0xb9,
- 0xc3, 0x95, 0x63, 0x6b, 0x76, 0x64, 0x74, 0x63, 0xb9, 0xe0, 0xb5, 0x8f,
- 0x31, 0xe1, 0xc1, 0xfc, 0xe4, 0x07, 0x04, 0x08, 0x05, 0x12, 0x0b, 0x8a,
- 0x79, 0xf2, 0xc2, 0x4b, 0x72, 0x62, 0x61, 0x6b, 0x2d, 0xf7, 0xc2, 0x1b,
- 0x6e, 0x6e, 0xe1, 0xc1, 0x5c, 0xea, 0x02, 0x84, 0x74, 0xf9, 0xc3, 0x1d,
- 0x72, 0x73, 0x37, 0x32, 0x64, 0x36, 0x75, 0xf9, 0xe0, 0xb6, 0x05, 0x61,
- 0x76, 0x76, 0x65, 0x6e, 0x6a, 0x72, 0x67, 0xe1, 0xc1, 0xaa, 0x35, 0x71,
- 0x76, 0x37, 0x7a, 0x38, 0x37, 0xb6, 0xc3, 0x03, 0x31, 0xe1, 0x03, 0x03,
- 0x84, 0xf4, 0xc2, 0x73, 0xec, 0xe0, 0xac, 0xd1, 0x63, 0xea, 0xc0, 0xa7,
- 0xe3, 0x0a, 0x1d, 0x33, 0x13, 0x08, 0x06, 0x1b, 0x12, 0x08, 0x87, 0x7a,
- 0xf2, 0x04, 0x07, 0x05, 0x85, 0x77, 0x32, 0x38, 0xe2, 0xe0, 0x8b, 0x19,
- 0x75, 0xb2, 0xe0, 0xb5, 0x1e, 0x73, 0xb0, 0xe0, 0xb5, 0xaa, 0x36, 0x39,
- 0xb4, 0xe0, 0xad, 0x74, 0xef, 0x02, 0xa0, 0x72, 0x72, 0x65, 0x69, 0x6f,
- 0x73, 0x2d, 0x65, 0x2d, 0x74, 0x65, 0x6c, 0x65, 0x63, 0x6f, 0x6d, 0x75,
- 0x6e, 0x69, 0x63, 0x61, 0x65, 0x73, 0x2d, 0x67, 0x68, 0x63, 0x32, 0xb9,
- 0xe0, 0xb1, 0x2c, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x65, 0x73, 0x2d,
- 0x76, 0x36, 0x61, 0xb2, 0xe0, 0xac, 0x8e, 0x6c, 0x63, 0x68, 0x63, 0x30,
- 0x65, 0x61, 0x30, 0x62, 0x32, 0x67, 0x32, 0x61, 0x39, 0x67, 0xe3, 0xe0,
- 0xb4, 0xcd, 0x69, 0x71, 0x70, 0x6e, 0xae, 0xe0, 0x78, 0xea, 0x67, 0x34,
- 0xe2, 0xe0, 0xa4, 0x49, 0x65, 0x73, 0x65, 0x6e, 0xe1, 0x02, 0x8a, 0x66,
- 0x6f, 0x72, 0x6c, 0x2d, 0x69, 0xb8, 0xe0, 0xb0, 0xc1, 0x2d, 0x66, 0x6f,
- 0x72, 0x6c, 0x2d, 0x6d, 0xe3, 0xca, 0x21, 0x63, 0xeb, 0x02, 0x88, 0x77,
- 0x63, 0x78, 0x65, 0xf4, 0xe0, 0xb4, 0x98, 0x32, 0x62, 0xb3, 0xe0, 0xac,
- 0xf3, 0x33, 0x73, 0x31, 0x34, 0xed, 0xe0, 0xb5, 0x2f, 0x32, 0x62, 0x72,
- 0xb7, 0xe0, 0xb6, 0x37, 0x31, 0x61, 0x76, 0xe7, 0x04, 0xe0, 0xb6, 0x2d,
- 0x2e, 0x78, 0x6e, 0x2d, 0xad, 0x41, 0xa9, 0xc1, 0x04, 0xe2, 0x0f, 0x03,
- 0x0d, 0x1e, 0x19, 0x08, 0x09, 0x15, 0x16, 0x1c, 0x1c, 0x0c, 0x0e, 0x12,
- 0x8a, 0xf5, 0xc0, 0xd5, 0x74, 0x73, 0x66, 0x6a, 0x6f, 0x72, 0x64, 0x2d,
- 0x39, 0xfa, 0xe0, 0xb0, 0xd6, 0xf2, 0x02, 0x88, 0x75, 0x6d, 0x2d, 0x76,
- 0xef, 0xe0, 0xb0, 0xcb, 0x6e, 0x6e, 0xf9, 0x02, 0x89, 0x73, 0x75, 0x6e,
- 0x64, 0x2d, 0x6d, 0xb8, 0xc0, 0x57, 0x2d, 0x77, 0xf5, 0xc0, 0x52, 0xef,
- 0x02, 0x90, 0x7a, 0x65, 0x6e, 0x2d, 0x73, 0x64, 0x74, 0x69, 0x72, 0x6f,
- 0x6c, 0x2d, 0x32, 0xef, 0xc9, 0x9e, 0x64, 0x2d, 0xb2, 0xe0, 0xab, 0xf6,
- 0x6d, 0x6c, 0x6f, 0x2d, 0xe7, 0xe0, 0xa9, 0xa5, 0x6c, 0x74, 0x2d, 0x65,
- 0x6c, 0xe1, 0xe0, 0x26, 0x3c, 0xea, 0x02, 0x89, 0x64, 0x64, 0x61, 0x72,
- 0x2d, 0xf0, 0xe0, 0xb0, 0x81, 0x61, 0x72, 0x6b, 0x79, 0x2d, 0xe6, 0xe0,
- 0x7e, 0x7c, 0xe9, 0x02, 0x89, 0x65, 0x76, 0x74, 0x2d, 0x30, 0xf1, 0xe0,
- 0xb0, 0x6d, 0x64, 0x72, 0x2d, 0x35, 0x6e, 0x61, 0xe3, 0xe0, 0xb4, 0xa0,
- 0x68, 0xe3, 0x02, 0x8e, 0x63, 0x61, 0x76, 0x75, 0x6f, 0x74, 0x6e, 0x61,
- 0x2d, 0x6b, 0xb7, 0xe0, 0xb0, 0x51, 0x61, 0x76, 0x75, 0x6f, 0x74, 0x6e,
- 0x61, 0x2d, 0xf3, 0x99, 0xe5, 0x02, 0x8b, 0x72, 0x6c, 0x65, 0x76, 0x67,
- 0x2d, 0x6a, 0xf8, 0xe0, 0xb0, 0x39, 0x61, 0x72, 0x61, 0x6c, 0x76, 0x68,
- 0x6b, 0x69, 0x2d, 0x79, 0xb4, 0xe0, 0xb0, 0x2b, 0x64, 0x64, 0x64, 0x6a,
- 0x2d, 0x6d, 0x72, 0x61, 0xe2, 0xe0, 0xb1, 0xf5, 0x63, 0x6b, 0x31, 0x62,
- 0x39, 0x61, 0x35, 0x64, 0x72, 0x65, 0xb4, 0xe0, 0xa9, 0xfe, 0x61, 0x6c,
- 0x73, 0x61, 0x6e, 0x2d, 0x73, 0x64, 0x74, 0x69, 0x72, 0x6f, 0x6c, 0x2d,
- 0x6e, 0xf3, 0xc8, 0xf8, 0x34, 0x77, 0x36, 0x30, 0x35, 0x66, 0xe5, 0xe0,
- 0xa6, 0x47, 0x2d, 0x35, 0x67, 0xe1, 0xe0, 0x9e, 0x05, 0xe1, 0x05, 0x0a,
- 0x12, 0x03, 0x8d, 0x76, 0x65, 0x72, 0x79, 0x2d, 0x79, 0xf5, 0xe0, 0xaf,
- 0xde, 0x75, 0x72, 0x73, 0x6b, 0x6f, 0x67, 0x2d, 0x68, 0x6c, 0x61, 0x6e,
- 0x64, 0x2d, 0x6a, 0xee, 0xe0, 0x25, 0x7a, 0x73, 0xeb, 0x90, 0x72, 0x6f,
- 0x70, 0x6f, 0x72, 0x74, 0x2d, 0x62, 0x79, 0xe1, 0xe0, 0xa7, 0x77, 0x6e,
- 0x64, 0x79, 0xad, 0xc9, 0xcc, 0xb9, 0x04, 0x08, 0x07, 0x92, 0x6b, 0x72,
- 0x74, 0x30, 0xb0, 0xe0, 0xb3, 0xd4, 0x65, 0x74, 0x35, 0xb2, 0xe0, 0xb4,
- 0x6b, 0x64, 0xe2, 0x02, 0x85, 0x71, 0xb2, 0xe0, 0xb3, 0xc4, 0x68, 0x62,
- 0x6c, 0x67, 0x36, 0xe4, 0xe0, 0xa9, 0x93, 0x30, 0xe1, 0x0a, 0x03, 0x03,
- 0x30, 0x60, 0x75, 0xf6, 0xe0, 0x3d, 0xab, 0x7a, 0xe8, 0xac, 0xed, 0xc0,
- 0x5a, 0x31, 0x61, 0xe6, 0xc1, 0x29, 0xb8, 0x04, 0x07, 0x07, 0x87, 0x79,
- 0x30, 0x61, 0x30, 0xb6, 0xc1, 0x80, 0x70, 0x76, 0x72, 0xb4, 0xe0, 0xac,
- 0xd5, 0x6c, 0x74, 0x72, 0x36, 0xb2, 0xc0, 0xe2, 0x30, 0xe1, 0x06, 0x0e,
- 0x0c, 0x06, 0x06, 0x85, 0x75, 0x2e, 0x78, 0x6e, 0x2d, 0x2d, 0x39, 0x30,
- 0x61, 0x33, 0xe1, 0xe0, 0xa9, 0x36, 0xf3, 0x02, 0x84, 0xf7, 0xe0, 0xb4,
- 0x72, 0x65, 0xe8, 0xe0, 0x6a, 0x63, 0x71, 0x65, 0x63, 0x64, 0xf2, 0x83,
- 0x6f, 0x32, 0xb1, 0xe0, 0xb3, 0x5c, 0x64, 0x78, 0xe8, 0xd7, 0xf4, 0x61,
- 0x61, 0x30, 0x63, 0x76, 0x61, 0xe3, 0xc0, 0xd3, 0x37, 0x74, 0x30, 0x61,
- 0x32, 0x36, 0xb4, 0xc0, 0x4e, 0xb6, 0x04, 0x0b, 0x08, 0x86, 0x71, 0x71,
- 0x39, 0x38, 0x36, 0x62, 0x33, 0xf8, 0xe0, 0xb2, 0x83, 0x6f, 0x72, 0x78,
- 0x32, 0xf2, 0xe0, 0xb3, 0x23, 0x66, 0x72, 0x7a, 0xb8, 0xc0, 0x5b, 0x62,
- 0x74, 0x77, 0xb5, 0xe0, 0xb2, 0xf0, 0xb5, 0x06, 0x07, 0x0d, 0x10, 0x07,
- 0xa4, 0x74, 0x7a, 0x6d, 0xb5, 0xe0, 0xb4, 0x17, 0x73, 0x75, 0x33, 0x34,
- 0x6a, 0x39, 0x33, 0x36, 0x62, 0xe7, 0xe0, 0x31, 0x9d, 0x72, 0xf4, 0x02,
- 0x85, 0x71, 0x33, 0xb4, 0xc0, 0x54, 0x70, 0x34, 0x39, 0xe3, 0xe0, 0xb2,
- 0xeb, 0x6a, 0x73, 0x30, 0x34, 0xb5, 0xc0, 0xe1, 0x35, 0xf1, 0x02, 0x9a,
- 0x78, 0x35, 0xe4, 0x04, 0xe0, 0xb3, 0xe6, 0xae, 0x07, 0x60, 0x76, 0x4e,
- 0xe0, 0x39, 0xb1, 0x78, 0x6e, 0x2d, 0x2d, 0x6a, 0x36, 0x77, 0x31, 0xb9,
- 0xc0, 0x7f, 0x77, 0x34, 0xb2, 0xe0, 0xb3, 0xcf, 0x34, 0x62, 0x37, 0x66,
- 0x74, 0x61, 0x30, 0xe3, 0xe0, 0xa8, 0x81, 0xb4, 0x06, 0x07, 0x10, 0x06,
- 0x14, 0x88, 0x70, 0x76, 0x78, 0xf3, 0xe0, 0xb2, 0xa7, 0x69, 0xf4, 0x02,
- 0x87, 0x37, 0x39, 0x37, 0xeb, 0xe0, 0xb2, 0x9c, 0x31, 0x36, 0xb8, 0xc0,
- 0x94, 0x67, 0x62, 0xf2, 0xe0, 0x50, 0x63, 0xb5, 0x02, 0x86, 0x71, 0x31,
- 0xb1, 0xe0, 0xa8, 0x54, 0x62, 0xf2, 0x03, 0xc0, 0x5c, 0x35, 0x63, 0xf9,
- 0xe0, 0xb1, 0xd5, 0x32, 0x63, 0x32, 0x64, 0xb9, 0xe0, 0xb2, 0x7f, 0x31,
- 0x61, 0x2e, 0x78, 0x6e, 0x2d, 0x2d, 0x70, 0x31, 0x61, 0xe3, 0xe0, 0xa1,
- 0x2b, 0xb3, 0x07, 0x07, 0x02, 0x09, 0x08, 0x08, 0x89, 0x70, 0x78, 0x75,
- 0xb8, 0xe0, 0xb1, 0x98, 0xe8, 0xae, 0x65, 0x30, 0x62, 0x37, 0x30, 0xb7,
- 0xe0, 0xb2, 0x82, 0x64, 0x73, 0x34, 0x34, 0xb3, 0xe0, 0xb3, 0x54, 0x62,
- 0x73, 0x74, 0x30, 0xb0, 0xe0, 0xb1, 0x92, 0x32, 0x76, 0x70, 0x33, 0x30,
- 0xe8, 0xe0, 0xb2, 0x34, 0x30, 0x72, 0x72, 0xb7, 0xe0, 0xb1, 0x9c, 0xb2,
- 0x02, 0x88, 0x73, 0x63, 0x72, 0x6a, 0xb9, 0xe0, 0xa7, 0xee, 0x6d, 0x34,
- 0x61, 0x31, 0xb5, 0xe0, 0xaf, 0x2e, 0xb1, 0x05, 0x08, 0x0f, 0x0f, 0xab,
- 0x71, 0x71, 0x77, 0x32, 0xb3, 0xe0, 0xb2, 0x16, 0x6c, 0x71, 0xf3, 0x02,
- 0x86, 0x37, 0x31, 0xe4, 0xe0, 0xb2, 0x01, 0x30, 0xb3, 0xc0, 0x4c, 0xe3,
- 0x02, 0x85, 0x74, 0xf7, 0xe0, 0xb1, 0xf4, 0x6b, 0x32, 0x65, 0xb1, 0xe0,
- 0xa9, 0xaa, 0x32, 0xe3, 0x03, 0x0a, 0x89, 0x6f, 0x30, 0x63, 0x33, 0x62,
- 0x34, 0x65, 0x76, 0xe1, 0x90, 0x66, 0x69, 0x38, 0x69, 0x78, 0x62, 0x38,
- 0xec, 0x87, 0x31, 0x66, 0x65, 0x30, 0x62, 0x72, 0x2e, 0x78, 0x6e, 0x2d,
- 0x2d, 0x6f, 0x33, 0x63, 0x77, 0xb4, 0xe0, 0xb1, 0xc8, 0x31, 0x62, 0x34,
- 0x63, 0xb3, 0xe0, 0xb1, 0x16, 0x30, 0x74, 0x72, 0x71, 0x37, 0x70, 0x37,
- 0x6e, 0xee, 0xe0, 0xb1, 0xaf, 0xea, 0xe0, 0xa0, 0x31, 0xe9, 0x04, 0xe0,
- 0xb1, 0x15, 0xe8, 0xe0, 0xaa, 0xe4, 0x66, 0x69, 0xee, 0xe0, 0x97, 0xf6,
- 0xe5, 0x02, 0x82, 0xf2, 0x8b, 0x6e, 0x2e, 0x70, 0x72, 0x67, 0xed, 0xe0,
- 0xa9, 0xd4, 0x62, 0xef, 0xe0, 0xab, 0xda, 0x34, 0x34, 0x33, 0x2e, 0xf0,
- 0xe0, 0x9e, 0xf2, 0xae, 0x60, 0x9a, 0x21, 0x48, 0x07, 0xd0, 0x63, 0xf7,
- 0x1a, 0x07, 0x0c, 0x06, 0x06, 0x12, 0x15, 0x20, 0x40, 0x71, 0x07, 0x0f,
- 0x0c, 0x40, 0x90, 0x23, 0x40, 0xff, 0x60, 0x84, 0x67, 0x51, 0xae, 0x1b,
- 0xcf, 0x6d, 0x7a, 0x6d, 0x69, 0xf5, 0xe0, 0x73, 0xb2, 0x77, 0x77, 0xae,
- 0x04, 0xe0, 0xb1, 0x72, 0x63, 0xeb, 0xe0, 0x95, 0x91, 0x75, 0x6f, 0xfa,
- 0xe0, 0x73, 0xa1, 0xf4, 0x60, 0xa7, 0x0d, 0xc1, 0x21, 0xf3, 0x08, 0x05,
- 0x60, 0x43, 0xb4, 0xe0, 0x6e, 0x8a, 0x6b, 0xf2, 0xe0, 0x73, 0x8d, 0xe1,
- 0xe0, 0x73, 0x89, 0xf2, 0x02, 0x87, 0x6f, 0xe3, 0x40, 0x9c, 0xe0, 0xa0,
- 0xf0, 0x69, 0x74, 0x65, 0x73, 0x74, 0x68, 0x69, 0xf3, 0xe0, 0x87, 0x79,
- 0xf0, 0x06, 0x0c, 0x09, 0xe0, 0x5e, 0x59, 0x6d, 0xf5, 0x04, 0xe0, 0x67,
- 0xf9, 0x64, 0x65, 0xf6, 0xe0, 0x7c, 0x19, 0x68, 0x6f, 0x73, 0x74, 0x65,
- 0xe4, 0xe0, 0x87, 0x57, 0xe4, 0xe0, 0x5f, 0xf9, 0xef, 0x07, 0x3a, 0x07,
- 0x1f, 0xe0, 0xaf, 0xc3, 0xf2, 0x03, 0x05, 0x84, 0x73, 0xe5, 0xe0, 0xa0,
- 0xa7, 0xec, 0xe0, 0xb0, 0x3e, 0xeb, 0x06, 0x0d, 0x13, 0xe0, 0xb1, 0xc9,
- 0xf3, 0x06, 0x60, 0xaf, 0x92, 0xc2, 0x50, 0x68, 0x6f, 0xf0, 0xe0, 0xaf,
- 0xb5, 0xe9, 0x02, 0x87, 0x73, 0x62, 0x6f, 0xf2, 0xe0, 0xa5, 0x6b, 0x6e,
- 0x67, 0x67, 0x72, 0x6f, 0xf5, 0xe0, 0x97, 0x33, 0x65, 0x72, 0xf3, 0xe0,
- 0x8d, 0xbe, 0x6f, 0x64, 0x73, 0xe9, 0xe0, 0x72, 0x1f, 0xec, 0x02, 0x97,
- 0xf4, 0x02, 0x8a, 0x6c, 0x61, 0x62, 0x2d, 0x64, 0x65, 0xed, 0xe0, 0x7b,
- 0x1d, 0x65, 0x72, 0x73, 0x6b, 0x6c, 0x75, 0xf7, 0xe0, 0x9e, 0xea, 0x6f,
- 0xed, 0xe0, 0xa1, 0x07, 0x64, 0x7a, 0x69, 0x73, 0x6c, 0xe1, 0xe0, 0xa5,
- 0x7c, 0x6e, 0x65, 0x78, 0xf4, 0xe0, 0xb0, 0xbb, 0xed, 0x07, 0x60, 0x7f,
- 0x6f, 0xe0, 0x31, 0x3b, 0x66, 0x6c, 0x61, 0xe2, 0xe0, 0x9d, 0x80, 0x6c,
- 0x6f, 0x63, 0xec, 0x04, 0xe0, 0xad, 0x89, 0x61, 0xf7, 0xca, 0x22, 0xe9,
- 0x0f, 0x03, 0x1f, 0x03, 0x18, 0x16, 0x0a, 0x05, 0x04, 0x60, 0x72, 0x47,
- 0xe0, 0x39, 0xd5, 0xf8, 0xda, 0x7a, 0xf4, 0x02, 0x98, 0xe8, 0x03, 0x09,
- 0x87, 0x79, 0x6f, 0x75, 0x74, 0x75, 0xe2, 0xe0, 0xaa, 0x74, 0x67, 0x6f,
- 0x6f, 0xe7, 0xe0, 0x35, 0x04, 0xae, 0xe0, 0x7a, 0x3c, 0xe4, 0xe0, 0x72,
- 0x8f, 0xef, 0xc5, 0xe7, 0xee, 0x0b, 0x60, 0x2d, 0xae, 0x41, 0xc0, 0x60,
- 0x80, 0xe8, 0xc0, 0xdb, 0xe4, 0x02, 0x84, 0xef, 0xe0, 0x8f, 0xe9, 0x6d,
- 0xe9, 0xe0, 0x83, 0x2e, 0xec, 0x02, 0x8d, 0x6c, 0x69, 0x61, 0xed, 0x04,
- 0xe0, 0x94, 0xee, 0x68, 0xe9, 0xe0, 0xa1, 0x2a, 0x64, 0x6c, 0xe9, 0xe0,
- 0x21, 0x0e, 0x6b, 0xe9, 0x04, 0xe0, 0xb1, 0x09, 0xae, 0xe0, 0x9e, 0x43,
- 0x69, 0xe8, 0xe0, 0x72, 0x4f, 0xe6, 0xe0, 0x72, 0x4b, 0xe5, 0x02, 0x92,
- 0xee, 0x04, 0xe0, 0xb0, 0xf4, 0x2e, 0x66, 0x75, 0x6e, 0x6b, 0x66, 0x65,
- 0x75, 0x65, 0xf2, 0xe0, 0x77, 0xf6, 0x6c, 0xf5, 0xe0, 0xa0, 0x4d, 0xe8,
- 0x04, 0x07, 0x08, 0x8a, 0x6f, 0x73, 0x77, 0xe8, 0xe0, 0xaf, 0xef, 0x6d,
- 0xae, 0x60, 0x5c, 0x65, 0xe0, 0x20, 0x1c, 0x69, 0x74, 0x65, 0x73, 0x6e,
- 0x6f, 0xf7, 0xe0, 0xa9, 0x2f, 0x61, 0xec, 0xe0, 0x9a, 0x84, 0xe5, 0x0a,
- 0x22, 0x11, 0x06, 0x05, 0x06, 0x14, 0x40, 0x86, 0x8b, 0x73, 0xf4, 0x06,
- 0x06, 0x0d, 0xe0, 0x85, 0x0a, 0x66, 0x61, 0xec, 0xe0, 0x9b, 0xdb, 0xe5,
- 0x04, 0xe0, 0xa0, 0x97, 0x75, 0x72, 0x6f, 0x70, 0xe5, 0xe0, 0x96, 0x60,
- 0x31, 0x2d, 0x75, 0xf3, 0xe0, 0x7b, 0x06, 0x6c, 0x6c, 0x62, 0x65, 0x69,
- 0x6e, 0x67, 0x7a, 0x6f, 0x6e, 0x65, 0xae, 0x60, 0xac, 0x34, 0xc1, 0x99,
- 0xe9, 0x60, 0xae, 0x61, 0xc1, 0x68, 0x67, 0xf2, 0xe0, 0xa4, 0x5d, 0x65,
- 0x6b, 0xec, 0xe0, 0xad, 0x20, 0xe4, 0x02, 0x8d, 0x65, 0x70, 0x6c, 0x6f,
- 0x79, 0xae, 0x60, 0x9b, 0x1b, 0x45, 0x0d, 0xcf, 0xcf, 0xe4, 0xe0, 0x9b,
- 0xbb, 0xe2, 0x0b, 0x08, 0x0e, 0x08, 0x08, 0x06, 0x20, 0x04, 0xe0, 0x9d,
- 0x45, 0x74, 0x68, 0x69, 0x6e, 0xe7, 0xe0, 0x92, 0xe6, 0xf3, 0x02, 0x87,
- 0x70, 0x61, 0x63, 0xe5, 0xe0, 0x41, 0x5b, 0x69, 0xf4, 0xd4, 0x76, 0x72,
- 0x65, 0x64, 0x69, 0xf2, 0xe0, 0xa7, 0x81, 0x70, 0x61, 0x61, 0x73, 0xae,
- 0xe0, 0x68, 0xfb, 0x6c, 0x69, 0xeb, 0xe0, 0xa2, 0x5a, 0xe8, 0x02, 0x98,
- 0xef, 0x02, 0x88, 0x73, 0x74, 0x69, 0x6e, 0xe7, 0xe0, 0x92, 0x6e, 0x70,
- 0xae, 0x60, 0x91, 0xbc, 0x49, 0x0c, 0x46, 0x32, 0x4c, 0xd8, 0xc2, 0x1f,
- 0x61, 0xf2, 0xe0, 0x50, 0x32, 0xe3, 0xe0, 0xa9, 0x3e, 0xae, 0x17, 0x06,
- 0x06, 0x60, 0x70, 0x5b, 0x4e, 0x24, 0x60, 0x26, 0x2a, 0x41, 0x0c, 0x40,
- 0xb8, 0x45, 0xcc, 0x41, 0x84, 0x40, 0xbe, 0xc0, 0x8d, 0xf4, 0x60, 0xae,
- 0x3d, 0xc0, 0xf3, 0xee, 0x60, 0xa5, 0xbe, 0xc8, 0xa3, 0xe9, 0x60, 0x9c,
- 0x76, 0xd1, 0xb0, 0x61, 0x74, 0x68, 0x65, 0xf2, 0x60, 0x8d, 0x69, 0xe0,
- 0x22, 0x68, 0xae, 0x02, 0x84, 0xf4, 0xe0, 0xaf, 0xb8, 0xe2, 0xe0, 0xaf,
- 0x7b, 0xe1, 0x10, 0x05, 0x2b, 0x1e, 0x16, 0x12, 0x20, 0x23, 0x0c, 0x09,
- 0x60, 0x43, 0xf0, 0xe0, 0x5e, 0xd6, 0x7a, 0xf5, 0xe0, 0x5a, 0x73, 0xf4,
- 0x03, 0x05, 0x98, 0x73, 0xef, 0xe0, 0x5d, 0x25, 0x63, 0xe8, 0x07, 0x04,
- 0x60, 0xa4, 0x3a, 0xcb, 0x5a, 0x61, 0x6e, 0xe4, 0x86, 0x2d, 0x61, 0x6e,
- 0x64, 0x2d, 0x63, 0x6c, 0xef, 0xe0, 0x91, 0xfc, 0x61, 0xf2, 0x04, 0xe0,
- 0x5f, 0xab, 0xe1, 0xe0, 0x55, 0xf5, 0xf3, 0x02, 0x86, 0x73, 0x61, 0xed,
- 0xe0, 0xab, 0x92, 0xe8, 0x02, 0x88, 0x74, 0x65, 0x6e, 0x61, 0xf7, 0xe0,
- 0xa7, 0xca, 0x69, 0x6e, 0x67, 0x74, 0x6f, 0x6e, 0xe4, 0xe0, 0x95, 0x34,
- 0xf2, 0x06, 0x05, 0x05, 0xe0, 0xad, 0x26, 0x73, 0xfa, 0xe0, 0x9d, 0xb2,
- 0x6d, 0xe9, 0xe0, 0xa1, 0x8f, 0x61, 0xe2, 0xe0, 0x5e, 0x3f, 0xee, 0x02,
- 0x85, 0x6f, 0xf5, 0xe0, 0xa7, 0x7d, 0xe7, 0x04, 0xe0, 0xaf, 0x3e, 0x67,
- 0xef, 0xe0, 0xae, 0xd4, 0xec, 0x07, 0x05, 0x06, 0x04, 0xe0, 0x9c, 0x66,
- 0x6d, 0xe1, 0xe0, 0x24, 0xdf, 0x6c, 0x6f, 0xee, 0xe0, 0x41, 0x4c, 0xe5,
- 0xe0, 0x99, 0x4f, 0x62, 0x72, 0x7a, 0x79, 0x63, 0xe8, 0xe0, 0xab, 0x2f,
- 0xeb, 0x06, 0x03, 0x04, 0xe0, 0x68, 0x1b, 0xf5, 0xcb, 0x87, 0xe5, 0xe0,
- 0xaa, 0x17, 0xe1, 0x02, 0x8a, 0x79, 0x61, 0x6d, 0x61, 0xae, 0x60, 0xa5,
- 0x4d, 0xc8, 0xa7, 0x73, 0x61, 0xae, 0x60, 0x83, 0x2c, 0xcf, 0xe4, 0x6a,
- 0xe9, 0x02, 0x84, 0xed, 0xe0, 0x59, 0x03, 0xeb, 0xe0, 0x41, 0x2b, 0x66,
- 0x66, 0x6c, 0x65, 0x63, 0xe5, 0xe0, 0xa8, 0xf1, 0xae, 0x60, 0x26, 0xf4,
- 0x60, 0x83, 0xb6, 0x41, 0xa6, 0x84, 0xf6, 0x1e, 0x26, 0x05, 0x04, 0x11,
- 0x07, 0x23, 0x40, 0x56, 0x06, 0x04, 0x25, 0x40, 0xb4, 0x06, 0x40, 0xce,
- 0x04, 0x06, 0x04, 0x40, 0xa6, 0x06, 0x60, 0x6c, 0x4e, 0x50, 0xe2, 0xca,
- 0x85, 0x1f, 0xc3, 0x05, 0x18, 0xe0, 0x35, 0x1f, 0xe5, 0x02, 0x8c, 0x6c,
- 0x65, 0x72, 0xae, 0x03, 0xc2, 0xe3, 0x1f, 0x43, 0xf8, 0xdf, 0x27, 0xe7,
- 0x60, 0x3c, 0xb2, 0x41, 0xad, 0xe0, 0x59, 0x3e, 0x61, 0x72, 0x67, 0xe7,
- 0xe0, 0x9c, 0xdf, 0x78, 0xec, 0xe0, 0x9e, 0x50, 0xf6, 0xe0, 0xad, 0x22,
- 0xf5, 0x05, 0x06, 0xe0, 0xae, 0x7d, 0x6c, 0x74, 0xf2, 0xe0, 0x4c, 0xe3,
- 0x65, 0xec, 0xe0, 0x8c, 0x7b, 0x73, 0xae, 0x60, 0x96, 0x0a, 0xd7, 0x03,
- 0xf0, 0x05, 0x0f, 0xe0, 0x65, 0xdc, 0xf3, 0x04, 0xe0, 0xa2, 0xa2, 0x2e,
- 0xed, 0x04, 0xe0, 0x67, 0x35, 0xe3, 0xe0, 0x7d, 0xf6, 0xee, 0x02, 0x87,
- 0x70, 0x6c, 0x75, 0xf3, 0xe0, 0xad, 0xb1, 0xe4, 0xe0, 0x90, 0x11, 0xef,
- 0x07, 0x04, 0x07, 0x0b, 0x0a, 0x27, 0x84, 0xf9, 0xe0, 0x41, 0x62, 0xf4,
- 0x60, 0x99, 0x9d, 0x53, 0xba, 0x8f, 0x73, 0xf3, 0x04, 0xe0, 0xad, 0x41,
- 0x65, 0xf6, 0xe0, 0x9f, 0xda, 0x6f, 0x72, 0x6c, 0x6f, 0x70, 0x65, 0xf2,
- 0xe0, 0x96, 0xba, 0xec, 0x07, 0x04, 0x04, 0x05, 0xe0, 0x88, 0x63, 0xf9,
- 0xe0, 0x55, 0x6e, 0xf6, 0xe0, 0xad, 0x2f, 0x6f, 0xe7, 0xe0, 0x57, 0xc7,
- 0xeb, 0x02, 0x87, 0x73, 0x77, 0x61, 0xe7, 0xe0, 0x6f, 0x6d, 0x65, 0x6e,
- 0x6b, 0x75, 0xee, 0xe0, 0x46, 0x05, 0xe4, 0xe0, 0x81, 0xe8, 0xe1, 0xc1,
- 0xe0, 0xee, 0x60, 0x93, 0x14, 0xda, 0xe3, 0xed, 0xe0, 0x86, 0x28, 0xec,
- 0x02, 0x84, 0xef, 0xe0, 0x8e, 0xfc, 0xe1, 0x02, 0x93, 0x64, 0xe9, 0x02,
- 0x86, 0x6d, 0x69, 0xf2, 0xe0, 0xaa, 0xea, 0x6b, 0x61, 0x76, 0x6b, 0x61,
- 0xfa, 0xe0, 0xaa, 0xe1, 0x61, 0x6e, 0x64, 0x65, 0xf2, 0xe0, 0x73, 0xff,
- 0xe9, 0x14, 0x04, 0x08, 0x07, 0x06, 0x20, 0x19, 0x1a, 0x08, 0x0c, 0x05,
- 0x07, 0x0e, 0x60, 0x89, 0x49, 0x60, 0x23, 0xd0, 0x81, 0xf8, 0xe0, 0xac,
- 0xfa, 0xf6, 0x60, 0x41, 0x68, 0x60, 0x6b, 0x41, 0x9c, 0x74, 0x65, 0x72,
- 0xe2, 0xe0, 0xac, 0x39, 0xf3, 0x60, 0xa1, 0x9d, 0xca, 0xfd, 0xf2, 0x02,
- 0x95, 0x74, 0xf5, 0x04, 0xe0, 0x97, 0xc2, 0x61, 0xec, 0x07, 0x01, 0x60,
- 0x9d, 0xa7, 0xcd, 0xb7, 0x2d, 0x75, 0xf3, 0xe0, 0x89, 0x29, 0x67, 0x69,
- 0xee, 0x60, 0xa1, 0x2c, 0xcc, 0x54, 0xf0, 0x05, 0x07, 0xe0, 0xad, 0x6e,
- 0x73, 0x69, 0x6e, 0xe1, 0xe0, 0xad, 0x13, 0x2e, 0x6a, 0x65, 0x6c, 0x61,
- 0x73, 0x74, 0x69, 0xe3, 0xe0, 0x95, 0xf6, 0xee, 0x05, 0x0f, 0xe0, 0xad,
- 0x4d, 0xee, 0x02, 0x87, 0x79, 0x74, 0x73, 0xe9, 0xe0, 0x89, 0xd6, 0x69,
- 0xe3, 0xe0, 0x89, 0xd1, 0x64, 0xe1, 0xe0, 0x9e, 0xcb, 0x6c, 0x6c, 0xe1,
- 0x60, 0x95, 0x6f, 0xd6, 0x03, 0xeb, 0x06, 0x60, 0xa3, 0x5f, 0xc8, 0xe6,
- 0x69, 0xee, 0xe0, 0x6b, 0xf2, 0x64, 0xe5, 0xe0, 0x94, 0x4f, 0xe3, 0x60,
- 0x25, 0x3c, 0xe0, 0x65, 0x12, 0x62, 0xef, 0x02, 0x81, 0x2d, 0x76, 0x61,
- 0x6c, 0x65, 0x6e, 0xf4, 0xe0, 0xa7, 0x7a, 0x61, 0xea, 0xe0, 0xa1, 0xbe,
- 0xe7, 0x60, 0xa9, 0x90, 0xc3, 0x84, 0xe5, 0x0e, 0x06, 0x12, 0x21, 0x40,
- 0x4b, 0x1d, 0x04, 0x15, 0x60, 0xaa, 0xdc, 0xc1, 0x6a, 0x76, 0x65, 0xec,
- 0xe0, 0x79, 0x51, 0xf4, 0x06, 0x60, 0xac, 0x3a, 0xc0, 0xb9, 0x65, 0x72,
- 0x69, 0x6e, 0x61, 0x69, 0x72, 0xe5, 0xe0, 0x45, 0xda, 0x73, 0xf4, 0x08,
- 0x0f, 0x60, 0x4c, 0xc8, 0xe0, 0x5c, 0x81, 0xf6, 0x02, 0x87, 0x1f, 0x43,
- 0x65, 0xe7, 0xe0, 0xa5, 0x4a, 0x61, 0xe7, 0xe0, 0xa1, 0xd4, 0x72, 0x65,
- 0xad, 0x60, 0x2c, 0x65, 0xc1, 0x27, 0xf2, 0x0b, 0x15, 0x04, 0x12, 0x07,
- 0x09, 0x60, 0xa5, 0x54, 0xc0, 0xf5, 0xf3, 0x08, 0x07, 0x60, 0x63, 0xbc,
- 0xe0, 0x3b, 0x22, 0x69, 0x63, 0x68, 0x65, 0xf2, 0xdd, 0x5a, 0x61, 0xe9,
- 0xe0, 0x96, 0xc8, 0xf2, 0xe0, 0x3a, 0xb6, 0x6d, 0x1f, 0x43, 0x76, 0x67,
- 0x65, 0x6e, 0x73, 0x62, 0x65, 0x72, 0x61, 0xf4, 0x5d, 0x42, 0xe0, 0x7c,
- 0x98, 0x69, 0x73, 0x69, 0xe7, 0xe0, 0xaa, 0xee, 0x63, 0x65, 0xec, 0x60,
- 0x3a, 0x34, 0xe0, 0x4b, 0x2f, 0xe2, 0xe0, 0x49, 0x03, 0xee, 0x07, 0x04,
- 0x06, 0x04, 0xe0, 0xaa, 0xfc, 0xf4, 0xe0, 0x29, 0x2a, 0x6e, 0x65, 0xf3,
- 0xe0, 0x2c, 0x79, 0xe9, 0xe0, 0x78, 0x76, 0xe5, 0x60, 0x6f, 0x87, 0xe0,
- 0x39, 0x2c, 0x6c, 0xf6, 0xd9, 0xc6, 0xe7, 0x02, 0x84, 0x1f, 0x43, 0xe5,
- 0x88, 0xe1, 0x06, 0x60, 0xaa, 0x7a, 0xc0, 0xdf, 0x72, 0x73, 0x68, 0xe5,
- 0xe0, 0xa5, 0x73, 0x66, 0xf3, 0xe0, 0xa7, 0x06, 0xe4, 0xe0, 0xa6, 0x9e,
- 0xe3, 0x60, 0xaa, 0xd2, 0xc1, 0x6a, 0xe2, 0xe0, 0xaa, 0xcc, 0xe1, 0x0f,
- 0x11, 0x08, 0x16, 0x3f, 0x04, 0x0b, 0x07, 0x04, 0x06, 0x60, 0xaa, 0x2a,
- 0xc1, 0x6b, 0xf2, 0x07, 0x05, 0x60, 0x97, 0xc9, 0xc9, 0x4f, 0x67, 0xe7,
- 0xe0, 0x9a, 0x65, 0xe5, 0xe0, 0x3f, 0xeb, 0x70, 0x6f, 0xf2, 0x60, 0x94,
- 0xa0, 0xce, 0xd2, 0xee, 0x07, 0x05, 0x4e, 0x40, 0xe0, 0x9c, 0xb7, 0x74,
- 0xe1, 0xe0, 0xa6, 0x87, 0xe7, 0x04, 0xe0, 0xab, 0x04, 0xf5, 0xe0, 0x9d,
- 0x17, 0xec, 0x04, 0x23, 0x0c, 0x81, 0xec, 0x02, 0x84, 0x1f, 0x43, 0xe9,
- 0x8b, 0xe5, 0x09, 0x0f, 0x10, 0x04, 0x60, 0xa3, 0xe9, 0xc6, 0xde, 0xe5,
- 0x03, 0x02, 0x85, 0xe4, 0x86, 0xad, 0x02, 0x82, 0x64, 0x2d, 0x61, 0xef,
- 0xc5, 0xdb, 0xad, 0x0f, 0x84, 0x65, 0x72, 0xae, 0x02, 0x83, 0xef, 0xdc,
- 0x49, 0xe8, 0xe0, 0x2c, 0xe1, 0x2d, 0xe4, 0x02, 0x81, 0x2d, 0x61, 0x6f,
- 0xf3, 0xe0, 0xa4, 0x5a, 0xeb, 0xe0, 0x72, 0x8b, 0xe7, 0x04, 0xe0, 0x55,
- 0x25, 0xe1, 0x60, 0xa6, 0x6d, 0xc4, 0x4a, 0x64, 0xf3, 0x60, 0x97, 0x5b,
- 0xcd, 0x13, 0x63, 0xe1, 0xd2, 0x4d, 0x61, 0x70, 0xf3, 0xe0, 0xaa, 0xa2,
- 0xae, 0x60, 0xa7, 0x5e, 0x42, 0xcd, 0xc0, 0x76, 0xae, 0x60, 0x99, 0x43,
- 0xd2, 0x47, 0x2d, 0x69, 0x6e, 0x66, 0xef, 0xe0, 0x9c, 0x67, 0xf5, 0x1e,
- 0x14, 0x05, 0x10, 0x04, 0x2f, 0x40, 0xee, 0x40, 0x4e, 0x2a, 0x09, 0x40,
- 0x5a, 0x1b, 0x1b, 0x2f, 0x13, 0x0d, 0x0c, 0x05, 0x10, 0x14, 0x1c, 0x1b,
- 0x07, 0x10, 0xe0, 0x65, 0x6b, 0xfa, 0x07, 0x04, 0x60, 0x90, 0x71, 0xda,
- 0xe3, 0xf3, 0xe0, 0x6c, 0xa3, 0x68, 0x67, 0x6f, 0x72, 0xef, 0xe0, 0x51,
- 0xef, 0xf9, 0x60, 0xab, 0x43, 0x88, 0xf7, 0x05, 0x06, 0xe0, 0x6c, 0x87,
- 0x75, 0x2e, 0xe1, 0xe0, 0x9b, 0xec, 0xe1, 0xe0, 0x55, 0xcf, 0xf6, 0xe0,
- 0x91, 0x05, 0xf4, 0x08, 0x06, 0x0e, 0x60, 0xa1, 0xa3, 0xc4, 0x93, 0x77,
- 0x65, 0xee, 0xe0, 0x97, 0x40, 0xf3, 0x02, 0x87, 0x75, 0x6e, 0x6f, 0x6d,
- 0xe9, 0xd4, 0xf2, 0xe9, 0xe0, 0x9e, 0xf6, 0xe1, 0x05, 0x06, 0xe0, 0x75,
- 0xf0, 0xfa, 0x4c, 0x5a, 0xe0, 0x3f, 0x9d, 0x73, 0x68, 0xe9, 0xe0, 0x64,
- 0x19, 0xf3, 0x11, 0x06, 0x04, 0x04, 0x09, 0x10, 0x40, 0x59, 0x06, 0x0e,
- 0x0d, 0x14, 0x60, 0x95, 0x6b, 0xd4, 0xd2, 0xf5, 0x60, 0x4d, 0x9e, 0xd2,
- 0x99, 0xf4, 0xe0, 0x82, 0x22, 0xf2, 0xe0, 0x76, 0xc8, 0x6c, 0x69, 0x76,
- 0x69, 0x6e, 0xe7, 0xe0, 0x89, 0x0e, 0xe8, 0x02, 0x85, 0x75, 0xe1, 0xe0,
- 0x9e, 0x82, 0xe9, 0x04, 0xe0, 0x89, 0x00, 0xeb, 0xce, 0x4e, 0x65, 0xf2,
- 0x03, 0x1b, 0x93, 0x73, 0x2e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76,
- 0x69, 0x72, 0x74, 0x75, 0x61, 0x6c, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x2e,
- 0x63, 0x6f, 0xed, 0xe0, 0xa8, 0xe3, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
- 0x74, 0xae, 0x04, 0xe0, 0xa3, 0x08, 0x67, 0x6f, 0x6f, 0xe7, 0xe0, 0xaa,
- 0x89, 0xae, 0x05, 0x0a, 0x0c, 0xd1, 0x7f, 0x70, 0x61, 0x72, 0x74, 0x79,
- 0x2e, 0xe5, 0xe0, 0x8c, 0x23, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x63, 0x65,
- 0x72, 0xf4, 0xe0, 0x4a, 0xab, 0x61, 0x73, 0x65, 0x69, 0x6e, 0x65, 0xf4,
- 0xe0, 0x70, 0x38, 0x64, 0x65, 0xe3, 0xe0, 0x84, 0x28, 0xe3, 0x04, 0xe0,
- 0xa6, 0xbb, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0xf9, 0xe0, 0x87, 0x62, 0xe1,
- 0x06, 0x60, 0x99, 0xcc, 0xc9, 0xf1, 0xae, 0x60, 0x99, 0x71, 0xce, 0xbc,
- 0xae, 0x60, 0x6b, 0x9d, 0x60, 0x27, 0x3d, 0x44, 0xab, 0x42, 0x5f, 0x24,
- 0x4a, 0xe2, 0x42, 0x2b, 0x40, 0xf9, 0xc2, 0x3b, 0xad, 0x09, 0x05, 0x08,
- 0x60, 0x7a, 0x07, 0x02, 0x02, 0x82, 0x77, 0xe5, 0xe0, 0xa1, 0xdf, 0x67,
- 0x6f, 0x76, 0x2d, 0x77, 0xe5, 0xdb, 0xfa, 0x65, 0x61, 0x73, 0x74, 0xad,
- 0x04, 0xe0, 0xa1, 0xe1, 0x31, 0xae, 0x5d, 0x08, 0xe0, 0x84, 0xdb, 0xf2,
- 0x0b, 0x04, 0x05, 0x04, 0x04, 0x04, 0x07, 0x0d, 0xe0, 0x63, 0xd3, 0xf5,
- 0xe0, 0x4c, 0xd9, 0x6f, 0xf7, 0xe0, 0x4d, 0x01, 0xee, 0xe0, 0x7e, 0x9c,
- 0xec, 0xe0, 0x3a, 0x9f, 0xe9, 0xe0, 0x7e, 0x94, 0x65, 0x73, 0x68, 0xe9,
- 0xe0, 0x4e, 0x92, 0x62, 0x69, 0x6e, 0xef, 0x02, 0x81, 0x2d, 0x70, 0x65,
- 0xf3, 0xe0, 0x90, 0xe8, 0xe1, 0x09, 0x05, 0x04, 0x60, 0x38, 0x01, 0xe0,
- 0x67, 0x47, 0x79, 0xe1, 0xe0, 0x70, 0xe6, 0xf5, 0xe0, 0xa5, 0xe9, 0x73,
- 0x6f, 0xe5, 0xe0, 0xa5, 0xfc, 0xf0, 0x07, 0x06, 0x04, 0x05, 0xe0, 0xa7,
- 0xde, 0xf0, 0x4e, 0xd4, 0xe0, 0x88, 0xd8, 0xef, 0xe0, 0x6b, 0x04, 0x6c,
- 0xe9, 0xe0, 0xa9, 0x43, 0x61, 0x61, 0x73, 0x2e, 0x6b, 0x61, 0x7a, 0x74,
- 0x65, 0x6c, 0x65, 0x70, 0x6f, 0x72, 0x74, 0xae, 0xe0, 0x57, 0xa6, 0xef,
- 0x60, 0x3f, 0xbd, 0x5f, 0xd2, 0xe0, 0x48, 0x56, 0xee, 0x0c, 0x06, 0x0b,
- 0x06, 0x20, 0x0f, 0x60, 0x83, 0x99, 0xe0, 0x24, 0xbf, 0x7a, 0x65, 0xee,
- 0xe0, 0x8e, 0x3e, 0x75, 0x73, 0x75, 0x61, 0x6c, 0x70, 0x65, 0xf2, 0xe0,
- 0x79, 0xd3, 0x6e, 0x61, 0xee, 0xe0, 0xa4, 0x87, 0xe9, 0x06, 0x0f, 0x06,
- 0xe0, 0xa7, 0x03, 0xf6, 0x02, 0x88, 0x65, 0x72, 0x73, 0x69, 0xf4, 0xe0,
- 0xa7, 0x34, 0xae, 0xe0, 0x9f, 0x75, 0xe3, 0x56, 0xf3, 0xe0, 0x8c, 0x56,
- 0xb5, 0xe0, 0xa9, 0x30, 0x64, 0x65, 0xf2, 0x02, 0x84, 0xf3, 0xe0, 0x26,
- 0x65, 0xae, 0x60, 0x8b, 0x4c, 0xd6, 0x64, 0x61, 0x7a, 0x75, 0xeb, 0xe0,
- 0x70, 0xca, 0xed, 0x06, 0x09, 0x06, 0xe0, 0x6a, 0x71, 0xe9, 0x04, 0xe0,
- 0x92, 0x8d, 0xe7, 0xe0, 0x6a, 0x7a, 0xe2, 0x60, 0xa3, 0x86, 0xc4, 0x3a,
- 0x61, 0xea, 0xe0, 0x6e, 0xde, 0xec, 0x08, 0x04, 0x60, 0x6f, 0x60, 0xe0,
- 0x30, 0x7c, 0xf3, 0xe0, 0x92, 0x95, 0x6c, 0x65, 0x6e, 0xf3, 0x04, 0xe0,
- 0x56, 0x65, 0x76, 0x61, 0xee, 0xe0, 0xa2, 0xfe, 0xeb, 0x06, 0x09, 0x09,
- 0xe0, 0xa8, 0xec, 0xe9, 0x04, 0xe0, 0xa2, 0x8b, 0xe8, 0xe0, 0x68, 0x22,
- 0x30, 0x2e, 0x62, 0x69, 0x67, 0xf6, 0xe0, 0xa8, 0x7e, 0xae, 0x0d, 0x46,
- 0x3c, 0x60, 0x8b, 0x2c, 0x44, 0xab, 0x4d, 0x65, 0x45, 0x43, 0x9c, 0x70,
- 0x72, 0x69, 0x6d, 0xe5, 0xe0, 0x22, 0xd6, 0x6a, 0xe9, 0x05, 0x08, 0xe0,
- 0x9b, 0xb2, 0x74, 0x61, 0x77, 0x61, 0xf2, 0xe0, 0x70, 0xae, 0xe9, 0xe0,
- 0x57, 0xe2, 0x69, 0x2e, 0x6e, 0x61, 0x62, 0x75, 0x2e, 0x63, 0x61, 0xf3,
- 0xe0, 0x99, 0x83, 0xe7, 0x07, 0x60, 0x69, 0xfa, 0xe0, 0x3e, 0xb4, 0xe9,
- 0xe0, 0x2d, 0x96, 0x66, 0xe3, 0xe0, 0x88, 0x87, 0xe5, 0x04, 0xe0, 0x3d,
- 0xaf, 0x6e, 0xef, 0x04, 0xe0, 0xa0, 0xea, 0x68, 0xe1, 0xe0, 0x32, 0x57,
- 0xe4, 0x08, 0x04, 0x60, 0x5e, 0xf9, 0xe0, 0x48, 0x25, 0xef, 0xe0, 0x4d,
- 0x2c, 0xe9, 0x60, 0x84, 0xff, 0xe0, 0x22, 0xcf, 0x63, 0x68, 0xe9, 0x03,
- 0x0d, 0x84, 0xee, 0x02, 0x85, 0x6f, 0xed, 0xe0, 0x52, 0x56, 0x61, 0xe4,
- 0xe0, 0x52, 0x83, 0xeb, 0xe0, 0x57, 0x43, 0x68, 0xe1, 0xe0, 0x51, 0x70,
- 0xe2, 0x06, 0x60, 0x86, 0xf2, 0xdf, 0x99, 0xe5, 0x04, 0xe0, 0xa6, 0xdc,
- 0xf2, 0x02, 0x88, 0x73, 0x70, 0x61, 0x63, 0xe5, 0xe0, 0x71, 0x15, 0xae,
- 0xe0, 0x39, 0x3c, 0xe1, 0x60, 0x28, 0x17, 0xe0, 0x80, 0x32, 0xb2, 0x02,
- 0x86, 0x2d, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x2e, 0x78, 0x6e, 0xe2, 0xe0,
- 0x82, 0xab, 0xae, 0x06, 0x60, 0x97, 0xc7, 0xd0, 0x63, 0x63, 0x68, 0x61,
- 0x6e, 0x6e, 0x65, 0xec, 0xe0, 0x8d, 0x7c, 0xf4, 0x28, 0x07, 0x2c, 0x14,
- 0x2c, 0x40, 0x58, 0x07, 0x40, 0xb7, 0x41, 0x8d, 0x04, 0x42, 0x31, 0x11,
- 0x22, 0x0a, 0x0c, 0x21, 0x3f, 0x40, 0x40, 0x09, 0x40, 0xe5, 0x06, 0x0f,
- 0x06, 0x42, 0x3c, 0x60, 0x73, 0x27, 0x51, 0x90, 0x4f, 0xa6, 0xc8, 0x46,
- 0x1f, 0x43, 0x78, 0xee, 0xe0, 0x74, 0x21, 0xf9, 0x07, 0x12, 0x0a, 0x03,
- 0xe0, 0xa1, 0x94, 0xf3, 0x06, 0x60, 0x99, 0x63, 0xca, 0xfa, 0xf6, 0x04,
- 0xe0, 0x99, 0xb2, 0x1f, 0x43, 0xe6, 0xe0, 0x9c, 0xe5, 0x70, 0x65, 0x64,
- 0x72, 0x65, 0x61, 0xed, 0xe0, 0xa6, 0xfc, 0xee, 0xcf, 0xe1, 0x63, 0xe8,
- 0xe0, 0x96, 0xac, 0xf7, 0x06, 0x60, 0x95, 0x35, 0xd2, 0x8a, 0x6d, 0x61,
- 0x69, 0x6c, 0xae, 0x60, 0x83, 0xf3, 0x60, 0x21, 0x85, 0xc2, 0x1f, 0xf6,
- 0x07, 0x05, 0x60, 0xa5, 0xd2, 0xc1, 0xd3, 0x65, 0xe4, 0xe0, 0x61, 0x80,
- 0xae, 0x0b, 0x06, 0x06, 0x60, 0x5b, 0xd1, 0x60, 0x38, 0xf1, 0xcf, 0x90,
- 0xf4, 0x60, 0xa5, 0xbb, 0xc1, 0x26, 0xe9, 0x60, 0xa5, 0xd7, 0xc0, 0x97,
- 0xe2, 0x60, 0x9e, 0x38, 0x48, 0x6a, 0xb3, 0xf5, 0x0a, 0x0a, 0x04, 0x0b,
- 0x1a, 0x09, 0x60, 0xa5, 0xa4, 0x9b, 0x78, 0x66, 0x61, 0x6d, 0x69, 0x6c,
- 0xf9, 0xe0, 0xa5, 0x31, 0xf6, 0xe0, 0xa6, 0x1e, 0xf3, 0x04, 0xe0, 0xa6,
- 0xff, 0x63, 0x61, 0xee, 0xe0, 0x9f, 0x5a, 0xf2, 0x04, 0x07, 0x04, 0x84,
- 0x79, 0x73, 0x74, 0xf9, 0xe0, 0x7e, 0x90, 0xe9, 0xe0, 0x22, 0xef, 0xe5,
- 0xe0, 0x95, 0xc9, 0xae, 0x60, 0x96, 0x1f, 0xd0, 0x76, 0xee, 0x04, 0xe0,
- 0x9b, 0xe9, 0xeb, 0xe0, 0xa4, 0xff, 0xec, 0x04, 0xe0, 0xa5, 0xe8, 0x65,
- 0x61, 0x70, 0x2d, 0x70, 0x61, 0x72, 0x74, 0xee, 0xe0, 0x7d, 0x79, 0xf4,
- 0x60, 0x43, 0xee, 0xe0, 0x63, 0x3f, 0xf3, 0x06, 0x40, 0x98, 0x04, 0x04,
- 0x8a, 0xf5, 0x0e, 0x05, 0x06, 0x0b, 0x1e, 0x0b, 0x07, 0x20, 0x04, 0x0b,
- 0x06, 0xe0, 0x5b, 0xfa, 0x79, 0xe1, 0xe0, 0x64, 0xbc, 0x77, 0x61, 0xee,
- 0xe0, 0x63, 0x40, 0x73, 0x68, 0x69, 0x6d, 0x61, 0xae, 0x60, 0x8b, 0xba,
- 0xd6, 0xc2, 0x72, 0xf5, 0x06, 0x04, 0x03, 0xe0, 0x7d, 0x7d, 0xf4, 0xe0,
- 0x3c, 0x64, 0xef, 0xd0, 0x9d, 0xe7, 0x04, 0xe0, 0x65, 0xfa, 0xe1, 0x04,
- 0xe0, 0x7b, 0x12, 0x73, 0xe8, 0xe0, 0x4f, 0x47, 0xee, 0x02, 0x84, 0xef,
- 0xe0, 0x4d, 0x16, 0xe1, 0xe0, 0x69, 0xe5, 0x6d, 0x61, 0x67, 0xef, 0xe0,
- 0x57, 0x19, 0xeb, 0x02, 0x8e, 0xf5, 0x05, 0x04, 0xe0, 0x9a, 0xdc, 0xed,
- 0xe0, 0x62, 0x4a, 0xe2, 0xe0, 0x61, 0x5f, 0xe9, 0x02, 0x86, 0x79, 0x6f,
- 0xee, 0xe0, 0x35, 0x60, 0x67, 0x61, 0xf4, 0xe0, 0xa5, 0x66, 0xe9, 0xe0,
- 0x5b, 0xef, 0x67, 0xe1, 0x04, 0xe0, 0x9b, 0xce, 0x72, 0xf5, 0xe0, 0x69,
- 0xef, 0x63, 0x68, 0xe9, 0xe0, 0x4f, 0xa0, 0xe2, 0x04, 0xe0, 0xa2, 0xa7,
- 0xe1, 0x04, 0xe0, 0x4e, 0xe4, 0xed, 0xe0, 0x3b, 0xab, 0xf4, 0xe0, 0x88,
- 0x52, 0xeb, 0xe0, 0x96, 0x54, 0x65, 0x6c, 0x69, 0x6e, 0x6f, 0x67, 0xf2,
- 0xe0, 0x9b, 0xb2, 0xae, 0x60, 0xa5, 0x0c, 0xc1, 0x46, 0xf2, 0x11, 0x09,
- 0x1e, 0x0d, 0x26, 0x12, 0x40, 0x47, 0x40, 0xc0, 0x60, 0x90, 0xed, 0x50,
- 0xf0, 0xc3, 0x0e, 0x1f, 0xc3, 0x04, 0xe0, 0x6f, 0x14, 0xf8, 0xc0, 0x49,
- 0xf9, 0x03, 0x05, 0x8b, 0x73, 0xe9, 0xe0, 0xa0, 0x1a, 0x63, 0x6c, 0x6f,
- 0x75, 0x64, 0x66, 0x6c, 0xe1, 0xe0, 0x8c, 0x1a, 0x2d, 0x73, 0x6e, 0x6f,
- 0x77, 0x70, 0xec, 0xe0, 0x4a, 0x0a, 0x75, 0x73, 0xf4, 0x06, 0x60, 0xa4,
- 0x04, 0xc2, 0x2a, 0xe5, 0xe0, 0xa2, 0x86, 0xef, 0x08, 0x05, 0x09, 0x05,
- 0x04, 0xe0, 0x90, 0xcd, 0x6e, 0xe4, 0xe0, 0x99, 0xc5, 0x6d, 0xf3, 0x60,
- 0x91, 0xd1, 0x4d, 0x13, 0xc2, 0x05, 0x69, 0xf4, 0xe0, 0xa0, 0xca, 0xe7,
- 0xe0, 0x72, 0x61, 0x61, 0x6e, 0xe4, 0xe0, 0x67, 0x97, 0xe9, 0x02, 0x8a,
- 0x74, 0x6f, 0x6e, 0x2e, 0x7a, 0x6f, 0xee, 0xe0, 0x87, 0xc9, 0x65, 0xf3,
- 0xe0, 0x22, 0x96, 0xe5, 0x05, 0x05, 0xe0, 0xa2, 0x40, 0x76, 0xe9, 0xe0,
- 0x8d, 0xd9, 0x6e, 0xf4, 0x04, 0xe0, 0xa4, 0x76, 0x69, 0xee, 0x03, 0x1c,
- 0x81, 0xef, 0x06, 0x03, 0x07, 0xe0, 0xa4, 0x61, 0xad, 0x02, 0x87, 0xf3,
- 0x15, 0x04, 0x09, 0xe0, 0x97, 0x44, 0xe1, 0x05, 0x60, 0x9f, 0x9d, 0x81,
- 0x6c, 0xf4, 0xe0, 0x9f, 0x98, 0x2d, 0xf3, 0x02, 0x84, 0x1f, 0x43, 0xfc,
- 0x85, 0xf5, 0x02, 0x81, 0x65, 0xe4, 0x04, 0xe0, 0x97, 0x44, 0xad, 0xe0,
- 0x97, 0x43, 0xe1, 0x09, 0x1a, 0x04, 0x40, 0x6c, 0x07, 0x0b, 0x0c, 0x8b,
- 0x76, 0x65, 0xec, 0x08, 0x09, 0x60, 0x83, 0x25, 0xe0, 0x22, 0x68, 0x65,
- 0x72, 0xf3, 0x60, 0x44, 0x9b, 0xe0, 0x60, 0xf8, 0xae, 0x60, 0xa1, 0xd1,
- 0xc2, 0x96, 0xf0, 0xe0, 0x9e, 0x36, 0xee, 0x0c, 0x30, 0x60, 0x45, 0x3f,
- 0x60, 0x55, 0x09, 0x43, 0x71, 0xc2, 0x5c, 0xf3, 0x03, 0x14, 0x8a, 0x75,
- 0x72, 0x6c, 0xae, 0x03, 0x05, 0x84, 0x6e, 0xec, 0xe0, 0xa5, 0x57, 0xe5,
- 0xe0, 0x5e, 0x41, 0xe2, 0xe0, 0x87, 0x30, 0x70, 0x6f, 0x72, 0xf4, 0x60,
- 0x7e, 0x5f, 0xe0, 0x24, 0xd1, 0x6c, 0x61, 0x74, 0xe5, 0x02, 0x84, 0xe4,
- 0xe0, 0x85, 0x55, 0xae, 0xe0, 0x87, 0xa2, 0xe9, 0x03, 0x0b, 0x89, 0x62,
- 0x61, 0x72, 0x6c, 0x65, 0x74, 0x74, 0xe1, 0xe0, 0x9f, 0x93, 0x61, 0x6e,
- 0x64, 0x72, 0x69, 0xe1, 0xe0, 0x9d, 0xcd, 0xad, 0x02, 0x8b, 0x62, 0x61,
- 0x72, 0x6c, 0x65, 0x74, 0x74, 0xe1, 0xe0, 0x96, 0x27, 0x61, 0x6e, 0x64,
- 0x72, 0x69, 0xe1, 0xe0, 0x9d, 0xb5, 0x69, 0xee, 0x60, 0x8a, 0x5b, 0xc6,
- 0x16, 0x66, 0x66, 0x69, 0x63, 0x70, 0x6c, 0x65, 0xf8, 0xe0, 0x8d, 0x99,
- 0x65, 0x75, 0x6d, 0x74, 0x67, 0x65, 0x72, 0x61, 0xe4, 0xe0, 0x7c, 0x06,
- 0xe4, 0x04, 0xe0, 0x8b, 0xeb, 0xe5, 0x60, 0x8a, 0x3a, 0xda, 0xb9, 0xae,
- 0xda, 0xbf, 0xae, 0x60, 0x9f, 0x84, 0x43, 0xfd, 0xc0, 0x76, 0xf0, 0xe0,
- 0xa3, 0x78, 0xef, 0x19, 0x0b, 0x40, 0x5c, 0x16, 0x16, 0x0f, 0x24, 0x1f,
- 0x06, 0x0c, 0x2c, 0x2b, 0x04, 0x40, 0x50, 0x0e, 0x2a, 0x04, 0x07, 0x0f,
- 0x12, 0xe0, 0xa2, 0xbf, 0xfa, 0x04, 0xe0, 0x3f, 0xd1, 0x73, 0x64, 0xe5,
- 0xe0, 0xa4, 0x53, 0xf9, 0x06, 0x40, 0x43, 0xe0, 0xa2, 0x9d, 0xef, 0x0d,
- 0x18, 0x04, 0x03, 0x08, 0x04, 0x06, 0x60, 0x60, 0x9f, 0xe0, 0x3e, 0xca,
- 0xf4, 0x03, 0x04, 0x85, 0xf3, 0xe0, 0x7e, 0xe7, 0x6f, 0xed, 0xe0, 0xa3,
- 0x81, 0xe1, 0x04, 0xe0, 0xa4, 0x93, 0xae, 0x60, 0xa0, 0x0e, 0xc3, 0x08,
- 0x73, 0xe1, 0xd9, 0x1e, 0xef, 0xc5, 0x28, 0xee, 0x60, 0x89, 0x4e, 0x4f,
- 0xbb, 0xca, 0x40, 0xeb, 0xe0, 0x59, 0xb1, 0x68, 0x61, 0xf3, 0xe0, 0x29,
- 0xa7, 0xe1, 0xe0, 0x98, 0xf6, 0xe1, 0x05, 0x08, 0xe0, 0xa3, 0x49, 0x6d,
- 0x61, 0xae, 0x60, 0x99, 0xf6, 0xc9, 0x62, 0xeb, 0xe0, 0x99, 0x77, 0xf7,
- 0x02, 0x8e, 0xee, 0x06, 0x60, 0xa2, 0x2a, 0xc2, 0x2a, 0x6e, 0x65, 0x77,
- 0xf3, 0xe0, 0x97, 0xe1, 0x61, 0xe4, 0xe0, 0x39, 0xbd, 0xf5, 0x02, 0x8f,
- 0xf2, 0x04, 0xe0, 0xa2, 0x6d, 0x69, 0x73, 0x6d, 0xae, 0x60, 0x75, 0x5c,
- 0xe0, 0x2b, 0x25, 0xe3, 0xe0, 0x6f, 0x1b, 0xf4, 0x04, 0xe0, 0x7d, 0x40,
- 0x74, 0x6f, 0x72, 0x69, 0xae, 0x60, 0x88, 0x3d, 0xda, 0xdc, 0xf3, 0x09,
- 0x07, 0x05, 0x60, 0x4d, 0xa9, 0xe0, 0x54, 0xfa, 0x68, 0xe9, 0x60, 0x9e,
- 0x58, 0xc0, 0xaa, 0x63, 0xe1, 0xe0, 0x9c, 0xe5, 0xe1, 0x04, 0xe0, 0x9e,
- 0xfd, 0x73, 0x68, 0x69, 0x6d, 0x69, 0xfa, 0xe0, 0x31, 0xed, 0xf2, 0x06,
- 0x03, 0x0a, 0xe0, 0x22, 0xcc, 0xf3, 0xd7, 0xe9, 0xe9, 0x02, 0x84, 0x6e,
- 0xef, 0xd8, 0x21, 0xe4, 0xce, 0x8b, 0xe1, 0x04, 0xe0, 0xa2, 0x45, 0x68,
- 0x69, 0xed, 0xe0, 0x5f, 0x59, 0xf0, 0x60, 0x9c, 0x09, 0xc7, 0xd6, 0xef,
- 0x04, 0xe0, 0x9f, 0xd3, 0xec, 0x60, 0x60, 0xca, 0xe0, 0x41, 0x37, 0xee,
- 0x09, 0x0a, 0x06, 0x09, 0x4e, 0x49, 0xe0, 0x61, 0x8e, 0xef, 0x04, 0xe0,
- 0x6c, 0x3c, 0x73, 0xe8, 0xe0, 0x3e, 0xa3, 0x6b, 0x6f, 0x74, 0xf3, 0xdc,
- 0x68, 0x64, 0x61, 0x62, 0x61, 0x79, 0xe1, 0xe0, 0x5f, 0x87, 0xe1, 0x04,
- 0xe0, 0x53, 0xe3, 0xed, 0xe0, 0x6b, 0x31, 0xed, 0x05, 0x04, 0x16, 0xd0,
- 0x3f, 0x6f, 0xe2, 0xce, 0x38, 0xe9, 0x0d, 0x04, 0x60, 0x29, 0xd7, 0x5f,
- 0xb0, 0x60, 0x22, 0x27, 0xe0, 0x35, 0x51, 0xf9, 0xe0, 0x6a, 0xc5, 0xeb,
- 0xe0, 0x75, 0x1c, 0xe1, 0x04, 0xe0, 0xa2, 0x62, 0x6b, 0x6f, 0xed, 0xe0,
- 0x91, 0xe2, 0xec, 0xe0, 0x7d, 0xd4, 0xeb, 0x08, 0x04, 0x15, 0x0f, 0x07,
- 0xe0, 0x71, 0x53, 0xf9, 0xe0, 0x44, 0xe9, 0xf5, 0x02, 0x87, 0x79, 0x61,
- 0x6d, 0xe1, 0xe0, 0xa1, 0xe2, 0x73, 0x68, 0x69, 0x6d, 0x61, 0xae, 0x60,
- 0x9e, 0xa6, 0xc3, 0xa1, 0xef, 0x02, 0x86, 0x72, 0x6f, 0xfa, 0xe0, 0x27,
- 0xe2, 0x6e, 0x61, 0xed, 0xe0, 0x97, 0xc9, 0xe9, 0x60, 0x27, 0xd7, 0xe0,
- 0x73, 0xa4, 0xe1, 0x03, 0x06, 0x87, 0x73, 0x68, 0xe9, 0xe0, 0x53, 0x71,
- 0x6d, 0x61, 0x63, 0xe8, 0xe0, 0x51, 0xda, 0x69, 0xae, 0x60, 0x9c, 0x5a,
- 0xc2, 0x4a, 0xe8, 0x06, 0x60, 0x87, 0xce, 0xcf, 0x68, 0x6e, 0x6f, 0x73,
- 0xe8, 0xe0, 0xa1, 0xd3, 0xe7, 0x07, 0x05, 0x08, 0x04, 0xe0, 0x9b, 0x5e,
- 0x75, 0xf2, 0xe0, 0x87, 0xaf, 0x6c, 0x69, 0x61, 0x74, 0xf4, 0xe0, 0x2f,
- 0xdf, 0xe9, 0xe0, 0x46, 0x20, 0xe1, 0x05, 0x05, 0xe0, 0x6a, 0xb9, 0x6e,
- 0xe5, 0xe0, 0xa1, 0xb0, 0x6b, 0x75, 0xf3, 0xe0, 0xa0, 0x65, 0xe5, 0xe0,
- 0x9e, 0x64, 0x64, 0xe1, 0x60, 0x98, 0x58, 0xc8, 0xec, 0x63, 0x68, 0xe9,
- 0x04, 0xe0, 0x9f, 0x5d, 0x67, 0x69, 0xae, 0x60, 0x98, 0x04, 0xc9, 0xc3,
- 0xe2, 0x05, 0x07, 0xe0, 0x4d, 0x86, 0x69, 0x73, 0x68, 0xe9, 0xe0, 0x56,
- 0x2a, 0xe5, 0x60, 0x9e, 0xc2, 0x98, 0xae, 0x0a, 0x60, 0x97, 0x12, 0x41,
- 0x56, 0x44, 0xe0, 0xc4, 0x03, 0xe7, 0x60, 0xa1, 0x04, 0xc1, 0x8e, 0xee,
- 0x04, 0xe0, 0xa2, 0xa9, 0xae, 0x06, 0x60, 0x9e, 0x6c, 0xc2, 0xcd, 0x6f,
- 0xf8, 0xe0, 0x52, 0x56, 0xed, 0x08, 0x60, 0x72, 0xf9, 0x5f, 0xac, 0xcf,
- 0xef, 0xae, 0x12, 0x60, 0x92, 0x1c, 0x45, 0x27, 0x20, 0x47, 0x63, 0x18,
- 0x11, 0x40, 0x68, 0x42, 0x37, 0x08, 0xc0, 0x82, 0xed, 0x60, 0x97, 0x3c,
- 0xcb, 0x43, 0xec, 0x04, 0xe0, 0xa2, 0x76, 0x6f, 0xee, 0xe0, 0x98, 0x24,
- 0xeb, 0x05, 0x1b, 0xe0, 0xa2, 0x50, 0x73, 0x61, 0xf4, 0xe0, 0xa0, 0x48,
- 0xea, 0x0b, 0x04, 0x05, 0x06, 0x60, 0x8f, 0xc0, 0x4b, 0xcb, 0xc6, 0xbf,
- 0x1f, 0x43, 0xf8, 0x82, 0x6f, 0xed, 0xe0, 0xa1, 0x5e, 0x6d, 0x61, 0xf8,
- 0xe0, 0x9b, 0x8e, 0x65, 0x6c, 0xe4, 0xe0, 0x8c, 0xa1, 0xe9, 0x09, 0x07,
- 0x09, 0x0c, 0x06, 0x05, 0x0a, 0xdf, 0x09, 0xf2, 0x60, 0x69, 0x54, 0xe0,
- 0x2d, 0x8b, 0xee, 0x04, 0xe0, 0x9c, 0xf0, 0xe7, 0xe0, 0x97, 0x2a, 0x6d,
- 0xe5, 0x03, 0xca, 0x27, 0x6b, 0x65, 0x65, 0xf0, 0xe0, 0x8b, 0xe3, 0x66,
- 0x66, 0xe1, 0xe0, 0x80, 0x83, 0x65, 0xee, 0xe0, 0x97, 0x57, 0x63, 0x6b,
- 0x65, 0x74, 0xf3, 0x60, 0xa1, 0x9d, 0xc0, 0x71, 0xe1, 0xe0, 0xa1, 0x02,
- 0xe8, 0x09, 0x08, 0x11, 0x17, 0x60, 0xa0, 0x16, 0xc1, 0xb5, 0x72, 0x75,
- 0x68, 0x65, 0xf2, 0xe0, 0x94, 0xb3, 0xe9, 0x04, 0xe0, 0x7a, 0x00, 0x6e,
- 0x67, 0x64, 0x75, 0x73, 0x74, 0x64, 0x61, 0xf4, 0xe0, 0x6b, 0x7a, 0xe5,
- 0x08, 0x07, 0x60, 0x4a, 0x40, 0xe0, 0x56, 0xd9, 0x77, 0x6f, 0x72, 0xeb,
- 0xe0, 0x5b, 0x58, 0x61, 0xf4, 0x60, 0x87, 0x97, 0xd7, 0xff, 0x61, 0x74,
- 0x2e, 0xf7, 0xc4, 0xfb, 0xe7, 0x04, 0xe0, 0xa1, 0xc0, 0xef, 0xe0, 0x3b,
- 0x4d, 0xe5, 0x0f, 0x09, 0x19, 0x16, 0x18, 0x20, 0x2f, 0x27, 0x60, 0x7d,
- 0xa6, 0x53, 0x0d, 0xc4, 0x54, 0xf8, 0x04, 0xe0, 0x7c, 0x5f, 0xf4, 0xe0,
- 0x36, 0xfc, 0xf3, 0x02, 0x8e, 0xf4, 0x02, 0x87, 0xae, 0x60, 0x2c, 0x15,
- 0xe0, 0x72, 0x98, 0xad, 0xe0, 0x3a, 0x22, 0x68, 0x69, 0x6b, 0x61, 0xe7,
- 0xe0, 0xa0, 0x45, 0xf2, 0x05, 0x0a, 0xe0, 0x90, 0x8d, 0xee, 0x04, 0xe0,
- 0x9a, 0x31, 0x6f, 0x70, 0xe9, 0xce, 0xf6, 0x6d, 0x65, 0xfa, 0xe0, 0xa0,
- 0x26, 0xee, 0x07, 0x04, 0x04, 0x04, 0xe0, 0x5e, 0x9f, 0xee, 0xe0, 0x62,
- 0xe5, 0xeb, 0xe0, 0x29, 0x20, 0xe5, 0xe0, 0x92, 0xb3, 0xe4, 0xe0, 0x2f,
- 0x2a, 0xed, 0x02, 0x99, 0xf0, 0x03, 0x06, 0x8b, 0x75, 0x72, 0xec, 0xe0,
- 0x6b, 0x56, 0x69, 0xef, 0x02, 0x81, 0x2d, 0x6f, 0x6c, 0xe2, 0xe0, 0x9b,
- 0xa4, 0xad, 0xe0, 0x77, 0x9c, 0x61, 0xf3, 0xce, 0x88, 0xec, 0x06, 0x60,
- 0x91, 0x06, 0xd0, 0x2f, 0xe5, 0x05, 0x0b, 0x0f, 0xc7, 0x5e, 0x6b, 0x6f,
- 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0xeb, 0xe0, 0x97, 0x18, 0x62, 0x69, 0x74,
- 0xae, 0x06, 0x60, 0xa0, 0x46, 0xc0, 0x64, 0x78, 0xf9, 0xe0, 0x83, 0x45,
- 0x2e, 0x61, 0x6d, 0x75, 0xee, 0xe0, 0x8c, 0xb2, 0xe3, 0x03, 0x05, 0x96,
- 0x6e, 0xef, 0xe0, 0x75, 0x28, 0xe8, 0x05, 0x08, 0xe0, 0xa0, 0xf6, 0x6e,
- 0x6f, 0x6c, 0x6f, 0xe7, 0xe0, 0x9e, 0xcb, 0x2e, 0x6f, 0x72, 0x61, 0xee,
- 0xe0, 0x80, 0xfc, 0xae, 0x60, 0x96, 0xc6, 0x42, 0x84, 0xc6, 0xeb, 0xe1,
- 0x04, 0xe0, 0x9f, 0x26, 0x63, 0x68, 0x65, 0x73, 0x2d, 0x79, 0xef, 0xe0,
- 0x6a, 0x5e, 0xe4, 0x60, 0x9f, 0x06, 0xc1, 0xd0, 0xe3, 0x0a, 0x60, 0x97,
- 0x8f, 0x47, 0xb7, 0x40, 0xc7, 0xc0, 0xb9, 0xf0, 0xe0, 0x58, 0x45, 0x62,
- 0x69, 0xf4, 0xe0, 0x3e, 0x55, 0xe1, 0x18, 0x0b, 0x0a, 0x38, 0x08, 0x25,
- 0x05, 0x1f, 0x29, 0x40, 0xc8, 0x07, 0x3d, 0x03, 0x0e, 0x13, 0x0f, 0x60,
- 0x93, 0x6a, 0x45, 0x91, 0xc4, 0x38, 0xf8, 0x04, 0xe0, 0xa0, 0x9e, 0xe9,
- 0x60, 0x9f, 0xe4, 0xc0, 0xb9, 0x77, 0x61, 0x72, 0x61, 0x6d, 0x6f, 0xf4,
- 0xe0, 0x5c, 0x3e, 0xf4, 0x04, 0x05, 0x0a, 0x9a, 0x74, 0xef, 0xe0, 0x9f,
- 0x9d, 0x73, 0x75, 0x6e, 0x6f, 0xae, 0x60, 0x9c, 0x75, 0xc1, 0x84, 0xe5,
- 0x03, 0x0a, 0x86, 0x79, 0x61, 0x6d, 0x61, 0xae, 0x60, 0x96, 0x00, 0xc9,
- 0x2e, 0x73, 0x68, 0xe9, 0xe0, 0x56, 0xe8, 0x62, 0x61, 0xf9, 0xe0, 0x3b,
- 0xfa, 0xe1, 0x04, 0xe0, 0x9f, 0xa4, 0x6d, 0xef, 0xe0, 0x7e, 0x0e, 0xf3,
- 0x03, 0xd8, 0x60, 0xe8, 0xe0, 0x84, 0xc4, 0xf2, 0x04, 0x0c, 0x04, 0x87,
- 0xf5, 0x04, 0xe0, 0x98, 0x7d, 0x6d, 0x69, 0x7a, 0xf5, 0xe0, 0x9a, 0xec,
- 0xee, 0xe0, 0x45, 0x19, 0xe7, 0x60, 0x2a, 0x91, 0xe0, 0x63, 0x82, 0xe1,
- 0x60, 0x43, 0x01, 0x60, 0x53, 0x81, 0xc5, 0xfc, 0x6f, 0xe2, 0xe0, 0x74,
- 0x22, 0xee, 0x05, 0x04, 0xe0, 0x98, 0x64, 0x6f, 0xe8, 0xd5, 0x1a, 0xe1,
- 0x07, 0x05, 0x60, 0x8a, 0xb0, 0xd4, 0x69, 0x67, 0xf5, 0xe0, 0x46, 0x09,
- 0x62, 0x65, 0xae, 0x60, 0x92, 0xf6, 0xc3, 0x5e, 0xed, 0x02, 0x84, 0xe2,
- 0xe0, 0x5e, 0x37, 0xe1, 0x0a, 0x04, 0x09, 0x04, 0x60, 0x4b, 0x41, 0xe0,
- 0x51, 0xf6, 0xf9, 0xe0, 0x62, 0xd1, 0x74, 0x73, 0x75, 0x6b, 0x75, 0xf2,
- 0xe0, 0x84, 0x89, 0xed, 0xe0, 0x4f, 0x79, 0xeb, 0x50, 0x9e, 0xe0, 0x35,
- 0xb6, 0xeb, 0x0a, 0x03, 0x12, 0x0f, 0x60, 0x49, 0x49, 0xe0, 0x55, 0x1f,
- 0xeb, 0xca, 0x12, 0xe9, 0x07, 0x60, 0x55, 0x2a, 0xe0, 0x40, 0x16, 0x6e,
- 0xef, 0x04, 0xe0, 0x9b, 0xb5, 0xf5, 0xe0, 0x83, 0xe8, 0xe5, 0x02, 0x89,
- 0xf4, 0x04, 0xe0, 0x89, 0x50, 0xef, 0xe0, 0x5f, 0xf2, 0xe8, 0xc8, 0xf0,
- 0xe1, 0x0e, 0x06, 0x0e, 0x14, 0x12, 0x08, 0x05, 0x0d, 0x0f, 0x04, 0x1f,
- 0xe0, 0x9b, 0x0d, 0x7a, 0x61, 0xeb, 0xe0, 0x47, 0xe6, 0x79, 0x61, 0x6d,
- 0x61, 0xae, 0x04, 0xe0, 0x9d, 0x09, 0xe7, 0x60, 0x97, 0xca, 0x96, 0xf4,
- 0x05, 0x0a, 0xe0, 0x5e, 0xa4, 0x73, 0x75, 0x6b, 0x69, 0xae, 0x60, 0x9a,
- 0xf2, 0xc3, 0x53, 0xef, 0xe0, 0x5c, 0xb8, 0xf3, 0x05, 0x05, 0xe0, 0x9b,
- 0x84, 0x68, 0xe9, 0xe0, 0x32, 0xb1, 0xe1, 0x60, 0x4f, 0xb4, 0xe0, 0x45,
- 0x4d, 0x72, 0x61, 0x7a, 0x75, 0xeb, 0xe0, 0x5d, 0x95, 0x6f, 0xeb, 0xe0,
- 0x4c, 0x2e, 0xee, 0x02, 0x85, 0x65, 0xfa, 0xe0, 0x49, 0xbc, 0x61, 0xe2,
- 0xe0, 0x62, 0xc1, 0xed, 0x02, 0x89, 0x6f, 0x72, 0x69, 0xae, 0x60, 0x98,
- 0xd6, 0xc3, 0xe7, 0xe1, 0xc0, 0x86, 0xe9, 0xe0, 0x5b, 0x13, 0x68, 0xe1,
- 0x07, 0x04, 0x05, 0x09, 0xe0, 0x66, 0xac, 0xf3, 0xe0, 0x94, 0x95, 0x72,
- 0xf5, 0xe0, 0x92, 0x18, 0x6d, 0x61, 0xae, 0x60, 0x73, 0x53, 0xe0, 0x27,
- 0x4b, 0xe7, 0xe0, 0x83, 0xbb, 0xe7, 0xe0, 0x9c, 0x8f, 0x6a, 0xe9, 0x60,
- 0x33, 0xc2, 0xce, 0x2d, 0xe9, 0x0c, 0x0f, 0x04, 0x05, 0x05, 0x08, 0x05,
- 0x60, 0x5a, 0x96, 0xcb, 0x7b, 0x73, 0x68, 0xe9, 0x02, 0x84, 0xee, 0xe0,
- 0x9a, 0x52, 0xae, 0x60, 0x9a, 0xec, 0xc2, 0xd0, 0xf2, 0xe0, 0x4b, 0xc4,
- 0x70, 0xe5, 0xe0, 0x9d, 0x6b, 0x6e, 0xe1, 0xe0, 0x4d, 0x93, 0x6b, 0x69,
- 0xae, 0x60, 0x94, 0x73, 0xc9, 0x55, 0x6a, 0xe9, 0xe0, 0x95, 0x22, 0x66,
- 0x75, 0xee, 0xe0, 0x78, 0xfa, 0xe8, 0xc8, 0x41, 0x67, 0xe1, 0x07, 0x60,
- 0x30, 0xd5, 0xe0, 0x28, 0xff, 0x6a, 0xef, 0xe0, 0x65, 0xff, 0xe4, 0x02,
- 0x8b, 0xef, 0x04, 0xe0, 0x54, 0x1b, 0x74, 0x73, 0xf5, 0xe0, 0x91, 0x99,
- 0x61, 0xef, 0xe0, 0x83, 0x79, 0x63, 0x68, 0xe9, 0x02, 0x84, 0xeb, 0xe0,
- 0x72, 0x69, 0x61, 0x72, 0xe1, 0xe0, 0x53, 0xe6, 0xe2, 0x06, 0x06, 0x09,
- 0xe0, 0x9e, 0x87, 0x75, 0x73, 0xe5, 0xe0, 0x9d, 0x16, 0x69, 0x74, 0x6f,
- 0x72, 0x64, 0x65, 0xf2, 0xd5, 0x75, 0x61, 0x79, 0x61, 0xed, 0xe0, 0x66,
- 0x8b, 0x33, 0x6c, 0x33, 0x70, 0x30, 0xf2, 0xe0, 0x92, 0xba, 0xf3, 0x2b,
- 0x28, 0x1e, 0x40, 0x53, 0x06, 0x35, 0x2d, 0x40, 0xf2, 0x41, 0xc4, 0x1d,
- 0x0f, 0x10, 0x40, 0x62, 0x40, 0xfe, 0x2b, 0x3a, 0x22, 0x40, 0x9f, 0x07,
- 0x40, 0x88, 0x42, 0xc0, 0x09, 0x05, 0x42, 0x03, 0x16, 0x41, 0x44, 0x0a,
- 0x42, 0xe5, 0x05, 0xe0, 0x62, 0xcb, 0x1f, 0xc3, 0x05, 0x1b, 0xe0, 0x8f,
- 0xb7, 0xf8, 0x04, 0x09, 0x05, 0x84, 0xf2, 0x44, 0xa8, 0x0a, 0x4a, 0x11,
- 0xe0, 0x78, 0x67, 0x6e, 0x64, 0xf2, 0xc4, 0xd0, 0xed, 0xe0, 0x94, 0x56,
- 0xe7, 0xe0, 0x88, 0x3e, 0x61, 0xec, 0x60, 0x8c, 0x71, 0x82, 0xfa, 0x06,
- 0x04, 0x05, 0xe0, 0x9e, 0x13, 0xeb, 0xe0, 0x42, 0x92, 0x65, 0xf8, 0xe0,
- 0x9d, 0xac, 0x63, 0xfa, 0x02, 0x85, 0x79, 0xf4, 0xe0, 0x62, 0x05, 0x65,
- 0xe3, 0xe0, 0x8d, 0x6f, 0xf9, 0x09, 0x0f, 0x2d, 0x08, 0x60, 0x90, 0x5c,
- 0xcd, 0x5b, 0xf3, 0x02, 0x85, 0x74, 0xe5, 0xe0, 0x80, 0xad, 0x2e, 0x71,
- 0x63, 0xf8, 0xe0, 0x90, 0xa1, 0xee, 0x02, 0xa2, 0xef, 0x02, 0x9a, 0x6c,
- 0x6f, 0x67, 0xf9, 0x04, 0xe0, 0x88, 0x91, 0x2d, 0xe4, 0x04, 0xe0, 0x78,
- 0x02, 0x69, 0x73, 0x6b, 0x73, 0x74, 0x61, 0x74, 0x69, 0xef, 0xe0, 0x73,
- 0x8e, 0x2d, 0xe4, 0xe0, 0x77, 0xf4, 0x63, 0x6c, 0x6f, 0x75, 0xe4, 0xe0,
- 0x9d, 0x6c, 0x6b, 0x6b, 0x79, 0x6c, 0xf6, 0xe0, 0x8f, 0x64, 0x64, 0xee,
- 0xe0, 0x31, 0x71, 0xf8, 0x60, 0x8b, 0x27, 0xd2, 0x8a, 0xf7, 0x03, 0x1e,
- 0x8e, 0xe9, 0x06, 0x08, 0x06, 0xe0, 0x96, 0xed, 0x6e, 0x6f, 0x75, 0x6a,
- 0x73, 0xe3, 0xd9, 0x19, 0x65, 0x62, 0xef, 0xe0, 0x8c, 0xfb, 0x64, 0x6e,
- 0xe9, 0x60, 0x3d, 0x59, 0xe0, 0x26, 0x2d, 0xe5, 0x04, 0xe0, 0x88, 0xb6,
- 0x65, 0x74, 0x70, 0x65, 0x70, 0xf0, 0xe0, 0x4f, 0x7c, 0x61, 0xf4, 0xe0,
- 0x88, 0xbb, 0xf6, 0x0c, 0x05, 0x06, 0x0b, 0x60, 0x5e, 0x07, 0x60, 0x3d,
- 0xe3, 0xc1, 0x6a, 0x6e, 0xad, 0xe0, 0x62, 0x33, 0x69, 0x7a, 0x7a, 0xe5,
- 0xcd, 0x6c, 0xe5, 0x02, 0x84, 0xec, 0xe0, 0x63, 0xa8, 0xe9, 0xe0, 0x96,
- 0x21, 0x63, 0x2e, 0x66, 0x69, 0x72, 0x65, 0xee, 0xe0, 0x6a, 0x1f, 0xf5,
- 0x12, 0x11, 0x0b, 0x0e, 0x1e, 0x27, 0x13, 0x15, 0x0b, 0x0b, 0x0f, 0x0c,
- 0x06, 0x05, 0x04, 0xe0, 0x9c, 0x60, 0xfa, 0x04, 0xe0, 0x53, 0x74, 0xf5,
- 0x04, 0xe0, 0x96, 0x18, 0xeb, 0x60, 0x47, 0xee, 0xe0, 0x53, 0xbe, 0x77,
- 0xe1, 0x04, 0xe0, 0x9a, 0x99, 0x6c, 0xeb, 0xe0, 0x27, 0x77, 0xf3, 0x02,
- 0x86, 0x6f, 0x6e, 0xef, 0xe0, 0x93, 0xb8, 0x61, 0xeb, 0xe0, 0x62, 0xcb,
- 0xf2, 0x06, 0x04, 0x05, 0xe0, 0x92, 0xda, 0xf2, 0xe0, 0x8c, 0x09, 0x6e,
- 0xe1, 0xe0, 0x96, 0xc9, 0x67, 0xe5, 0x04, 0xe0, 0x76, 0x7e, 0x6f, 0x6e,
- 0x73, 0x68, 0xe1, 0xe0, 0x6e, 0xf8, 0xf0, 0x03, 0x0b, 0x88, 0xf0, 0x03,
- 0xd2, 0x96, 0xec, 0x60, 0x44, 0x7e, 0xe0, 0x56, 0xc7, 0x65, 0x72, 0x73,
- 0x61, 0xec, 0xe0, 0x8f, 0x0e, 0x61, 0x62, 0x61, 0x73, 0x65, 0xae, 0x06,
- 0x60, 0x84, 0x24, 0xd8, 0x85, 0xe9, 0xe0, 0x89, 0x66, 0xee, 0x07, 0x60,
- 0x47, 0x10, 0xe0, 0x52, 0x55, 0xee, 0x04, 0xe0, 0x96, 0x84, 0x79, 0x64,
- 0xe1, 0xe0, 0x95, 0x1f, 0xed, 0x05, 0x09, 0xe0, 0x81, 0xc2, 0x6f, 0x74,
- 0x6f, 0xae, 0x60, 0x96, 0x3e, 0xc2, 0x63, 0xe9, 0x51, 0xa4, 0xe0, 0x26,
- 0xb5, 0xec, 0x06, 0x60, 0x96, 0x62, 0xc1, 0x06, 0xe9, 0xe0, 0x9c, 0x2e,
- 0xeb, 0x02, 0x85, 0x75, 0xed, 0xe0, 0x58, 0x5d, 0xe1, 0xcd, 0x48, 0xe9,
- 0x05, 0x04, 0xe0, 0x57, 0xb3, 0xf3, 0xe0, 0x66, 0xdf, 0x66, 0xf5, 0xe0,
- 0x95, 0xaf, 0x67, 0xe9, 0x04, 0xe0, 0x46, 0xce, 0x6e, 0x61, 0xed, 0xe0,
- 0x99, 0xc6, 0xe5, 0x60, 0x85, 0xcc, 0xc8, 0x36, 0x63, 0xeb, 0xe0, 0x9a,
- 0x94, 0xe2, 0xe0, 0x94, 0xcb, 0x2e, 0x70, 0x61, 0x62, 0xe1, 0xe0, 0x4f,
- 0x8b, 0xf4, 0x13, 0x28, 0x18, 0x08, 0x40, 0x5b, 0x1c, 0x08, 0x0a, 0x16,
- 0x07, 0x60, 0x3a, 0x5a, 0x60, 0x60, 0x08, 0xc0, 0xf4, 0xf5, 0x03, 0x06,
- 0x96, 0x74, 0x74, 0xe7, 0xe0, 0x91, 0x30, 0x66, 0xe6, 0x02, 0x88, 0x74,
- 0x6f, 0x72, 0x65, 0xe1, 0xe0, 0x97, 0x9b, 0x2d, 0x34, 0x2d, 0x73, 0x61,
- 0x6c, 0xe5, 0xe0, 0x66, 0x4a, 0xe4, 0x58, 0xf1, 0x60, 0x7c, 0x3d, 0xc5,
- 0x54, 0xf2, 0x04, 0x04, 0x03, 0x84, 0xf9, 0xe0, 0x96, 0xd8, 0xe9, 0xd9,
- 0xa8, 0xe5, 0xe0, 0x95, 0x4a, 0x61, 0x6e, 0xe4, 0x60, 0x96, 0xd8, 0xc4,
- 0x3d, 0x70, 0x65, 0x74, 0x65, 0xf2, 0xe0, 0x7f, 0xd3, 0xef, 0x05, 0x40,
- 0x45, 0x04, 0x84, 0xf2, 0x08, 0x08, 0x17, 0x06, 0x11, 0xe0, 0x8d, 0x37,
- 0x6a, 0x2e, 0x66, 0x61, 0xf2, 0xe0, 0x9b, 0xe3, 0xe5, 0x05, 0x05, 0xe0,
- 0x9b, 0xda, 0x62, 0x61, 0xf3, 0xc4, 0xe6, 0xae, 0x60, 0x89, 0xb7, 0x03,
- 0x45, 0xd6, 0x42, 0x22, 0x03, 0xc9, 0x39, 0xe4, 0x60, 0x95, 0x97, 0xc5,
- 0x42, 0x61, 0x67, 0xe5, 0x04, 0xe0, 0x9b, 0xc1, 0x2e, 0x79, 0x61, 0x6e,
- 0x64, 0x65, 0xf8, 0xe0, 0x49, 0x74, 0x2d, 0x65, 0xec, 0xe0, 0x95, 0x7c,
- 0x6c, 0xef, 0xdf, 0x5f, 0xeb, 0xe0, 0x69, 0xc4, 0x63, 0x6b, 0x68, 0x6f,
- 0xec, 0xe0, 0x94, 0xbb, 0xea, 0x02, 0x84, 0x1f, 0x43, 0xf8, 0x86, 0xef,
- 0x04, 0xe0, 0x6d, 0xd0, 0x72, 0x64, 0x61, 0xec, 0x04, 0xe0, 0x9a, 0x99,
- 0x73, 0x68, 0x61, 0x6c, 0xf3, 0xe0, 0x8d, 0x32, 0x68, 0x2e, 0x61, 0x63,
- 0xae, 0xe0, 0x6b, 0x88, 0xe7, 0x05, 0x60, 0x3b, 0x9e, 0x85, 0xad, 0xe0,
- 0x74, 0x3d, 0xe5, 0x04, 0xe0, 0x80, 0x94, 0xe9, 0x05, 0x06, 0xe0, 0x8d,
- 0x0b, 0x6e, 0x6b, 0xea, 0xe0, 0x90, 0x6e, 0x65, 0xf2, 0xe0, 0x74, 0x85,
- 0xe3, 0x60, 0x3d, 0xcd, 0xe0, 0x5d, 0x8f, 0xe1, 0x0b, 0x09, 0x40, 0x4a,
- 0x1f, 0x03, 0x03, 0x0f, 0x18, 0x06, 0x8d, 0xf6, 0x04, 0xe0, 0x85, 0xe7,
- 0xe5, 0xe0, 0x8a, 0x89, 0xf4, 0x06, 0x27, 0x06, 0xe0, 0x9a, 0x19, 0xe9,
- 0x04, 0xe0, 0x91, 0x29, 0xe3, 0x03, 0x04, 0x91, 0xf3, 0xe0, 0x8c, 0x6f,
- 0xae, 0x04, 0xe0, 0x73, 0xd5, 0x6f, 0x62, 0x73, 0x65, 0x72, 0x76, 0x61,
- 0x62, 0xec, 0xe0, 0x4e, 0x40, 0x2d, 0x61, 0x63, 0x63, 0x65, 0xf3, 0xe0,
- 0x9a, 0xf1, 0x68, 0x65, 0xec, 0xe0, 0x8c, 0x79, 0xe5, 0x07, 0x08, 0x60,
- 0x79, 0x90, 0xdf, 0x43, 0x6f, 0x66, 0x64, 0x65, 0xec, 0xe0, 0x74, 0x8b,
- 0x66, 0x61, 0xf2, 0xe0, 0x99, 0x3f, 0xf2, 0x07, 0x08, 0x06, 0x06, 0xe0,
- 0x9a, 0xdb, 0x6f, 0x73, 0x74, 0x77, 0xef, 0xe0, 0x5c, 0x36, 0x6e, 0x62,
- 0xe5, 0xe0, 0x7e, 0xbb, 0x67, 0x61, 0xf2, 0xe0, 0x96, 0xf0, 0xe1, 0xd4,
- 0x6d, 0xf0, 0xc3, 0x52, 0xee, 0xce, 0x51, 0xec, 0x02, 0x87, 0x6f, 0x77,
- 0x61, 0xad, 0xe0, 0x3f, 0x41, 0x62, 0xe1, 0xe0, 0x36, 0xcb, 0xe7, 0x02,
- 0x8a, 0x69, 0x6e, 0x67, 0x2e, 0x6f, 0x6e, 0x72, 0xe5, 0xdd, 0xbc, 0x65,
- 0x2e, 0x6e, 0x6f, 0x64, 0x65, 0x61, 0xf2, 0xe0, 0x88, 0xa6, 0xe4, 0x60,
- 0x94, 0x0d, 0xc5, 0x97, 0x63, 0x6b, 0x68, 0x65, 0x72, 0x6f, 0x2d, 0x6e,
- 0x65, 0xf4, 0xe0, 0x6f, 0xf6, 0xe2, 0xe0, 0x8c, 0x05, 0xf3, 0x06, 0x60,
- 0x99, 0x23, 0xc1, 0x6a, 0x6c, 0x2e, 0x6f, 0x72, 0x69, 0x67, 0x69, 0x6e,
- 0x2e, 0x63, 0x64, 0x6e, 0x37, 0x37, 0x2d, 0x73, 0x65, 0x63, 0xf5, 0xe0,
- 0x86, 0x1b, 0xf2, 0x09, 0x55, 0xf3, 0x60, 0x82, 0xc2, 0x0a, 0xc1, 0xae,
- 0x68, 0xf4, 0xe0, 0x83, 0xa8, 0x71, 0x75, 0x61, 0x72, 0xe5, 0x04, 0xe0,
- 0x98, 0x35, 0x37, 0xae, 0x60, 0x9a, 0x1f, 0x03, 0x98, 0xf0, 0x0b, 0x0a,
- 0x0c, 0x08, 0x06, 0x09, 0x0b, 0x04, 0xe0, 0x8c, 0x4f, 0xf9, 0x04, 0xe0,
- 0x98, 0x1d, 0x64, 0xe5, 0xe0, 0x6c, 0x9e, 0xef, 0x04, 0xe0, 0x99, 0x19,
- 0x72, 0xf4, 0x60, 0x99, 0xd1, 0xc0, 0x6a, 0x6a, 0x65, 0x6c, 0x6b, 0xe1,
- 0xe0, 0x60, 0x7e, 0x68, 0x69, 0xee, 0xe0, 0x36, 0x3f, 0x65, 0x63, 0x74,
- 0x72, 0x75, 0xed, 0xe0, 0x3a, 0xdc, 0x64, 0x6e, 0x73, 0xae, 0x60, 0x95,
- 0xc9, 0x42, 0x10, 0xc2, 0x04, 0xe2, 0xe0, 0x97, 0x20, 0xe1, 0x04, 0xe0,
- 0x9a, 0x0b, 0x63, 0xe5, 0x07, 0x04, 0x60, 0x97, 0xd4, 0xc2, 0x2a, 0xeb,
- 0xe0, 0x6c, 0x9f, 0x2d, 0x74, 0x6f, 0x2d, 0xf2, 0xe0, 0x90, 0xf3, 0xef,
- 0x1e, 0x26, 0x0d, 0x25, 0x06, 0x1a, 0x06, 0x19, 0x07, 0x04, 0x07, 0x0c,
- 0x05, 0x07, 0x4a, 0x22, 0x4a, 0x7a, 0x60, 0x2b, 0x2c, 0x60, 0x55, 0x63,
- 0x42, 0x4a, 0x41, 0x38, 0xc0, 0x69, 0xf5, 0x02, 0x91, 0x74, 0xe8, 0x02,
- 0x85, 0x77, 0xe5, 0xe0, 0x84, 0x3d, 0x63, 0x61, 0x72, 0x6f, 0x6c, 0xe9,
- 0xd7, 0xed, 0x6e, 0xe4, 0x02, 0x85, 0x63, 0xe1, 0xe0, 0x22, 0x59, 0x61,
- 0x6e, 0x64, 0x76, 0x69, 0xf3, 0xe0, 0x8f, 0xa7, 0xf3, 0x07, 0x60, 0x58,
- 0x3a, 0xe0, 0x3d, 0x82, 0x6e, 0xef, 0xe0, 0x85, 0x35, 0xf2, 0x09, 0x04,
- 0x05, 0x05, 0x4a, 0x11, 0xe0, 0x78, 0x67, 0xf4, 0xe0, 0x8d, 0x71, 0x72,
- 0xe5, 0xe0, 0x21, 0x96, 0x6f, 0xe3, 0xe0, 0x75, 0xb9, 0xad, 0x06, 0x60,
- 0x21, 0xa3, 0x04, 0x86, 0x76, 0x61, 0xf2, 0xe0, 0x84, 0x24, 0x70, 0x6f,
- 0xf4, 0xe0, 0x88, 0xd3, 0xee, 0x08, 0x07, 0x60, 0x56, 0xa9, 0xe0, 0x41,
- 0x1f, 0xe7, 0x60, 0x2d, 0xf3, 0xe0, 0x6b, 0x7c, 0x64, 0xf2, 0x04, 0xe0,
- 0x81, 0x71, 0xe5, 0xe0, 0x21, 0x68, 0xed, 0x60, 0x8e, 0xce, 0xc0, 0xb6,
- 0xec, 0x03, 0x0b, 0x85, 0xf5, 0x04, 0xe0, 0x93, 0xa5, 0x74, 0x69, 0xef,
- 0xe0, 0x8a, 0x57, 0x6f, 0xe7, 0xe0, 0x35, 0xc4, 0xe1, 0x60, 0x98, 0x50,
- 0xbd, 0xeb, 0x60, 0x61, 0x2b, 0xe0, 0x2e, 0x6f, 0xea, 0xe0, 0x94, 0x40,
- 0x67, 0xee, 0x60, 0x92, 0xfc, 0xc5, 0x42, 0x66, 0xf4, 0x04, 0xe0, 0x77,
- 0xbb, 0x77, 0x61, 0xf2, 0xe0, 0x68, 0x5e, 0x65, 0xe4, 0xe0, 0x58, 0x4a,
- 0x64, 0x65, 0x67, 0xe1, 0xe0, 0x41, 0xd6, 0xe3, 0x04, 0x06, 0x04, 0x84,
- 0xe9, 0x60, 0x53, 0xec, 0xde, 0x37, 0xe8, 0xe0, 0x25, 0xe6, 0xe3, 0xe0,
- 0x86, 0x4b, 0xae, 0x05, 0x60, 0x8d, 0xbf, 0x8b, 0x73, 0x72, 0xe3, 0xc1,
- 0x4f, 0xee, 0x0a, 0x07, 0x05, 0x06, 0x04, 0x60, 0x86, 0x4d, 0xd2, 0x8a,
- 0x1f, 0x43, 0xe5, 0x18, 0xe0, 0x5f, 0x50, 0x6f, 0xe1, 0xe0, 0x5f, 0x62,
- 0x69, 0x6c, 0xec, 0xe0, 0x8a, 0x5e, 0xe3, 0xe0, 0x8e, 0xb7, 0xe1, 0x04,
- 0xe0, 0x5f, 0x50, 0x61, 0xf3, 0xe0, 0x97, 0xdb, 0xed, 0x0a, 0x05, 0x06,
- 0x03, 0x04, 0x60, 0x7d, 0xcd, 0xda, 0xe3, 0x1f, 0x43, 0xf8, 0xd8, 0xcb,
- 0x75, 0x73, 0xe8, 0xe0, 0x4e, 0x9b, 0xef, 0xd8, 0xc2, 0xe9, 0xe0, 0x97,
- 0x59, 0xe1, 0x02, 0x91, 0x72, 0xf4, 0x04, 0xe0, 0x98, 0xa7, 0x6c, 0x61,
- 0x62, 0x65, 0x6c, 0x69, 0x6e, 0xe7, 0xe0, 0x64, 0x52, 0x6c, 0x6c, 0x2d,
- 0x77, 0x65, 0xe2, 0xe0, 0x96, 0x53, 0xec, 0x0c, 0x05, 0x06, 0x60, 0x79,
- 0x8d, 0x4a, 0x4a, 0x43, 0x2b, 0xd1, 0x79, 0x75, 0xf0, 0xe0, 0x37, 0xf4,
- 0x64, 0xae, 0x60, 0x97, 0x1e, 0x84, 0xe1, 0x04, 0xe0, 0x37, 0xe6, 0x74,
- 0xf4, 0xe0, 0x81, 0x60, 0xeb, 0x0c, 0x0d, 0x15, 0x10, 0x04, 0x14, 0x22,
- 0x10, 0x10, 0xe0, 0x97, 0xd8, 0x1f, 0xc3, 0x02, 0x85, 0x65, 0xee, 0xe0,
- 0x8c, 0x36, 0x61, 0xee, 0xc0, 0x7d, 0xf9, 0x08, 0x06, 0x60, 0x59, 0x9b,
- 0xe0, 0x3e, 0xad, 0x67, 0x65, 0xe1, 0xe0, 0x68, 0x0f, 0x64, 0x69, 0xf6,
- 0xe0, 0x89, 0xe7, 0xef, 0x08, 0x04, 0x60, 0x59, 0x81, 0xe0, 0x25, 0xcc,
- 0xe4, 0xe0, 0x66, 0x2a, 0xe3, 0xcb, 0x89, 0x6c, 0xe5, 0xd8, 0xea, 0xea,
- 0x05, 0x06, 0xe0, 0x6e, 0x5c, 0x1f, 0x43, 0xe5, 0xe0, 0x8e, 0x86, 0x65,
- 0x72, 0xf6, 0x60, 0x8d, 0x21, 0xc3, 0x71, 0xe9, 0x08, 0x06, 0x0d, 0x60,
- 0x96, 0x60, 0xc1, 0x9e, 0x70, 0x74, 0xf6, 0xe0, 0x50, 0x8e, 0xe5, 0x04,
- 0xe0, 0x92, 0xc8, 0x72, 0xf6, 0x60, 0x57, 0xe3, 0xe0, 0x3a, 0xf0, 0xae,
- 0x60, 0x95, 0xd4, 0xc1, 0x36, 0x65, 0x64, 0x73, 0x6d, 0xef, 0x04, 0xe0,
- 0x96, 0xfb, 0x6b, 0x6f, 0x72, 0xf3, 0xe0, 0x50, 0x6b, 0xe1, 0x02, 0x84,
- 0xf5, 0xe0, 0x92, 0xa6, 0xee, 0x04, 0xe0, 0x8b, 0xb4, 0xe9, 0xe0, 0x91,
- 0x01, 0xae, 0x60, 0x92, 0x71, 0xc4, 0x5f, 0xea, 0x60, 0x67, 0xe2, 0xe0,
- 0x2f, 0xef, 0xe9, 0x0f, 0x29, 0x09, 0x0e, 0x07, 0x0d, 0x03, 0x04, 0x0a,
- 0x0c, 0x60, 0x85, 0xdd, 0xd1, 0x6d, 0x74, 0xe5, 0x06, 0x05, 0x07, 0xe0,
- 0x97, 0xa7, 0x73, 0xae, 0xe0, 0x70, 0x57, 0x6c, 0x65, 0x61, 0xe6, 0xe0,
- 0x97, 0x86, 0x2e, 0xf4, 0x02, 0x88, 0x72, 0x61, 0x6e, 0x73, 0xe9, 0xe0,
- 0x64, 0xcf, 0x62, 0x2d, 0x68, 0x6f, 0x73, 0xf4, 0xe0, 0x8b, 0x2b, 0xf2,
- 0x04, 0xe0, 0x91, 0x56, 0x61, 0xe3, 0xce, 0xe7, 0xee, 0x02, 0x85, 0x67,
- 0xec, 0xe0, 0x8c, 0x2a, 0xe1, 0x60, 0x97, 0x22, 0xc0, 0x5e, 0x6d, 0x70,
- 0x6c, 0xe5, 0xe0, 0x23, 0xfd, 0xec, 0x02, 0x86, 0xeb, 0x60, 0x95, 0x46,
- 0xc2, 0x2a, 0xea, 0xe0, 0x25, 0x7b, 0xe9, 0xd8, 0xa2, 0xe7, 0xe0, 0x91,
- 0x2c, 0xe5, 0x04, 0xe0, 0x90, 0x30, 0x6c, 0xec, 0xe0, 0x6d, 0x93, 0xe3,
- 0x04, 0xe0, 0x95, 0xe7, 0x69, 0xec, 0x60, 0x8f, 0x44, 0xc2, 0x68, 0x62,
- 0x65, 0x6e, 0xe9, 0xe0, 0x8f, 0x90, 0xe8, 0x0d, 0x04, 0x07, 0x40, 0x70,
- 0x42, 0x07, 0x0e, 0x60, 0x82, 0x1b, 0xd2, 0x8a, 0xf7, 0xe0, 0x96, 0xc3,
- 0x75, 0x6e, 0x61, 0xee, 0xe0, 0x95, 0xb0, 0xef, 0x0b, 0x16, 0x05, 0x3a,
- 0x09, 0x60, 0x42, 0x20, 0xe0, 0x49, 0x46, 0xf7, 0x07, 0x05, 0x60, 0x94,
- 0xc1, 0xc2, 0x50, 0x74, 0xe9, 0xe0, 0x7a, 0xda, 0x61, 0xae, 0x60, 0x7a,
- 0xb0, 0x54, 0xb0, 0xc3, 0x08, 0x75, 0xea, 0xe0, 0x95, 0x86, 0xf0, 0x09,
- 0x06, 0x06, 0x04, 0x05, 0x06, 0xe0, 0x96, 0xde, 0x77, 0x61, 0x72, 0xe5,
- 0xc3, 0xea, 0x73, 0x65, 0xec, 0xe0, 0x27, 0xe7, 0xf0, 0xe0, 0x82, 0x48,
- 0x69, 0x74, 0xf3, 0xd9, 0x79, 0x61, 0x72, 0xe5, 0xe0, 0x36, 0xb9, 0xae,
- 0x0b, 0x60, 0x4b, 0x30, 0x44, 0x02, 0x60, 0x38, 0x3c, 0xcb, 0xa9, 0x62,
- 0x72, 0x65, 0x6e, 0x64, 0x6c, 0xf9, 0xd6, 0x99, 0x6e, 0x61, 0x69, 0xae,
- 0x60, 0x8b, 0xe9, 0xc0, 0x65, 0x62, 0x61, 0xf2, 0xe0, 0x71, 0x7e, 0xe9,
- 0x18, 0x12, 0x05, 0x11, 0x40, 0x4b, 0x13, 0x40, 0x6e, 0x40, 0x7a, 0x29,
- 0x0b, 0x05, 0x16, 0x0f, 0x60, 0x29, 0x7e, 0x60, 0x66, 0xcd, 0xc3, 0x84,
- 0x7a, 0xf5, 0x02, 0x89, 0x6f, 0x6b, 0x61, 0xae, 0x60, 0x8d, 0x3d, 0xc8,
- 0x4d, 0x6b, 0xf5, 0xe0, 0x41, 0x63, 0x74, 0xe1, 0xe0, 0x52, 0x52, 0xf3,
- 0x05, 0x04, 0xe0, 0x91, 0x45, 0xf5, 0xe0, 0x7a, 0x71, 0x68, 0x69, 0x6b,
- 0xf5, 0xe0, 0x28, 0xba, 0xf2, 0x03, 0x18, 0x84, 0xef, 0x02, 0x86, 0x73,
- 0x61, 0xf4, 0xe0, 0x87, 0xbb, 0xe9, 0x04, 0xe0, 0x95, 0x23, 0x73, 0x68,
- 0x69, 0xae, 0x60, 0x5d, 0xa1, 0xe0, 0x2f, 0x14, 0xe9, 0xe0, 0x3e, 0x0f,
- 0xe1, 0x05, 0x06, 0x09, 0x04, 0x8e, 0x74, 0x61, 0xeb, 0xe0, 0x5d, 0xe1,
- 0xef, 0x04, 0xe0, 0x95, 0x2e, 0xeb, 0xe0, 0x5e, 0x33, 0xee, 0xe0, 0x84,
- 0x80, 0xeb, 0x04, 0xe0, 0x94, 0xf6, 0x61, 0x77, 0x61, 0xae, 0x60, 0x8e,
- 0x72, 0xc3, 0x1e, 0xe8, 0xe0, 0x3c, 0x1b, 0xef, 0x03, 0x04, 0x86, 0xf9,
- 0xe0, 0x8b, 0x55, 0x6a, 0x69, 0xf2, 0xe0, 0x93, 0x9b, 0x67, 0xe1, 0xe0,
- 0x52, 0x31, 0xee, 0x0b, 0x0b, 0x0d, 0x0e, 0x06, 0x08, 0x11, 0x10, 0xe0,
- 0x53, 0x8f, 0x79, 0x6f, 0x73, 0x68, 0x69, 0x74, 0x6f, 0xed, 0xe0, 0x4b,
- 0x4d, 0x74, 0xef, 0x07, 0x60, 0x3e, 0x45, 0xe0, 0x50, 0x06, 0xeb, 0xe0,
- 0x92, 0x13, 0x73, 0x68, 0xe9, 0x02, 0x84, 0xf2, 0xe0, 0x8e, 0x53, 0x6e,
- 0xef, 0xe0, 0x92, 0x03, 0x6f, 0x6e, 0x73, 0xe5, 0xc6, 0x02, 0x6b, 0x61,
- 0x6d, 0x69, 0x67, 0xef, 0xd6, 0xc7, 0xea, 0x02, 0x85, 0x75, 0xeb, 0xe0,
- 0x79, 0xcd, 0x6f, 0xae, 0x60, 0x8b, 0x5a, 0x43, 0x33, 0xc2, 0x4e, 0xe7,
- 0x02, 0x89, 0x75, 0xae, 0x60, 0x8a, 0xe9, 0x41, 0x26, 0xc5, 0xac, 0xef,
- 0xe0, 0x59, 0x0d, 0xe1, 0x04, 0xe0, 0x69, 0x77, 0x6e, 0x6f, 0x6d, 0x61,
- 0xe3, 0xe0, 0x93, 0x27, 0xed, 0x04, 0x40, 0x4a, 0x89, 0xef, 0x0b, 0x0a,
- 0x03, 0x0f, 0x07, 0x04, 0x06, 0x06, 0xe0, 0x23, 0x5b, 0x74, 0x73, 0xf5,
- 0x03, 0xd9, 0x79, 0xeb, 0xe0, 0x44, 0xb1, 0xf3, 0xd8, 0x5c, 0xee, 0x02,
- 0x87, 0x6f, 0x73, 0x65, 0xeb, 0xe0, 0x54, 0xc6, 0x69, 0xf4, 0xe0, 0x8d,
- 0xd2, 0xeb, 0x60, 0x3f, 0xff, 0xe0, 0x4a, 0xfb, 0xea, 0xe0, 0x91, 0xa8,
- 0x69, 0x63, 0xe8, 0xe0, 0x52, 0xb2, 0x66, 0x75, 0xf3, 0xe0, 0x53, 0xfd,
- 0x64, 0xe1, 0x04, 0xe0, 0x8c, 0x06, 0x74, 0xe5, 0xe0, 0x8e, 0x92, 0x69,
- 0x7a, 0x75, 0xae, 0x60, 0x8b, 0xfe, 0xc8, 0x44, 0xe1, 0x08, 0x08, 0x0c,
- 0x60, 0x4a, 0x9c, 0xd2, 0xf8, 0x6e, 0x65, 0xae, 0x60, 0x90, 0x5d, 0xc3,
- 0xdd, 0xed, 0x02, 0x84, 0xef, 0xe0, 0x70, 0x04, 0x61, 0xeb, 0xe0, 0x94,
- 0x21, 0x62, 0xe1, 0xe0, 0x2f, 0x0e, 0xeb, 0x06, 0x05, 0x09, 0xe0, 0x44,
- 0x0f, 0x73, 0xe8, 0xe0, 0x94, 0x24, 0x6f, 0x6b, 0x75, 0x63, 0x68, 0xf5,
- 0xe0, 0x43, 0xf7, 0xe1, 0x0a, 0x05, 0x60, 0x4e, 0x1a, 0x43, 0x07, 0xe0,
- 0x3c, 0xd8, 0x74, 0xf3, 0xe0, 0x79, 0x45, 0xef, 0xe0, 0x93, 0xf3, 0x6a,
- 0x6f, 0x6e, 0x61, 0x77, 0x61, 0x74, 0xe5, 0xe0, 0x93, 0xc7, 0x69, 0xe2,
- 0xe0, 0x4c, 0xeb, 0x66, 0xf4, 0x02, 0x85, 0x65, 0xe4, 0xe0, 0x67, 0x92,
- 0x63, 0x72, 0x79, 0x70, 0x74, 0x6f, 0xae, 0x60, 0x70, 0xe3, 0xe0, 0x23,
- 0x97, 0x63, 0x68, 0xe9, 0x04, 0xe0, 0x54, 0x4e, 0x6b, 0x61, 0x73, 0x68,
- 0xf5, 0xe0, 0x2b, 0x07, 0xe2, 0x03, 0x0a, 0x8a, 0xf5, 0x03, 0xc0, 0x9f,
- 0x6b, 0x61, 0xf7, 0xe0, 0x8d, 0x19, 0xe5, 0x04, 0xe0, 0x90, 0xd9, 0x63,
- 0xe8, 0xe0, 0x93, 0x78, 0x61, 0x74, 0x61, 0xae, 0x60, 0x5b, 0xf7, 0xe0,
- 0x35, 0x48, 0xe5, 0x04, 0xe0, 0x84, 0xe2, 0x72, 0x62, 0x72, 0x6f, 0x6f,
- 0xeb, 0xe0, 0x91, 0x01, 0xe1, 0x07, 0x05, 0x07, 0x06, 0xe0, 0x92, 0xb1,
- 0xf2, 0x60, 0x93, 0x82, 0x9e, 0x6e, 0x67, 0x72, 0xe9, 0xe0, 0x70, 0xba,
- 0x6b, 0x6f, 0xf4, 0xe0, 0x36, 0x8b, 0x63, 0x6b, 0x6e, 0x65, 0xf4, 0xe0,
- 0x65, 0x92, 0xe7, 0x04, 0xe0, 0x94, 0x7e, 0xad, 0xe0, 0x5f, 0x0e, 0xe6,
- 0x60, 0x93, 0x85, 0xbd, 0xe5, 0x1a, 0x0d, 0x06, 0x1d, 0x40, 0xb7, 0x06,
- 0x25, 0x0f, 0x40, 0x4a, 0x1b, 0x04, 0x1a, 0x04, 0x25, 0x0b, 0x08, 0x60,
- 0x73, 0x8c, 0x5d, 0x14, 0x07, 0xc1, 0xd3, 0xf8, 0x06, 0x60, 0x92, 0xb2,
- 0xc1, 0xa1, 0xae, 0x60, 0x90, 0x97, 0xc3, 0x52, 0xf6, 0x41, 0xc0, 0xe0,
- 0x53, 0xef, 0xf4, 0x03, 0x0c, 0x86, 0xf4, 0x04, 0xe0, 0x43, 0x01, 0x6c,
- 0xe5, 0x60, 0x60, 0x8b, 0xc2, 0x2e, 0xef, 0x60, 0x89, 0x9c, 0xc6, 0x15,
- 0x61, 0x67, 0x61, 0xf9, 0xe0, 0x8f, 0x19, 0xf2, 0x03, 0xc0, 0xa8, 0xf6,
- 0x02, 0x99, 0x69, 0x63, 0xe5, 0x04, 0xe0, 0x63, 0x23, 0xae, 0x04, 0xe0,
- 0x76, 0x1c, 0x67, 0x6f, 0x76, 0xae, 0x04, 0xe0, 0x91, 0x59, 0x73, 0xe3,
- 0xe0, 0x67, 0x15, 0xe5, 0x0c, 0x09, 0x05, 0x05, 0x09, 0x12, 0x05, 0x18,
- 0x06, 0x06, 0x0a, 0x8f, 0x73, 0x61, 0x72, 0x63, 0x61, 0xf3, 0xe0, 0x8b,
- 0x4b, 0x72, 0xf3, 0xe0, 0x6c, 0x7b, 0x71, 0x75, 0xe1, 0xc0, 0x5b, 0xf0,
- 0x04, 0xe0, 0x69, 0x23, 0xb2, 0xe0, 0x93, 0x88, 0xed, 0x02, 0x85, 0x70,
- 0xb3, 0xe0, 0x93, 0xd4, 0x69, 0x6e, 0x65, 0x63, 0x72, 0x61, 0xe6, 0xe0,
- 0x88, 0x12, 0x69, 0xf2, 0xe0, 0x6d, 0x0e, 0xe8, 0x03, 0x06, 0x85, 0x75,
- 0x6d, 0xef, 0xe0, 0x69, 0x73, 0x74, 0xf4, 0xe0, 0x93, 0x62, 0x61, 0x6c,
- 0x66, 0x6c, 0x69, 0xe6, 0xe0, 0x8c, 0xd7, 0x67, 0x61, 0xed, 0xe0, 0x4d,
- 0xcd, 0x66, 0x74, 0xf0, 0xe0, 0x80, 0xd0, 0x65, 0x78, 0x63, 0x68, 0x61,
- 0x6e, 0xe7, 0xe0, 0x8c, 0xc1, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72,
- 0x73, 0x74, 0x72, 0x69, 0xeb, 0xe0, 0x8c, 0xb2, 0xe2, 0x05, 0x05, 0xe0,
- 0x44, 0xb9, 0x6c, 0xef, 0xe0, 0x76, 0x0f, 0xe2, 0xe0, 0x80, 0xa7, 0xe1,
- 0x04, 0xe0, 0x88, 0xd6, 0x6e, 0x69, 0xf3, 0xe0, 0x51, 0x4d, 0x6f, 0x75,
- 0xec, 0xe0, 0x7c, 0xf1, 0xee, 0x07, 0x0d, 0x04, 0x06, 0xe0, 0x80, 0x93,
- 0xf3, 0x02, 0x84, 0xe9, 0xe0, 0x2e, 0xe9, 0x65, 0x65, 0xf2, 0xe0, 0x75,
- 0xe3, 0xee, 0xe0, 0x51, 0xb7, 0x64, 0x61, 0xe9, 0xe0, 0x39, 0x69, 0x61,
- 0x73, 0xe1, 0xe0, 0x82, 0x1c, 0xed, 0x02, 0x86, 0x69, 0x6e, 0xe5, 0xe0,
- 0x5a, 0x7f, 0x62, 0x6f, 0xeb, 0xe0, 0x51, 0x86, 0xec, 0x0a, 0x2b, 0x06,
- 0x5d, 0xeb, 0x60, 0x6d, 0xd4, 0xc6, 0x4a, 0xec, 0x02, 0x9e, 0xf3, 0x02,
- 0x8a, 0x79, 0x6f, 0x75, 0x72, 0x68, 0x6f, 0xed, 0xe0, 0x7e, 0xc3, 0xad,
- 0x04, 0xe0, 0x7d, 0x7c, 0x66, 0x6f, 0x72, 0xad, 0x04, 0xe0, 0x92, 0x78,
- 0xec, 0xe0, 0x44, 0x5d, 0x66, 0x79, 0x2e, 0x73, 0x74, 0x6f, 0xf2, 0xe0,
- 0x92, 0xc9, 0xea, 0x60, 0x8f, 0xa5, 0xc2, 0x68, 0x66, 0x69, 0x70, 0xae,
- 0x60, 0x74, 0xa1, 0x4f, 0x3e, 0x4c, 0xd8, 0x42, 0x1f, 0x9c, 0xeb, 0x02,
- 0x8c, 0xe9, 0x07, 0x60, 0x3d, 0xf1, 0xe0, 0x4d, 0x2d, 0xeb, 0xe0, 0x42,
- 0x42, 0x64, 0x31, 0x2e, 0x62, 0x65, 0x65, 0x62, 0x79, 0xf4, 0xe0, 0x84,
- 0xed, 0xea, 0xe0, 0x3a, 0xc5, 0xe9, 0x08, 0x08, 0x04, 0x60, 0x3d, 0x81,
- 0xc4, 0x0f, 0x72, 0xef, 0x60, 0x3f, 0xa6, 0xe0, 0x4f, 0xa4, 0xe8, 0xe0,
- 0x50, 0xdc, 0x64, 0xe1, 0xe0, 0x86, 0xf9, 0xe5, 0xe0, 0x90, 0xe5, 0xe3,
- 0x05, 0x14, 0x05, 0xce, 0x56, 0x75, 0xf2, 0x04, 0xe0, 0x91, 0xcb, 0x69,
- 0x74, 0xf9, 0x04, 0xe0, 0x92, 0x9f, 0x74, 0x61, 0x63, 0xf4, 0xe0, 0x67,
- 0xd9, 0x72, 0xe5, 0xe0, 0x7c, 0x05, 0x61, 0x61, 0xf3, 0xe0, 0x47, 0xb5,
- 0x62, 0x61, 0x73, 0x74, 0x6f, 0x70, 0x6f, 0xec, 0xe0, 0x77, 0xa2, 0xe1,
- 0x5d, 0x8f, 0x60, 0x5c, 0xc7, 0xd7, 0x07, 0xae, 0x60, 0x8d, 0x0d, 0x03,
- 0x03, 0xc5, 0x43, 0xe4, 0x08, 0x09, 0x60, 0x4e, 0xe4, 0xe0, 0x43, 0x7c,
- 0x73, 0x63, 0x6c, 0x6f, 0x75, 0xe4, 0xe0, 0x81, 0xba, 0xee, 0xe0, 0x53,
- 0xab, 0xe3, 0x0d, 0x1d, 0x08, 0x40, 0x4a, 0x40, 0xa6, 0x0e, 0x60, 0x87,
- 0xd7, 0xc9, 0x54, 0xf2, 0x02, 0x86, 0x79, 0x73, 0xe5, 0xe0, 0x6b, 0x89,
- 0x61, 0x70, 0xf0, 0x02, 0x86, 0x69, 0x6e, 0xe7, 0xe0, 0x6e, 0x75, 0x65,
- 0x72, 0x2d, 0x73, 0x69, 0xf4, 0xe0, 0x84, 0xf1, 0x6f, 0xf4, 0x60, 0x63,
- 0x5c, 0xe0, 0x2e, 0xd3, 0xe9, 0x04, 0xe0, 0x61, 0xc1, 0x65, 0xee, 0x02,
- 0x86, 0x74, 0x69, 0xf3, 0xe0, 0x8d, 0xe3, 0x63, 0xe5, 0x0b, 0x0d, 0x0b,
- 0x10, 0x60, 0x70, 0x15, 0x5f, 0xa6, 0xc2, 0x2a, 0xf3, 0x04, 0xe0, 0x8f,
- 0xde, 0x6e, 0x61, 0x74, 0x75, 0xf2, 0xe0, 0x7c, 0x21, 0x63, 0x65, 0x6e,
- 0x74, 0x65, 0xf2, 0x60, 0x8b, 0x66, 0xc4, 0x6a, 0x61, 0x6e, 0xe4, 0x04,
- 0xe0, 0x70, 0x1e, 0x69, 0x6e, 0x64, 0x75, 0x73, 0xf4, 0xe0, 0x87, 0xcd,
- 0x2d, 0x66, 0xe9, 0xe0, 0x71, 0xc6, 0xe8, 0x08, 0x0d, 0x08, 0x40, 0x44,
- 0x06, 0x06, 0x88, 0xf7, 0x02, 0x85, 0x65, 0xe9, 0xe0, 0x83, 0xc3, 0x61,
- 0xf2, 0xe0, 0x8f, 0xef, 0x75, 0xec, 0x60, 0x67, 0x59, 0xe0, 0x29, 0x93,
- 0xef, 0x04, 0x21, 0x07, 0x8e, 0x6f, 0xec, 0x06, 0x08, 0x04, 0xe0, 0x91,
- 0xa8, 0x73, 0x2e, 0x6e, 0x73, 0xf7, 0xe0, 0x78, 0x9c, 0xe2, 0xe0, 0x48,
- 0xbc, 0xae, 0x06, 0x60, 0x8d, 0xfe, 0xc1, 0x7a, 0xee, 0x60, 0x8f, 0xc3,
- 0xc0, 0xd7, 0x6c, 0x61, 0x72, 0x73, 0xe8, 0xce, 0x95, 0x6b, 0xef, 0x02,
- 0x85, 0x6c, 0xe1, 0xe0, 0x7c, 0xbf, 0x6b, 0xe5, 0xe0, 0x5f, 0xc0, 0x65,
- 0x6e, 0x62, 0x72, 0x75, 0xee, 0xe0, 0x87, 0x8a, 0x6d, 0x69, 0xe4, 0xe0,
- 0x90, 0x56, 0x6c, 0x65, 0xf3, 0xe0, 0x3d, 0xa0, 0x61, 0x65, 0x66, 0x66,
- 0xec, 0xe0, 0x7e, 0xb1, 0xae, 0x10, 0x04, 0x04, 0x08, 0x05, 0x06, 0x60,
- 0x70, 0x33, 0x40, 0x48, 0x4c, 0x1f, 0x42, 0x53, 0x83, 0xf5, 0xe0, 0x5d,
- 0xb1, 0xf4, 0xe0, 0x7f, 0x07, 0xf3, 0x60, 0x8f, 0x7c, 0x40, 0xcd, 0xc0,
- 0x97, 0xec, 0x60, 0x8f, 0x77, 0xaf, 0xe9, 0x60, 0x8f, 0x8d, 0xc0, 0xfe,
- 0xe1, 0xe0, 0x90, 0x61, 0xe1, 0x04, 0xe0, 0x91, 0x34, 0x6c, 0x65, 0x62,
- 0x6f, 0x6f, 0xeb, 0xe0, 0x5c, 0xe1, 0xae, 0x0d, 0x5d, 0xb5, 0x60, 0x2c,
- 0x16, 0x60, 0x41, 0xe9, 0x41, 0x8a, 0xc2, 0x19, 0xec, 0x60, 0x8f, 0x49,
- 0x86, 0xe2, 0x60, 0x76, 0x34, 0x59, 0x10, 0x40, 0x53, 0xc1, 0x80, 0xe1,
- 0x1a, 0x0e, 0x04, 0x20, 0x07, 0x1d, 0x27, 0x26, 0x0c, 0x1a, 0x40, 0xc8,
- 0x23, 0x33, 0x40, 0x5f, 0x2d, 0x12, 0x0d, 0x0a, 0x06, 0x08, 0x18, 0xe0,
- 0x8e, 0x31, 0xf9, 0x04, 0xe0, 0x8b, 0xb3, 0x61, 0x6d, 0x61, 0xae, 0x60,
- 0x87, 0x86, 0xc8, 0x2a, 0xf8, 0xe0, 0x8f, 0xfa, 0xf6, 0x05, 0x12, 0xe0,
- 0x89, 0x9d, 0xe5, 0x04, 0xe0, 0x90, 0xd6, 0x73, 0x2d, 0x74, 0x68, 0x65,
- 0x2d, 0x77, 0x68, 0x61, 0xec, 0xe0, 0x90, 0x48, 0x61, 0x6e, 0x6e, 0x61,
- 0xe8, 0xe0, 0x76, 0x03, 0xf5, 0x60, 0x34, 0x1d, 0xe0, 0x36, 0xf5, 0xf4,
- 0x06, 0x04, 0x0c, 0xe0, 0x61, 0x04, 0xf4, 0xe0, 0x39, 0xc0, 0x73, 0x75,
- 0x6d, 0x61, 0x73, 0x65, 0x6e, 0x64, 0xe1, 0xe0, 0x8a, 0x31, 0x6f, 0x73,
- 0xe8, 0xe0, 0x3c, 0x00, 0xf3, 0x07, 0x04, 0x0a, 0x05, 0xe0, 0x90, 0x82,
- 0xf3, 0xe0, 0x78, 0xf0, 0x6b, 0x61, 0x74, 0x63, 0x68, 0x65, 0xf7, 0xe0,
- 0x62, 0xf4, 0x65, 0xe2, 0xe0, 0x53, 0x70, 0xe1, 0x02, 0x85, 0x79, 0xe1,
- 0xe0, 0x4e, 0xb2, 0x67, 0xf5, 0xd5, 0xb3, 0xf2, 0x09, 0x06, 0x07, 0x04,
- 0x60, 0x8e, 0xa3, 0xc0, 0x4e, 0x75, 0x66, 0xf5, 0xe0, 0x8c, 0x80, 0x70,
- 0x73, 0x62, 0xef, 0xe0, 0x62, 0xbd, 0xef, 0xe0, 0x84, 0x78, 0xe4, 0x02,
- 0x84, 0xe9, 0xe0, 0x78, 0x6b, 0xe5, 0xe0, 0x7b, 0xd6, 0xf0, 0x04, 0xe0,
- 0x90, 0x4b, 0x70, 0x6f, 0x72, 0xef, 0xe0, 0x36, 0x5f, 0xef, 0x03, 0x05,
- 0x87, 0x74, 0x6f, 0xed, 0xca, 0x30, 0x67, 0x6f, 0x6e, 0xe3, 0xe0, 0x87,
- 0xc8, 0x62, 0x65, 0x72, 0x6e, 0x61, 0x72, 0xe4, 0xe0, 0x84, 0x4e, 0xee,
- 0x0a, 0x05, 0x29, 0x08, 0x0a, 0x04, 0x04, 0x0a, 0xc0, 0x63, 0x75, 0xeb,
- 0xe0, 0x39, 0xfd, 0xf4, 0x02, 0x88, 0x6f, 0x61, 0x6e, 0x64, 0xf2, 0xe0,
- 0x78, 0x13, 0xe1, 0x04, 0x06, 0x04, 0x86, 0x6d, 0x61, 0xf2, 0xe0, 0x53,
- 0x8d, 0xe6, 0xe0, 0x8c, 0x5c, 0x63, 0x72, 0xf5, 0xe0, 0x81, 0xef, 0x62,
- 0x61, 0x72, 0x62, 0x61, 0xf2, 0xe0, 0x8a, 0x7b, 0xef, 0x60, 0x7e, 0x68,
- 0x46, 0xb5, 0xc5, 0x4c, 0xee, 0x04, 0xe0, 0x53, 0x31, 0x61, 0xee, 0xe0,
- 0x8b, 0xd7, 0xea, 0xe0, 0x8c, 0x62, 0xe7, 0xe0, 0x4b, 0x87, 0x66, 0x72,
- 0x61, 0x6e, 0x63, 0x69, 0xf3, 0xe0, 0x85, 0xc8, 0xe4, 0x0d, 0x0e, 0x0b,
- 0x05, 0x31, 0x60, 0x4d, 0xa6, 0x60, 0x36, 0xcc, 0xc3, 0x71, 0x76, 0x69,
- 0xeb, 0x04, 0xe0, 0x8f, 0xb8, 0x63, 0x6f, 0x72, 0x6f, 0xed, 0xc5, 0x18,
- 0x6e, 0x65, 0xf3, 0x04, 0xe0, 0x8e, 0xb6, 0xf3, 0xe0, 0x22, 0xa3, 0x69,
- 0xe5, 0xe0, 0x74, 0x8d, 0xe5, 0x04, 0xe0, 0x81, 0x1c, 0xae, 0x03, 0x19,
- 0x8a, 0x78, 0x6e, 0x2d, 0x2d, 0x6d, 0x72, 0x65, 0x2d, 0x6f, 0x67, 0x2d,
- 0x72, 0x6f, 0x6d, 0x73, 0x64, 0x61, 0x6c, 0x2d, 0x71, 0x71, 0xe2, 0xe0,
- 0x8e, 0x8e, 0x76, 0x65, 0x73, 0x74, 0x66, 0x6f, 0xec, 0xe0, 0x8c, 0x1d,
- 0xed, 0x60, 0x4c, 0x2a, 0x8e, 0x63, 0x61, 0xf4, 0xe0, 0x72, 0x08, 0x61,
- 0x67, 0x6f, 0x63, 0xe8, 0xe0, 0x21, 0xa3, 0xed, 0x07, 0x04, 0x0d, 0x04,
- 0xe0, 0x5b, 0x4c, 0xf5, 0xe0, 0x8b, 0x60, 0xf3, 0x02, 0x84, 0xf5, 0xe0,
- 0x7a, 0xaf, 0x63, 0x6c, 0xf5, 0xe0, 0x85, 0xf8, 0xee, 0xe0, 0x79, 0xea,
- 0x65, 0x67, 0xe1, 0xe0, 0x84, 0xb1, 0xec, 0x0b, 0x04, 0x0f, 0x05, 0x09,
- 0x60, 0x49, 0x74, 0xe0, 0x3f, 0x25, 0xfa, 0xe0, 0x73, 0x07, 0x76, 0x61,
- 0x64, 0x6f, 0xf2, 0x04, 0xe0, 0x8e, 0x6d, 0x64, 0x61, 0xec, 0xe0, 0x83,
- 0xf5, 0x75, 0xe4, 0xe0, 0x8c, 0xfe, 0xe5, 0x60, 0x2d, 0x55, 0x60, 0x58,
- 0x8e, 0xc9, 0x37, 0xe1, 0x60, 0x80, 0xb9, 0xc7, 0x7e, 0xeb, 0x05, 0x20,
- 0x04, 0xde, 0x2a, 0xf5, 0x05, 0x16, 0xe0, 0x8c, 0x63, 0x72, 0xe1, 0x08,
- 0x06, 0x60, 0x4c, 0x30, 0xe0, 0x42, 0xc0, 0x67, 0x61, 0xf7, 0xe0, 0x49,
- 0x95, 0xae, 0x60, 0x84, 0x1d, 0xc9, 0x8f, 0xe8, 0xe0, 0x81, 0xac, 0xe5,
- 0xe0, 0x29, 0xf5, 0xe1, 0x0e, 0x15, 0x06, 0x07, 0x57, 0xfe, 0x60, 0x3e,
- 0x43, 0x60, 0x2d, 0xcd, 0xc2, 0xaa, 0xe9, 0x02, 0x89, 0x6d, 0x69, 0x6e,
- 0x61, 0x74, 0xef, 0xe0, 0x72, 0xde, 0xae, 0x60, 0x62, 0xf7, 0x60, 0x25,
- 0x01, 0xc5, 0x94, 0x68, 0x6f, 0xe7, 0xe0, 0x86, 0xf4, 0x65, 0xae, 0x60,
- 0x8c, 0x30, 0xc1, 0x43, 0xe4, 0xe0, 0x56, 0xe7, 0xe9, 0x05, 0x0e, 0x08,
- 0x09, 0x84, 0xf4, 0x04, 0xe0, 0x62, 0xeb, 0x61, 0x6d, 0x61, 0xae, 0x60,
- 0x85, 0x3a, 0xc8, 0x55, 0x6e, 0x74, 0x6c, 0x6f, 0xf5, 0xe0, 0x81, 0x92,
- 0xeb, 0x04, 0xe0, 0x4a, 0x15, 0xe1, 0xe0, 0x4c, 0xa9, 0xea, 0xe0, 0x3d,
- 0x5d, 0xe7, 0xe0, 0x49, 0x92, 0x67, 0xe1, 0x05, 0x05, 0xe0, 0x4a, 0x69,
- 0x6d, 0xe9, 0xe0, 0x3e, 0xd0, 0xae, 0x60, 0x84, 0xc5, 0xc8, 0x9f, 0x66,
- 0xe5, 0x04, 0xe0, 0x8e, 0x68, 0x74, 0xf9, 0x60, 0x8c, 0x16, 0xc2, 0x50,
- 0xe4, 0x04, 0xe0, 0x8a, 0xe0, 0x69, 0xf3, 0xe0, 0x77, 0xc9, 0x62, 0x61,
- 0xe5, 0xe0, 0x62, 0x84, 0x61, 0x72, 0x6c, 0x61, 0xee, 0xe0, 0x8c, 0x97,
- 0xae, 0x08, 0x09, 0x60, 0x8b, 0xa7, 0x04, 0xc1, 0x23, 0x67, 0x6f, 0x76,
- 0xae, 0x60, 0x8a, 0x81, 0xc1, 0x2f, 0xe3, 0x60, 0x8d, 0x7f, 0xc0, 0xb1,
- 0x2d, 0x65, 0x61, 0x73, 0xf4, 0xe0, 0x85, 0xdf, 0x35, 0xf9, 0xe0, 0x80,
- 0xd9, 0xb3, 0x03, 0xc0, 0x44, 0xae, 0x0d, 0x09, 0x19, 0x10, 0x21, 0x05,
- 0x60, 0x39, 0x42, 0x09, 0xe0, 0x20, 0x1c, 0x74, 0x65, 0x63, 0x6b, 0x69,
- 0xe4, 0xe0, 0x7a, 0x0b, 0x64, 0x75, 0x61, 0x6c, 0x73, 0x74, 0x61, 0x63,
- 0x6b, 0xae, 0x07, 0x05, 0x40, 0x47, 0x21, 0xc0, 0x5a, 0x75, 0x73, 0xad,
- 0xc0, 0xd7, 0xe5, 0xc0, 0xab, 0xe3, 0x03, 0xc0, 0x41, 0x6e, 0x2d, 0x6e,
- 0x6f, 0x72, 0x74, 0x68, 0x2d, 0xb1, 0xe0, 0x6f, 0x14, 0xe1, 0x39, 0xc0,
- 0x94, 0xad, 0x07, 0x28, 0x21, 0x19, 0x11, 0x11, 0x9f, 0x77, 0x65, 0x62,
- 0x73, 0x69, 0x74, 0xe5, 0x02, 0xb6, 0xae, 0x0b, 0x05, 0x0e, 0x05, 0x60,
- 0x39, 0x2f, 0x09, 0xe0, 0x20, 0x1c, 0x75, 0x73, 0xad, 0xc0, 0x54, 0x65,
- 0x75, 0xad, 0x03, 0xc0, 0x7b, 0x77, 0x65, 0x73, 0x74, 0xad, 0x40, 0x74,
- 0xa4, 0x63, 0x61, 0xad, 0xc0, 0x70, 0x61, 0x70, 0xad, 0x02, 0x87, 0x73,
- 0x6f, 0x75, 0x74, 0xe8, 0xc0, 0x78, 0x6e, 0x6f, 0x72, 0x74, 0xe8, 0xae,
- 0xad, 0x04, 0x05, 0x04, 0x83, 0x75, 0x73, 0xad, 0x18, 0x89, 0x73, 0x61,
- 0xad, 0x93, 0x65, 0xf5, 0xaf, 0x61, 0x70, 0xad, 0x02, 0x85, 0x73, 0x6f,
- 0xf5, 0xc0, 0x5c, 0x6e, 0x6f, 0x72, 0x74, 0x68, 0x65, 0xe1, 0xa0, 0x75,
- 0x73, 0xad, 0x03, 0x04, 0x8f, 0x77, 0xe5, 0xc0, 0x4e, 0x65, 0x61, 0x73,
- 0x74, 0xad, 0xc0, 0x4c, 0x66, 0x69, 0x70, 0x73, 0x2d, 0x75, 0x73, 0x2d,
- 0x67, 0x6f, 0x76, 0x2d, 0x77, 0x65, 0x73, 0xf4, 0xac, 0xe5, 0x02, 0x86,
- 0x78, 0x74, 0x65, 0x72, 0xee, 0x94, 0x75, 0xad, 0x02, 0x8a, 0x77, 0x65,
- 0x73, 0x74, 0xad, 0x03, 0x24, 0x82, 0xb3, 0xa6, 0x63, 0x65, 0x6e, 0x74,
- 0x72, 0x61, 0xec, 0x8d, 0x61, 0x70, 0xad, 0x02, 0x89, 0x73, 0x6f, 0x75,
- 0x74, 0xe8, 0x02, 0x87, 0xad, 0x8f, 0x6e, 0x6f, 0x72, 0x74, 0x68, 0x65,
- 0x61, 0x73, 0x74, 0xad, 0x02, 0x82, 0xb2, 0x82, 0x31, 0x2e, 0x61, 0x6d,
- 0x61, 0x7a, 0x6f, 0x6e, 0x61, 0xf7, 0xe0, 0x8c, 0x88, 0xf2, 0x1e, 0x35,
- 0x0d, 0x2e, 0x06, 0x20, 0x24, 0x04, 0x40, 0x96, 0x04, 0x04, 0x04, 0x40,
- 0xb1, 0x04, 0x41, 0x99, 0x05, 0x40, 0xe0, 0x60, 0x4f, 0x38, 0x47, 0x1d,
- 0x57, 0x15, 0xd4, 0xb7, 0x1f, 0xc3, 0x04, 0x17, 0x03, 0x88, 0xf8, 0x08,
- 0x06, 0x04, 0x40, 0xf0, 0xe0, 0x4a, 0x8b, 0xf9, 0x40, 0xcb, 0xe0, 0x30,
- 0x56, 0xed, 0xe0, 0x2a, 0x04, 0xe4, 0xe0, 0x85, 0x42, 0xe6, 0xc3, 0xf4,
- 0xe5, 0x04, 0xe0, 0x53, 0x8f, 0xe8, 0xc4, 0x0e, 0xe1, 0x03, 0xd4, 0xbf,
- 0x68, 0x6b, 0x6b, 0x65, 0x72, 0x1f, 0xc3, 0xc4, 0x0a, 0xfa, 0x02, 0x84,
- 0xe7, 0xe0, 0x4d, 0xfb, 0x65, 0x73, 0xfa, 0xe0, 0x80, 0x8e, 0xf9, 0x07,
- 0x15, 0x06, 0x05, 0xe0, 0x3a, 0x61, 0xf5, 0x03, 0x05, 0x85, 0x6f, 0xe8,
- 0xe0, 0x88, 0x0b, 0x6b, 0xf9, 0xe0, 0x8c, 0x2c, 0x67, 0x61, 0x73, 0xe1,
- 0xe0, 0x41, 0xda, 0x6f, 0x6b, 0xe1, 0xe0, 0x37, 0x26, 0x67, 0xe7, 0xe0,
- 0x8b, 0x8d, 0x62, 0x6e, 0xe9, 0xe0, 0x7a, 0xf4, 0xf7, 0x60, 0x8b, 0x9d,
- 0xc0, 0xdb, 0xf5, 0x0c, 0x05, 0x05, 0x06, 0x60, 0x44, 0x87, 0x50, 0xce,
- 0xe0, 0x37, 0x01, 0x73, 0xf3, 0xe0, 0x80, 0x10, 0x6f, 0xf6, 0xe0, 0x7a,
- 0xab, 0xee, 0x60, 0x8b, 0x86, 0xc0, 0xd5, 0xe7, 0xd2, 0x0c, 0xf3, 0x07,
- 0x04, 0x09, 0x05, 0xe0, 0x8c, 0x39, 0xf6, 0xe0, 0x8b, 0x4e, 0x73, 0x2e,
- 0x6d, 0x79, 0x2e, 0xe9, 0xe0, 0x7d, 0x85, 0x63, 0xae, 0xe0, 0x74, 0xea,
- 0xae, 0x05, 0x60, 0x86, 0xc7, 0x83, 0xe2, 0xe0, 0x7c, 0xfc, 0xf2, 0xe0,
- 0x64, 0x78, 0xef, 0x16, 0x10, 0x0d, 0x11, 0x06, 0x03, 0x14, 0x05, 0x05,
- 0x04, 0x06, 0x13, 0x59, 0xb3, 0x60, 0x30, 0x9a, 0x60, 0x3b, 0x44, 0xc6,
- 0x11, 0xf9, 0x05, 0x04, 0xe0, 0x30, 0x52, 0xeb, 0xe0, 0x7d, 0xb7, 0x61,
- 0x6c, 0xad, 0xe0, 0x41, 0x0b, 0xf6, 0x02, 0x85, 0x6e, 0xef, 0xe0, 0x71,
- 0x1c, 0x69, 0xe7, 0xe0, 0x8a, 0x8f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x6d,
- 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0xee, 0xe0, 0x8b, 0xc8, 0x74,
- 0x6f, 0xf2, 0xe0, 0x87, 0xa5, 0xf2, 0xd9, 0xf4, 0xed, 0x05, 0x07, 0xe0,
- 0x85, 0xad, 0xf3, 0x60, 0x5e, 0x2a, 0xe0, 0x28, 0x7b, 0x61, 0xae, 0x60,
- 0x89, 0xa5, 0xc0, 0xc0, 0x6c, 0xec, 0xe0, 0x7a, 0xc5, 0x6b, 0xf5, 0xe0,
- 0x4b, 0x33, 0xe7, 0xe0, 0x57, 0xb3, 0xe4, 0x60, 0x80, 0xbc, 0xc5, 0x4b,
- 0xe3, 0x02, 0x88, 0xeb, 0x44, 0xdd, 0x60, 0x7b, 0xc7, 0xc9, 0x3a, 0x68,
- 0xe5, 0x60, 0x26, 0xa2, 0xe0, 0x64, 0x4f, 0xae, 0x06, 0x60, 0x86, 0x2f,
- 0x03, 0x83, 0xe9, 0x60, 0x8a, 0x76, 0xc1, 0x1f, 0xee, 0xe0, 0x7d, 0xd3,
- 0xed, 0xe0, 0x8a, 0x26, 0xea, 0xe0, 0x63, 0xd6, 0xe9, 0x14, 0x06, 0x05,
- 0x1f, 0x21, 0x16, 0x05, 0x10, 0x05, 0x04, 0x15, 0x60, 0x67, 0x0f, 0x48,
- 0xa6, 0x59, 0x73, 0xc0, 0xbc, 0x76, 0x6e, 0xe5, 0xe0, 0x70, 0x8e, 0x74,
- 0xf4, 0xe0, 0x86, 0xdd, 0xf3, 0x06, 0x06, 0x04, 0xe0, 0x51, 0xd6, 0x1f,
- 0x43, 0xf8, 0xe0, 0x80, 0x69, 0xef, 0xe0, 0x80, 0x65, 0x68, 0x69, 0x72,
- 0xe9, 0x04, 0xe0, 0x8a, 0x37, 0x66, 0x75, 0xea, 0xe0, 0x8a, 0x33, 0xef,
- 0x08, 0x07, 0x0b, 0x60, 0x8a, 0x76, 0xc0, 0xb9, 0x70, 0x72, 0x65, 0xf4,
- 0xe0, 0x7f, 0x5e, 0x64, 0x65, 0x6a, 0x61, 0x6e, 0x65, 0x69, 0xf2, 0xe0,
- 0x81, 0x2b, 0x62, 0x72, 0x61, 0xee, 0xcc, 0x1d, 0xee, 0x04, 0xe0, 0x84,
- 0xed, 0xe7, 0x02, 0x84, 0xf3, 0xe0, 0x38, 0x7a, 0xe5, 0x04, 0xe0, 0x83,
- 0xda, 0x72, 0xe9, 0xe0, 0x59, 0x2e, 0x6d, 0xe9, 0xe0, 0x83, 0xc2, 0x6b,
- 0xf5, 0x04, 0xe0, 0x87, 0x1d, 0x7a, 0x65, 0x6e, 0x74, 0x61, 0x6b, 0x61,
- 0xf4, 0xdf, 0xaa, 0x69, 0xeb, 0xe0, 0x86, 0xeb, 0xe6, 0xe0, 0x21, 0x28,
- 0xe3, 0x05, 0x04, 0xe0, 0x7f, 0x1a, 0xef, 0xe0, 0x89, 0xe3, 0xe8, 0x04,
- 0xe0, 0x8a, 0xe6, 0x61, 0x72, 0xe4, 0xe0, 0x3a, 0xef, 0x62, 0x65, 0x69,
- 0x72, 0xe1, 0xe0, 0x7e, 0xfc, 0xe8, 0xe0, 0x86, 0x3d, 0xe5, 0x15, 0x06,
- 0x10, 0x40, 0x48, 0x36, 0x19, 0x08, 0x0d, 0x07, 0x0c, 0x05, 0x1c, 0x1c,
- 0x23, 0x05, 0x40, 0x43, 0xe0, 0x89, 0x41, 0x78, 0x72, 0xef, 0xe0, 0x89,
- 0xaf, 0x76, 0xe9, 0x02, 0x85, 0x73, 0xf4, 0xe0, 0x88, 0x91, 0x65, 0xf7,
- 0x60, 0x88, 0xda, 0xc1, 0xd3, 0xf3, 0x04, 0x0c, 0x11, 0xa0, 0xf4, 0x04,
- 0xe0, 0x8a, 0x9e, 0x61, 0x75, 0x72, 0xe1, 0xe0, 0x62, 0x4d, 0xe9, 0x02,
- 0x86, 0x73, 0x74, 0xe1, 0xe0, 0x6a, 0xb3, 0x6e, 0x64, 0x65, 0x76, 0xe9,
- 0xe0, 0x6c, 0xf2, 0xe5, 0x02, 0x96, 0x72, 0xf6, 0x02, 0x89, 0x65, 0x2d,
- 0x6f, 0x6e, 0x6c, 0x69, 0xee, 0xc2, 0x1e, 0x64, 0xae, 0x60, 0x66, 0x39,
- 0x19, 0xe0, 0x24, 0x1b, 0x61, 0x72, 0x63, 0xe8, 0xe0, 0x83, 0xe1, 0xae,
- 0x60, 0x72, 0xa3, 0xd5, 0x73, 0xf0, 0x06, 0x08, 0x05, 0x07, 0x06, 0x8c,
- 0x75, 0x62, 0x6c, 0x69, 0xe3, 0xe0, 0x86, 0xe4, 0x6f, 0xf2, 0xe0, 0x89,
- 0x2b, 0x6c, 0xae, 0x60, 0x62, 0xd4, 0xce, 0xce, 0x62, 0x6f, 0xe4, 0xe0,
- 0x5c, 0x0f, 0x61, 0x69, 0xf2, 0x04, 0xe0, 0x8a, 0x38, 0x2e, 0xed, 0xe0,
- 0x55, 0x7e, 0xae, 0x04, 0xe0, 0x89, 0x76, 0xeb, 0xe0, 0x89, 0x31, 0xee,
- 0x07, 0x09, 0x5e, 0x9d, 0xe0, 0x6b, 0x7c, 0xf4, 0x04, 0xe0, 0x8a, 0x1d,
- 0xe1, 0xe0, 0x77, 0xee, 0x6e, 0xe5, 0x60, 0x2b, 0xcd, 0xe0, 0x57, 0x0c,
- 0x6d, 0x6f, 0x74, 0x65, 0xf7, 0xe0, 0x85, 0x76, 0xec, 0x02, 0x84, 0xe9,
- 0xe0, 0x29, 0x12, 0xae, 0x60, 0x86, 0x46, 0xc0, 0xe9, 0x6b, 0x6c, 0x61,
- 0xed, 0xe0, 0x89, 0x8e, 0xe9, 0x04, 0xe0, 0x88, 0xcc, 0x73, 0xe5, 0x60,
- 0x88, 0x50, 0xc1, 0x9e, 0x68, 0xe1, 0xe0, 0x80, 0x93, 0xe7, 0x02, 0x95,
- 0x67, 0x69, 0xef, 0x03, 0x03, 0x86, 0xad, 0x02, 0x86, 0x65, 0x6d, 0xe9,
- 0xe0, 0x53, 0xa2, 0x63, 0x61, 0xec, 0xe0, 0x72, 0x1f, 0xae, 0xe0, 0x77,
- 0xa9, 0xe4, 0x08, 0x07, 0x08, 0x60, 0x74, 0x44, 0xd5, 0x6c, 0x75, 0x6d,
- 0x62, 0xf2, 0xe0, 0x65, 0xdf, 0x69, 0x72, 0x65, 0x63, 0xf4, 0xe0, 0x74,
- 0xcc, 0xae, 0xe0, 0x4d, 0x5f, 0xe3, 0x04, 0x05, 0x0b, 0x84, 0x72, 0xe5,
- 0xe0, 0x87, 0x50, 0xe9, 0x02, 0x84, 0xf0, 0xe0, 0x7e, 0x44, 0xe6, 0xe0,
- 0x71, 0x9b, 0xe8, 0xe0, 0x87, 0x1c, 0xae, 0x60, 0x7f, 0x6b, 0x03, 0x40,
- 0xb5, 0x48, 0x84, 0xb3, 0x62, 0xf5, 0xe0, 0x59, 0xf5, 0xe1, 0x02, 0x97,
- 0xec, 0x05, 0x06, 0xe0, 0x65, 0x30, 0xf4, 0x60, 0x86, 0xe4, 0xc0, 0xf5,
- 0x65, 0x73, 0x74, 0x61, 0x74, 0xe5, 0x60, 0x85, 0x82, 0xc3, 0xed, 0xe4,
- 0x07, 0x07, 0x09, 0x09, 0xe0, 0x89, 0x49, 0x79, 0x6d, 0x61, 0xe4, 0xe0,
- 0x7b, 0x91, 0x74, 0x68, 0x65, 0x64, 0x6f, 0xe3, 0xe0, 0x6b, 0xf0, 0x6d,
- 0x79, 0x62, 0x6c, 0x6f, 0xe7, 0xe0, 0x87, 0x09, 0x2d, 0x62, 0x6f, 0x6f,
- 0xeb, 0xe0, 0x75, 0x43, 0xae, 0x60, 0x72, 0xc2, 0xd5, 0x15, 0x64, 0xf6,
- 0xe0, 0x88, 0x95, 0xe1, 0x12, 0x08, 0x24, 0x05, 0x04, 0x17, 0x04, 0x05,
- 0x16, 0x11, 0x1a, 0x0a, 0x20, 0x58, 0x66, 0xe0, 0x6e, 0x93, 0x77, 0x61,
- 0x2d, 0x6d, 0xe1, 0xe0, 0x72, 0x65, 0xf6, 0x02, 0x8c, 0x70, 0x61, 0x67,
- 0x65, 0x2e, 0x63, 0x6f, 0x2e, 0xe9, 0xe0, 0x7f, 0x2a, 0x65, 0xee, 0x04,
- 0xe0, 0x81, 0xdb, 0x64, 0x62, 0xae, 0x06, 0x60, 0x61, 0x87, 0xd2, 0x2a,
- 0xe3, 0x59, 0xd9, 0xe0, 0x57, 0xb6, 0x75, 0xed, 0xe0, 0x83, 0xc4, 0xf2,
- 0xe0, 0x5d, 0x94, 0xee, 0x06, 0x06, 0x05, 0xe0, 0x83, 0xab, 0x7a, 0x61,
- 0xee, 0xe0, 0x7e, 0x57, 0x6b, 0xef, 0xe0, 0x83, 0xb3, 0x64, 0xe1, 0xe0,
- 0x5b, 0x33, 0xec, 0xe0, 0x24, 0xd2, 0x6b, 0xeb, 0xe0, 0x40, 0xc5, 0xe9,
- 0x05, 0x04, 0xe0, 0x4f, 0x42, 0xee, 0xe0, 0x7a, 0xe3, 0xec, 0x02, 0x84,
- 0xf7, 0xe0, 0x6d, 0xb9, 0x72, 0xef, 0xe0, 0x7a, 0xa4, 0xe8, 0x02, 0x85,
- 0x6f, 0xec, 0xe0, 0x81, 0xd9, 0x6b, 0x6b, 0x65, 0x72, 0x61, 0xf6, 0xe0,
- 0x83, 0x2a, 0xe7, 0x02, 0x84, 0xf5, 0xe0, 0x70, 0x07, 0x2d, 0x63, 0x6c,
- 0x6f, 0x75, 0xe4, 0x02, 0x83, 0x2d, 0x63, 0x68, 0x2e, 0x68, 0x6f, 0x73,
- 0xf4, 0xe0, 0x58, 0xd1, 0x66, 0x66, 0x6c, 0x65, 0x65, 0x6e, 0xf4, 0xe0,
- 0x85, 0x39, 0xe4, 0x07, 0x06, 0x60, 0x80, 0xe8, 0xc6, 0x98, 0xef, 0x60,
- 0x7c, 0x9e, 0xc4, 0x94, 0x69, 0xef, 0x04, 0xe0, 0x88, 0x6f, 0xae, 0x07,
- 0x60, 0x60, 0x0f, 0xe0, 0x27, 0xa0, 0xe6, 0xe0, 0x88, 0x61, 0xe3, 0x04,
- 0xe0, 0x73, 0xba, 0x6b, 0x6d, 0x61, 0x7a, 0xe5, 0xe0, 0x85, 0x95, 0xae,
- 0x07, 0x08, 0x60, 0x77, 0xe1, 0xd0, 0x63, 0x63, 0x64, 0x6e, 0x37, 0xb7,
- 0xe0, 0x88, 0x25, 0x61, 0x70, 0x70, 0x73, 0x70, 0xef, 0xe0, 0x69, 0x48,
- 0xf1, 0x0d, 0x2d, 0x04, 0x04, 0x06, 0x0e, 0x04, 0x0c, 0x06, 0x09, 0xe0,
- 0x87, 0xc4, 0xf5, 0x03, 0x13, 0x8d, 0xe9, 0x02, 0x8a, 0x70, 0x65, 0x6c,
- 0x65, 0x6d, 0x65, 0xee, 0xe0, 0x26, 0x89, 0x63, 0x6b, 0x73, 0xf9, 0xc9,
- 0x57, 0xe5, 0x04, 0xe0, 0x7b, 0xca, 0x62, 0x65, 0xe3, 0x60, 0x85, 0xe6,
- 0xc2, 0x2a, 0x61, 0x6c, 0x69, 0x66, 0x69, 0xef, 0xe0, 0x87, 0xa7, 0xf3,
- 0xe0, 0x52, 0xf9, 0xf0, 0xe0, 0x81, 0x84, 0x6f, 0x74, 0xef, 0xe0, 0x87,
- 0x86, 0x6c, 0x64, 0xae, 0x05, 0x60, 0x85, 0x5c, 0x84, 0x67, 0x6f, 0xf6,
- 0xe0, 0x85, 0x5c, 0xe8, 0xe0, 0x75, 0x5b, 0xe3, 0x04, 0xe0, 0x5b, 0xa9,
- 0x2e, 0xe3, 0x60, 0x86, 0xd5, 0xc1, 0x00, 0x62, 0x75, 0xf3, 0xe0, 0x7f,
- 0x08, 0xe1, 0x04, 0xe0, 0x87, 0xcb, 0xb2, 0xe0, 0x87, 0xc2, 0x2d, 0xe1,
- 0xe0, 0x7b, 0xc5, 0xf0, 0x23, 0x04, 0x1d, 0x06, 0x11, 0x40, 0x60, 0x06,
- 0x13, 0x41, 0x50, 0x0f, 0x41, 0x1b, 0x08, 0x09, 0x40, 0x92, 0x40, 0x91,
- 0x40, 0x71, 0x0f, 0x0a, 0x40, 0x70, 0x0a, 0x15, 0x09, 0x60, 0x56, 0xc4,
- 0xe0, 0x29, 0x89, 0xfa, 0xe0, 0x86, 0x33, 0xf9, 0x08, 0x05, 0x60, 0x57,
- 0x3f, 0xe0, 0x30, 0x4d, 0x6d, 0xee, 0xe0, 0x3c, 0x8f, 0xe1, 0x04, 0xe0,
- 0x7f, 0xef, 0x74, 0x69, 0x67, 0x6f, 0x72, 0x73, 0xeb, 0xe0, 0x75, 0x9a,
- 0xf7, 0x60, 0x7c, 0x38, 0xcb, 0x44, 0xf6, 0x05, 0x07, 0xe0, 0x86, 0x00,
- 0x74, 0xae, 0x60, 0x56, 0x28, 0xd5, 0x02, 0xe8, 0xe0, 0x86, 0xb0, 0xf5,
- 0x0a, 0x06, 0x05, 0x06, 0x06, 0x07, 0x05, 0xe0, 0x85, 0xce, 0x73, 0x73,
- 0x79, 0xe3, 0xc3, 0xee, 0xf0, 0x06, 0xe0, 0x48, 0x9a, 0x6e, 0x79, 0xf5,
- 0xe0, 0x7f, 0xb5, 0x6c, 0x61, 0xf7, 0xe0, 0x76, 0x2a, 0xe7, 0x60, 0x51,
- 0x10, 0xe0, 0x34, 0xc9, 0x65, 0x62, 0xec, 0xd9, 0xfd, 0xe2, 0x08, 0x05,
- 0x1c, 0x60, 0x71, 0x96, 0xd5, 0x78, 0x74, 0xec, 0xe0, 0x73, 0x2c, 0xec,
- 0x02, 0x95, 0xe9, 0x02, 0x85, 0x73, 0xe8, 0xe0, 0x69, 0x3b, 0xe3, 0x04,
- 0xe0, 0x84, 0xf0, 0x2d, 0x69, 0x6e, 0x71, 0xf5, 0xe0, 0x3c, 0x0d, 0xae,
- 0xe0, 0x3a, 0xc3, 0xae, 0x03, 0xc0, 0xee, 0xf3, 0xe0, 0x86, 0x03, 0xf4,
- 0x60, 0x75, 0x98, 0xd1, 0x6d, 0xf3, 0x0d, 0x5a, 0xf5, 0x51, 0x79, 0x60,
- 0x2a, 0x95, 0x60, 0x28, 0xce, 0xc7, 0x21, 0x73, 0xe5, 0xe0, 0x48, 0x3c,
- 0xf2, 0x0e, 0x08, 0x06, 0x12, 0x40, 0x76, 0x40, 0x4b, 0x3d, 0x0a, 0x10,
- 0xe0, 0x85, 0xa6, 0x7a, 0x65, 0x77, 0x6f, 0xf2, 0xe0, 0x26, 0x49, 0x76,
- 0x63, 0xf9, 0xe0, 0x66, 0xdb, 0xf5, 0x05, 0x05, 0xe0, 0x86, 0xc5, 0x73,
- 0xfa, 0xe0, 0x23, 0x2b, 0x64, 0x65, 0x6e, 0xf4, 0xe0, 0x53, 0xc1, 0xef,
- 0x0c, 0x0c, 0x0a, 0x04, 0x06, 0x08, 0x0c, 0x10, 0x04, 0xe0, 0x86, 0x69,
- 0xf4, 0x02, 0x85, 0x6f, 0xee, 0xe0, 0x74, 0xa3, 0xe5, 0xe0, 0x7a, 0xa3,
- 0x70, 0x65, 0x72, 0xf4, 0x60, 0x2e, 0x39, 0xe0, 0x56, 0xc7, 0xed, 0xe0,
- 0x85, 0xb0, 0x6a, 0x65, 0xe3, 0xe0, 0x7f, 0xf7, 0x67, 0x72, 0x65, 0x73,
- 0xf3, 0xe0, 0x5c, 0xca, 0xe6, 0x07, 0x60, 0x56, 0xb0, 0xe0, 0x2f, 0xd1,
- 0x65, 0xf3, 0xc2, 0x34, 0xe4, 0x04, 0xe0, 0x86, 0x78, 0x75, 0x63, 0x74,
- 0x69, 0x6f, 0xee, 0x60, 0x84, 0x22, 0xc0, 0x7d, 0x63, 0xe8, 0xc1, 0x97,
- 0xae, 0x16, 0x54, 0xb0, 0x60, 0x41, 0xd2, 0x4d, 0xed, 0x5b, 0xd2, 0x42,
- 0x7f, 0x40, 0x57, 0x2c, 0x3d, 0x40, 0xcf, 0x40, 0x58, 0xc0, 0xf3, 0xf4,
- 0x04, 0xe0, 0x85, 0x29, 0x79, 0xf0, 0xe0, 0x43, 0xb1, 0xe9, 0x06, 0x30,
- 0x08, 0xe0, 0x81, 0xf8, 0xf6, 0x02, 0x96, 0x61, 0x74, 0x69, 0x7a, 0x65,
- 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 0x69, 0x6e, 0x73, 0x75, 0x72, 0x61,
- 0x6e, 0xe3, 0xe0, 0x78, 0xe8, 0xae, 0x0b, 0x60, 0x69, 0xe2, 0x58, 0x7f,
- 0x42, 0xc8, 0x40, 0x8a, 0xb1, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63,
- 0xe5, 0xe0, 0x31, 0xaa, 0x6e, 0x63, 0x69, 0x70, 0xe5, 0xe0, 0x63, 0xee,
- 0x6d, 0xe5, 0x04, 0xe0, 0x86, 0x03, 0x74, 0x65, 0xec, 0xe0, 0x6e, 0x91,
- 0xe5, 0x02, 0xa9, 0xf3, 0x05, 0x1e, 0xe0, 0x56, 0xba, 0xf3, 0x05, 0x09,
- 0xe0, 0x85, 0xe5, 0x65, 0xae, 0x60, 0x48, 0xb7, 0x60, 0x31, 0xfa, 0x9d,
- 0xae, 0x08, 0x60, 0x75, 0x77, 0x4c, 0xd3, 0xc1, 0x43, 0xed, 0x60, 0x83,
- 0xb3, 0xc1, 0x22, 0x69, 0xe4, 0xe0, 0x4d, 0x49, 0x71, 0x75, 0x61, 0x6c,
- 0x69, 0x66, 0x79, 0x6d, 0x65, 0x2e, 0x74, 0x6f, 0x64, 0xe1, 0xe0, 0x75,
- 0x54, 0x64, 0xae, 0x60, 0x29, 0x73, 0x60, 0x51, 0x2d, 0xc7, 0xf4, 0xe1,
- 0x05, 0x04, 0xe0, 0x81, 0xfa, 0xf8, 0xe0, 0x84, 0x2f, 0x6d, 0x65, 0xf2,
- 0xe0, 0x62, 0xd9, 0xae, 0x60, 0x80, 0x39, 0x03, 0x41, 0x33, 0xc2, 0xcd,
- 0xf0, 0x04, 0xe0, 0x66, 0xaa, 0xae, 0x53, 0xf5, 0x60, 0x5f, 0x59, 0x41,
- 0xe4, 0xcd, 0x76, 0xef, 0x10, 0x06, 0x06, 0x0a, 0x2a, 0x3b, 0x18, 0x0a,
- 0x29, 0x0b, 0x18, 0x04, 0x16, 0xe0, 0x7c, 0x0b, 0x7a, 0x6e, 0xe1, 0xe0,
- 0x22, 0x0a, 0x77, 0x69, 0xe1, 0xe0, 0x60, 0xa9, 0xf4, 0x04, 0xe0, 0x62,
- 0x8c, 0x61, 0xe7, 0xe0, 0x37, 0x67, 0x73, 0xf4, 0x05, 0x19, 0xe0, 0x85,
- 0x47, 0x73, 0x2d, 0x61, 0x6e, 0x64, 0x2d, 0x74, 0x65, 0x6c, 0x65, 0x63,
- 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x74, 0x69, 0xef, 0xe0,
- 0x21, 0x4f, 0x6d, 0x61, 0x6e, 0x2d, 0x65, 0x63, 0xe8, 0xe0, 0x4e, 0xae,
- 0xf2, 0x06, 0x13, 0x1c, 0xe0, 0x83, 0x69, 0xf4, 0x06, 0x60, 0x7b, 0xab,
- 0xc6, 0x59, 0xec, 0x04, 0xe0, 0x56, 0x58, 0x6c, 0x69, 0x67, 0xe1, 0xe0,
- 0x7e, 0x89, 0xf3, 0x03, 0x09, 0x87, 0x1f, 0x43, 0x61, 0x1f, 0x45, 0xcb,
- 0xe0, 0x34, 0x43, 0x67, 0x72, 0x75, 0xee, 0xe0, 0x7f, 0xd4, 0x61, 0x6e,
- 0xe7, 0x60, 0x7a, 0x15, 0xc5, 0x78, 0x64, 0xe5, 0xe0, 0x4d, 0xe1, 0xee,
- 0x03, 0x06, 0x86, 0x79, 0x2e, 0xe3, 0xe0, 0x75, 0xc0, 0x70, 0x65, 0xf3,
- 0xe0, 0x5d, 0xeb, 0x69, 0x61, 0x74, 0x6f, 0xf7, 0xe0, 0x4a, 0xd8, 0x6d,
- 0x6f, 0xf2, 0x03, 0xde, 0x79, 0x73, 0xeb, 0xc0, 0x60, 0xec, 0x04, 0x06,
- 0x05, 0x91, 0x74, 0x61, 0xf6, 0xe0, 0x61, 0x59, 0x6b, 0xef, 0xe0, 0x2d,
- 0xa7, 0xe9, 0x02, 0x8a, 0x74, 0xe9, 0x04, 0xe0, 0x83, 0xec, 0xe3, 0xe0,
- 0x82, 0xa5, 0x63, 0xe5, 0xcf, 0x48, 0xae, 0x60, 0x78, 0x1b, 0x41, 0x65,
- 0xc8, 0x6c, 0xeb, 0x04, 0xe0, 0x71, 0xf7, 0x72, 0x6f, 0xf6, 0xe0, 0x7f,
- 0x68, 0xe9, 0x02, 0x86, 0x76, 0x72, 0xef, 0xe0, 0x64, 0x87, 0x6e, 0xf4,
- 0x02, 0x85, 0x74, 0xef, 0xe0, 0x47, 0xff, 0x32, 0x74, 0xe8, 0xe0, 0x4a,
- 0x27, 0xe8, 0xe0, 0x82, 0xdb, 0xe4, 0x03, 0x07, 0x87, 0x7a, 0x6f, 0x6e,
- 0xe5, 0xe0, 0x56, 0x00, 0x6c, 0x61, 0x73, 0xe9, 0xe0, 0x47, 0x9e, 0x68,
- 0xe1, 0xc7, 0x00, 0xae, 0x60, 0x45, 0xc6, 0xe0, 0x3d, 0x4a, 0xee, 0x60,
- 0x79, 0x2e, 0x49, 0xda, 0xc1, 0x6a, 0xed, 0x04, 0xe0, 0x84, 0x66, 0xee,
- 0xe0, 0x82, 0xfb, 0xec, 0x0b, 0x18, 0x04, 0x08, 0x0c, 0x60, 0x20, 0x4b,
- 0xe0, 0x63, 0xdb, 0xf5, 0x05, 0x0d, 0xe0, 0x82, 0x70, 0x72, 0x69, 0x6e,
- 0x61, 0x63, 0x69, 0x6f, 0x6e, 0x61, 0xec, 0xe0, 0x82, 0x26, 0x6d, 0xe2,
- 0xe0, 0x6f, 0x9d, 0x6f, 0xae, 0xc1, 0x3c, 0x65, 0x73, 0xeb, 0x60, 0x5a,
- 0x91, 0xc9, 0xae, 0x63, 0xae, 0x06, 0x4e, 0xb2, 0xe0, 0x6e, 0xca, 0xe3,
- 0xe0, 0x20, 0xe9, 0xe1, 0x07, 0x04, 0x14, 0x26, 0xe0, 0x7f, 0x9f, 0xfa,
- 0xe0, 0x7e, 0xa4, 0xf9, 0x04, 0xe0, 0x84, 0x15, 0x73, 0x74, 0x61, 0x74,
- 0x69, 0x6f, 0xee, 0x04, 0xe0, 0x84, 0x0a, 0xad, 0xe0, 0x7f, 0x6f, 0xf4,
- 0x02, 0x94, 0x74, 0x65, 0xf2, 0x02, 0x84, 0xf0, 0xe0, 0x47, 0x5f, 0x2d,
- 0x61, 0x70, 0x70, 0xae, 0x60, 0x5f, 0xee, 0xe0, 0x24, 0x00, 0x66, 0x6f,
- 0x72, 0xed, 0x02, 0x85, 0x73, 0xe8, 0xe0, 0x65, 0xb2, 0xb0, 0xe0, 0x83,
- 0x0e, 0xee, 0x02, 0x86, 0xf4, 0x60, 0x79, 0xcd, 0xc3, 0x7b, 0x65, 0x74,
- 0x61, 0xf2, 0xe0, 0x7a, 0x9a, 0xe9, 0x16, 0x04, 0x08, 0x0a, 0x0c, 0x06,
- 0x12, 0x07, 0x0c, 0x07, 0x12, 0x0f, 0x60, 0x25, 0xda, 0x5e, 0xb5, 0x60,
- 0x30, 0xf4, 0xcc, 0x0c, 0xfa, 0xe0, 0x80, 0x15, 0x78, 0x6f, 0x6c, 0x69,
- 0xee, 0xe0, 0x4d, 0x1e, 0x74, 0x74, 0x73, 0x62, 0x75, 0x72, 0xe7, 0xe0,
- 0x4e, 0x8c, 0xf3, 0x06, 0x60, 0x6c, 0xe3, 0xd1, 0x17, 0x74, 0xef, 0xe0,
- 0x7d, 0xf7, 0x6f, 0x6e, 0xe5, 0xe0, 0x70, 0xd9, 0xee, 0x08, 0x05, 0x60,
- 0x81, 0xb3, 0x41, 0xcf, 0x81, 0x6f, 0xeb, 0xe0, 0x6b, 0x2d, 0xe2, 0xe0,
- 0x44, 0xce, 0x6d, 0x69, 0x65, 0xee, 0xe0, 0x7d, 0x60, 0xec, 0x04, 0xe0,
- 0x75, 0xaf, 0x6f, 0xf4, 0x60, 0x7c, 0xdd, 0xc4, 0x44, 0x67, 0x62, 0x6f,
- 0xe1, 0xe0, 0x6c, 0xd7, 0xe5, 0x02, 0x87, 0x6d, 0x6f, 0x6e, 0xf4, 0xe0,
- 0x7d, 0x3b, 0x64, 0x6d, 0x6f, 0x6e, 0xf4, 0xe0, 0x81, 0xec, 0xe3, 0x04,
- 0xe0, 0x81, 0x7b, 0xf4, 0x04, 0xe0, 0x71, 0x24, 0x75, 0xf2, 0xe0, 0x77,
- 0xed, 0x61, 0xe3, 0xe0, 0x60, 0x62, 0xe8, 0x0a, 0x06, 0x0d, 0x1c, 0x23,
- 0x60, 0x81, 0x2d, 0xc1, 0xb5, 0x79, 0x73, 0xe9, 0xe0, 0x82, 0x47, 0x78,
- 0x2e, 0x65, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65, 0xe4, 0xe0, 0x46, 0x86,
- 0xef, 0x05, 0x10, 0xe0, 0x6d, 0xa2, 0x74, 0xef, 0x06, 0x60, 0x81, 0x40,
- 0xc1, 0xd3, 0x67, 0x72, 0x61, 0x70, 0xe8, 0xe0, 0x80, 0xe0, 0x65, 0x6e,
- 0xe9, 0xe0, 0x53, 0x69, 0x69, 0xec, 0x02, 0x85, 0x69, 0xf0, 0xe0, 0x81,
- 0x2c, 0xe1, 0x02, 0x86, 0x74, 0x65, 0xec, 0xe0, 0x7b, 0x24, 0x64, 0x65,
- 0x6c, 0x70, 0x68, 0x69, 0xe1, 0x04, 0xe0, 0x80, 0xbe, 0x61, 0x72, 0xe5,
- 0xe0, 0x7d, 0x6c, 0x61, 0x72, 0x6d, 0x61, 0xe3, 0x04, 0xe0, 0x80, 0xab,
- 0x69, 0x65, 0xee, 0x04, 0xe0, 0x7f, 0xa7, 0xf3, 0xe0, 0x77, 0xb2, 0xe7,
- 0x07, 0x04, 0x60, 0x72, 0x60, 0xce, 0xf8, 0xe6, 0xe0, 0x58, 0x18, 0xe1,
- 0xcd, 0x4e, 0xe6, 0x04, 0xe0, 0x82, 0xba, 0x69, 0xfa, 0xe0, 0x6f, 0xfd,
- 0xe5, 0x0c, 0x12, 0x2c, 0x05, 0x09, 0x06, 0x06, 0x60, 0x81, 0x2c, 0xc1,
- 0x24, 0xf3, 0x02, 0x83, 0xe3, 0xdc, 0xd8, 0x61, 0x72, 0xef, 0x02, 0x81,
- 0x2d, 0x75, 0x72, 0xe2, 0xe0, 0x75, 0xef, 0xf2, 0x04, 0x04, 0x14, 0x84,
- 0xf5, 0xe0, 0x4e, 0x2d, 0xf3, 0x02, 0x87, 0x70, 0x65, 0x63, 0xf4, 0xe0,
- 0x32, 0x3c, 0x6f, 0xae, 0x60, 0x53, 0xa1, 0x60, 0x24, 0xf7, 0xc7, 0x17,
- 0xed, 0xe0, 0x73, 0xe6, 0xae, 0x06, 0x60, 0x5e, 0x94, 0xd9, 0xb6, 0xf3,
- 0xe0, 0x82, 0x6c, 0x70, 0xf0, 0xe0, 0x70, 0x55, 0xee, 0x04, 0xe0, 0x48,
- 0x24, 0xfa, 0xe0, 0x81, 0x0d, 0x65, 0x77, 0xe5, 0xe0, 0x74, 0x8c, 0x63,
- 0x6f, 0xf2, 0xe0, 0x5c, 0x15, 0xae, 0x60, 0x6b, 0xd1, 0x51, 0x12, 0x03,
- 0x44, 0x00, 0xc0, 0x62, 0xe4, 0x04, 0xe0, 0x80, 0xd6, 0x6e, 0xf3, 0xe0,
- 0x62, 0x46, 0xe3, 0x03, 0x07, 0x84, 0x6c, 0x6f, 0x75, 0xe4, 0xe0, 0x4c,
- 0x37, 0xe3, 0xe0, 0x80, 0x55, 0xae, 0x60, 0x7e, 0x70, 0xc2, 0x52, 0x62,
- 0xae, 0x60, 0x56, 0x1f, 0x60, 0x26, 0x99, 0x83, 0xe1, 0x11, 0x0d, 0x04,
- 0x05, 0x21, 0x40, 0x51, 0x18, 0x1d, 0x40, 0x4e, 0x10, 0x06, 0x32, 0xe0,
- 0x80, 0xb8, 0xf9, 0x04, 0xe0, 0x82, 0x06, 0x77, 0x68, 0x69, 0x72, 0xec,
- 0xe0, 0x81, 0xeb, 0xf6, 0xe0, 0x7c, 0x5a, 0x74, 0xf2, 0xe0, 0x7f, 0xd6,
- 0xf3, 0x02, 0x97, 0xf3, 0x02, 0x8e, 0x65, 0x6e, 0x67, 0x65, 0x72, 0x2d,
- 0x61, 0x73, 0x73, 0x6f, 0xe3, 0xe0, 0x64, 0xc8, 0x61, 0x67, 0xe5, 0xe0,
- 0x72, 0xe8, 0x61, 0x64, 0x65, 0xee, 0xe0, 0x7c, 0x5e, 0xf2, 0x09, 0x0f,
- 0x06, 0x04, 0x09, 0x0c, 0xe0, 0x7f, 0xc9, 0xf4, 0x06, 0x04, 0x60, 0x7f,
- 0xec, 0xb2, 0xee, 0xe0, 0x4d, 0xb7, 0xe9, 0xe0, 0x71, 0x58, 0x6f, 0x63,
- 0xe8, 0xe0, 0x65, 0x72, 0xed, 0xe0, 0x7c, 0x12, 0x6c, 0x69, 0x61, 0x6d,
- 0x65, 0xee, 0xe0, 0x43, 0x6f, 0x69, 0xf3, 0x04, 0xe0, 0x81, 0xa2, 0xae,
- 0x60, 0x7c, 0x3b, 0xc3, 0x3d, 0xe1, 0x06, 0x05, 0x06, 0xe0, 0x3f, 0xf7,
- 0x73, 0xe9, 0xe0, 0x61, 0xa4, 0x6c, 0x6c, 0xe5, 0xe0, 0x5d, 0x13, 0x63,
- 0x68, 0x75, 0xf4, 0xe0, 0x73, 0x28, 0xee, 0x05, 0x09, 0xe0, 0x5c, 0x5a,
- 0x74, 0x68, 0x65, 0x6f, 0x6e, 0xf3, 0xe0, 0x6d, 0x8f, 0xe1, 0x04, 0xe0,
- 0x7b, 0xf5, 0x73, 0xef, 0xc2, 0xc6, 0xec, 0x05, 0x0e, 0xe0, 0x6f, 0x62,
- 0xed, 0x04, 0xe0, 0x68, 0x3a, 0x73, 0x70, 0x72, 0x69, 0x6e, 0xe7, 0xe0,
- 0x7a, 0xc6, 0xe5, 0x04, 0xe0, 0x77, 0x4b, 0xf2, 0xe0, 0x70, 0x65, 0x67,
- 0xe5, 0x06, 0x04, 0x3a, 0xe0, 0x81, 0x08, 0xf8, 0xe0, 0x7b, 0x53, 0xf3,
- 0x02, 0x8e, 0x70, 0x65, 0x65, 0x64, 0x6d, 0x6f, 0x62, 0x69, 0x6c, 0x69,
- 0xfa, 0xe0, 0x78, 0x69, 0xae, 0x06, 0x08, 0x09, 0xe0, 0x5d, 0x12, 0x77,
- 0x69, 0x61, 0x72, 0xe4, 0xe0, 0x5b, 0x93, 0x74, 0x6f, 0x72, 0x70, 0x72,
- 0x6f, 0xea, 0xd2, 0x12, 0x69, 0x74, 0x2e, 0x68, 0x73, 0x2d, 0x68, 0x65,
- 0x69, 0x6c, 0x62, 0x72, 0x6f, 0xee, 0xe0, 0x56, 0xca, 0x66, 0x72, 0x6f,
- 0x6e, 0xf4, 0xe0, 0x80, 0xa5, 0xe4, 0x04, 0x04, 0xdb, 0xd4, 0xf5, 0xe0,
- 0x7b, 0x58, 0x65, 0x72, 0x62, 0xef, 0xe0, 0x70, 0xe9, 0x63, 0x69, 0xe6,
- 0xe0, 0x66, 0xbc, 0x61, 0x73, 0xae, 0x04, 0x0d, 0x0f, 0x87, 0x6d, 0x61,
- 0x73, 0x73, 0x69, 0x76, 0x65, 0x67, 0x72, 0xe9, 0xe0, 0x7c, 0x43, 0x68,
- 0x6f, 0x73, 0x74, 0x65, 0x64, 0x2d, 0x62, 0x79, 0x2d, 0x70, 0x72, 0xe5,
- 0xc0, 0xb1, 0x64, 0x61, 0x74, 0xe1, 0xe0, 0x5a, 0x15, 0x62, 0x65, 0x65,
- 0x62, 0xf9, 0xe0, 0x6c, 0xd4, 0xae, 0x60, 0x78, 0x51, 0x42, 0xfa, 0x41,
- 0x36, 0xc2, 0xcd, 0xef, 0x27, 0x11, 0x38, 0x0f, 0x2e, 0x10, 0x20, 0x40,
- 0x66, 0x40, 0x8d, 0x41, 0x49, 0x40, 0x4c, 0x1c, 0x40, 0xe9, 0x40, 0x60,
- 0x40, 0x40, 0x40, 0x60, 0x0a, 0x2b, 0x29, 0x40, 0x51, 0x40, 0x4f, 0x1f,
- 0x35, 0x33, 0x60, 0x35, 0xb3, 0xd8, 0xb2, 0xfa, 0x05, 0x07, 0xe0, 0x7d,
- 0xec, 0x75, 0xae, 0x60, 0x7a, 0x11, 0xc2, 0x6d, 0xef, 0xe0, 0x7f, 0x2f,
- 0xf9, 0x08, 0x0c, 0x05, 0x04, 0x15, 0xe0, 0x75, 0x4b, 0x73, 0x74, 0x72,
- 0x65, 0x2d, 0x73, 0x6c, 0x69, 0xe4, 0xe0, 0x56, 0xed, 0x6f, 0xe4, 0xe0,
- 0x3c, 0x0b, 0x67, 0xe1, 0xda, 0x9e, 0xe1, 0x07, 0x60, 0x22, 0x9c, 0xe0,
- 0x5d, 0x0f, 0x6d, 0xe1, 0x04, 0xe0, 0x75, 0x76, 0x7a, 0x61, 0x6b, 0xe9,
- 0xe0, 0x73, 0x30, 0x2e, 0xec, 0xe0, 0x80, 0x31, 0xf8, 0x05, 0x05, 0xe0,
- 0x2f, 0xe8, 0x66, 0xef, 0xe0, 0x7b, 0xed, 0xae, 0xe0, 0x61, 0xb9, 0xf7,
- 0x03, 0x09, 0x93, 0x6f, 0x2e, 0x63, 0x6f, 0x64, 0xe5, 0xe0, 0x7d, 0x41,
- 0xee, 0x05, 0x08, 0xe0, 0x4d, 0xbd, 0x70, 0x72, 0x6f, 0x76, 0xe9, 0xe0,
- 0x77, 0x4a, 0x2e, 0xf0, 0xe0, 0x80, 0x0c, 0xe1, 0x02, 0x87, 0x72, 0x69,
- 0x61, 0x73, 0xe1, 0xc5, 0x36, 0xee, 0xe0, 0x3e, 0x11, 0xf6, 0x06, 0x4c,
- 0x6f, 0xe0, 0x72, 0x7f, 0x65, 0x72, 0x68, 0x61, 0x6c, 0xec, 0xe0, 0x7a,
- 0xc2, 0xf5, 0x04, 0x0b, 0x07, 0x84, 0x74, 0x73, 0x79, 0x73, 0x74, 0x65,
- 0x6d, 0xf3, 0xe0, 0x7b, 0x49, 0xed, 0x60, 0x41, 0x2b, 0xe0, 0x3a, 0xcd,
- 0xe4, 0xe0, 0x36, 0x49, 0x63, 0xe8, 0xe0, 0x47, 0xa9, 0xf4, 0x09, 0x17,
- 0x18, 0x07, 0x60, 0x7e, 0x26, 0xc0, 0x46, 0x73, 0xf5, 0x05, 0x0c, 0xe0,
- 0x7b, 0x25, 0xeb, 0x04, 0xe0, 0x7e, 0xb4, 0x69, 0xae, 0x60, 0x63, 0x59,
- 0xd7, 0x55, 0xe3, 0xe0, 0x2a, 0x85, 0xef, 0x09, 0x04, 0x04, 0x60, 0x38,
- 0xa9, 0xe0, 0x30, 0x52, 0xf9, 0xe0, 0x3b, 0x71, 0xe9, 0xe0, 0x22, 0x34,
- 0x66, 0x75, 0xeb, 0xe0, 0x63, 0xc3, 0x68, 0x65, 0x72, 0xae, 0xe0, 0x75,
- 0x6f, 0xe1, 0x0b, 0x06, 0x06, 0x60, 0x23, 0xba, 0x60, 0x3f, 0xdd, 0xc0,
- 0xca, 0xf2, 0x60, 0x7b, 0x9c, 0xc1, 0x5f, 0x70, 0x2e, 0xe3, 0xe0, 0x72,
- 0x2f, 0xeb, 0x04, 0xe0, 0x21, 0xcf, 0x69, 0xae, 0x60, 0x76, 0x0d, 0x46,
- 0xde, 0xc1, 0x43, 0xf3, 0x0a, 0x04, 0x24, 0x06, 0x16, 0x09, 0x25, 0xe0,
- 0x77, 0xb6, 0x1f, 0x43, 0xf8, 0xa6, 0xf4, 0x02, 0x9d, 0xf2, 0x02, 0x93,
- 0xef, 0x06, 0x46, 0xa0, 0xe0, 0x4f, 0xe3, 0xf7, 0x04, 0xe0, 0x6a, 0xd8,
- 0x77, 0x6c, 0x6b, 0xf0, 0xe0, 0x7b, 0x58, 0x65, 0x2d, 0x74, 0xef, 0xe0,
- 0x38, 0x3e, 0xe5, 0xe0, 0x72, 0x89, 0x6f, 0x79, 0xf2, 0xe0, 0x77, 0xfe,
- 0xe8, 0x02, 0x84, 0xf5, 0xe0, 0x47, 0xaa, 0xe9, 0x02, 0x84, 0xee, 0xe0,
- 0x47, 0x73, 0x6d, 0x61, 0xae, 0x60, 0x7c, 0x76, 0xc1, 0x2e, 0xe5, 0x04,
- 0xe0, 0x79, 0xd8, 0xf4, 0xe0, 0x42, 0x00, 0xe1, 0x02, 0x85, 0x73, 0xe3,
- 0xe0, 0x73, 0x2e, 0xeb, 0x02, 0x8c, 0xe9, 0x04, 0xe0, 0x46, 0x3f, 0x6b,
- 0x61, 0x6d, 0xe9, 0xe0, 0x4f, 0x14, 0xe1, 0x06, 0x60, 0x7d, 0xe4, 0xc1,
- 0x10, 0x73, 0x61, 0x79, 0xe1, 0xe0, 0x2a, 0x61, 0x2e, 0xe8, 0x02, 0x87,
- 0x6f, 0x72, 0x64, 0xe1, 0xe0, 0x72, 0xbd, 0x65, 0xe4, 0xe0, 0x75, 0x3e,
- 0xf2, 0x0c, 0x04, 0x0d, 0x06, 0x0b, 0x40, 0xd0, 0x0f, 0x1b, 0xe0, 0x71,
- 0x8c, 0xf8, 0xe0, 0x4a, 0x39, 0xf3, 0x07, 0x60, 0x51, 0x18, 0xe0, 0x28,
- 0x7a, 0x69, 0xf4, 0xe0, 0x7e, 0x3e, 0xeb, 0x60, 0x69, 0x60, 0xcf, 0x27,
- 0xe9, 0x02, 0x84, 0x73, 0xf4, 0xd7, 0x77, 0xe7, 0xe0, 0x6f, 0xba, 0xe7,
- 0x05, 0x05, 0xe0, 0x7e, 0xa3, 0x61, 0xee, 0xe0, 0x70, 0x04, 0xae, 0x21,
- 0x06, 0x0a, 0x17, 0x09, 0x0c, 0x17, 0x0d, 0x0b, 0x09, 0x11, 0x0d, 0x49,
- 0xb9, 0x20, 0x2a, 0x60, 0x46, 0xfe, 0x2c, 0x40, 0x53, 0x4b, 0x2b, 0x03,
- 0x12, 0x25, 0x40, 0x72, 0x5a, 0x87, 0xc4, 0x09, 0xf9, 0x60, 0x7d, 0xa5,
- 0xc0, 0xba, 0xf5, 0x60, 0x7c, 0x9d, 0x0d, 0x2f, 0x40, 0x9b, 0xc1, 0x05,
- 0xf3, 0x60, 0x73, 0x2c, 0x41, 0xf0, 0x46, 0x46, 0x41, 0x31, 0x0a, 0x1b,
- 0x03, 0x14, 0x03, 0x40, 0x7a, 0x19, 0x05, 0x1c, 0x0f, 0xc0, 0xda, 0xf2,
- 0x60, 0x7c, 0x7f, 0x07, 0x40, 0x84, 0xc0, 0x65, 0xee, 0x60, 0x7c, 0x73,
- 0x40, 0x5d, 0x40, 0x7a, 0x40, 0x4f, 0xc0, 0xb6, 0xed, 0x60, 0x77, 0x85,
- 0x43, 0xb1, 0x41, 0x31, 0x03, 0x07, 0x03, 0x18, 0x17, 0x03, 0x40, 0x7a,
- 0x1e, 0x1c, 0x0f, 0x40, 0x73, 0xc0, 0x67, 0xeb, 0x60, 0x7c, 0x50, 0x03,
- 0x1f, 0x1a, 0x03, 0x1e, 0x40, 0x84, 0xc0, 0xfb, 0xe9, 0x60, 0x5d, 0x68,
- 0x5e, 0xe5, 0x18, 0x03, 0x1a, 0xc0, 0xe7, 0xe8, 0x60, 0x7c, 0x45, 0x32,
- 0x40, 0x7a, 0xc0, 0xbc, 0xe7, 0x60, 0x7c, 0x54, 0x17, 0x03, 0x1e, 0x40,
- 0x5c, 0x19, 0x0f, 0x21, 0x24, 0x40, 0x4f, 0xc0, 0x67, 0xe3, 0x60, 0x7a,
- 0xed, 0x41, 0x34, 0x39, 0x03, 0x1e, 0x40, 0x96, 0xc0, 0x82, 0xe2, 0x60,
- 0x74, 0x9a, 0x47, 0x77, 0x03, 0x07, 0x18, 0x1d, 0x1e, 0x40, 0x5c, 0x19,
- 0x05, 0x1c, 0xb3, 0x65, 0x67, 0x6f, 0xee, 0x04, 0xe0, 0x7b, 0xac, 0x74,
- 0x72, 0x61, 0xe9, 0xe0, 0x74, 0x4f, 0xe1, 0x05, 0x11, 0xe0, 0x76, 0x08,
- 0x6e, 0x67, 0xe5, 0x04, 0xe0, 0x7d, 0xc2, 0x63, 0x6c, 0x6f, 0x75, 0x64,
- 0x2e, 0xf4, 0xe0, 0x6a, 0x56, 0xe3, 0xe0, 0x7c, 0x5d, 0xae, 0x15, 0x06,
- 0x4a, 0x30, 0x60, 0x37, 0x4d, 0x4c, 0x23, 0x43, 0xed, 0x5b, 0x4b, 0x4d,
- 0x85, 0x41, 0x75, 0x40, 0x45, 0xc0, 0x72, 0xf4, 0x60, 0x7b, 0xc0, 0xc0,
- 0xd2, 0xed, 0xe0, 0x7d, 0x2f, 0xf0, 0x03, 0x11, 0x8b, 0xf0, 0x04, 0xe0,
- 0x77, 0x54, 0x65, 0xe7, 0x04, 0xe0, 0x77, 0xca, 0x1f, 0x43, 0xe5, 0xe0,
- 0x7a, 0x26, 0xef, 0x02, 0x84, 0xec, 0xe0, 0x40, 0x98, 0xe3, 0xe0, 0x41,
- 0x6e, 0xe5, 0x02, 0x88, 0x72, 0x61, 0x75, 0x6e, 0xe9, 0xe0, 0x76, 0x8d,
- 0xee, 0x06, 0x09, 0x0c, 0xe0, 0x7d, 0x4d, 0x73, 0x6f, 0x63, 0x69, 0x61,
- 0xec, 0xe0, 0x66, 0x99, 0x63, 0x72, 0x61, 0x66, 0x74, 0x2e, 0x68, 0x6f,
- 0xf3, 0xe0, 0x46, 0x4a, 0x61, 0xe9, 0xe0, 0x76, 0xc8, 0xef, 0x07, 0x06,
- 0x04, 0x05, 0xe0, 0x7c, 0x47, 0x73, 0x68, 0xe9, 0xe0, 0x33, 0x80, 0xf0,
- 0xe0, 0x61, 0x9c, 0x6b, 0xf5, 0xe0, 0x26, 0x97, 0x67, 0xf5, 0xe0, 0x5f,
- 0x4c, 0xee, 0x16, 0x03, 0x10, 0x0a, 0x09, 0x18, 0x13, 0x06, 0x09, 0x16,
- 0x0e, 0x08, 0x10, 0x60, 0x2b, 0x48, 0x60, 0x45, 0x2e, 0x4a, 0xfb, 0xad,
- 0xfa, 0xc0, 0x87, 0xf4, 0x02, 0x88, 0x68, 0x65, 0x77, 0x69, 0xe6, 0xe0,
- 0x74, 0x32, 0x61, 0xf2, 0xe0, 0x44, 0x7a, 0x72, 0xe5, 0x04, 0xe0, 0x74,
- 0x2f, 0xe4, 0xe0, 0x36, 0x99, 0x70, 0x6f, 0x72, 0x74, 0x65, 0xf2, 0xe0,
- 0x55, 0x7b, 0xef, 0x05, 0x04, 0xe0, 0x61, 0x8c, 0xed, 0xe0, 0x3a, 0xbd,
- 0xae, 0x04, 0xe0, 0x78, 0xd6, 0x66, 0x75, 0x6b, 0xf5, 0x60, 0x78, 0x39,
- 0xc3, 0x2f, 0xec, 0x04, 0xe0, 0x7c, 0xd2, 0x69, 0x6e, 0xe5, 0x04, 0xe0,
- 0x7c, 0xcb, 0xae, 0x60, 0x35, 0x2a, 0xe0, 0x45, 0x77, 0x6a, 0x75, 0xeb,
- 0xe0, 0x43, 0xd3, 0xe7, 0x60, 0x3b, 0xe7, 0x60, 0x40, 0x1d, 0xc0, 0xb9,
- 0xe6, 0x02, 0x8b, 0x6c, 0x61, 0x73, 0x68, 0x64, 0x72, 0x69, 0xf6, 0xe0,
- 0x68, 0x03, 0x61, 0x62, 0x72, 0x69, 0xe3, 0xe0, 0x46, 0x37, 0x64, 0x69,
- 0x67, 0x69, 0x74, 0x61, 0x6c, 0x6f, 0x63, 0x65, 0xe1, 0xe0, 0x7b, 0xbe,
- 0x63, 0x69, 0x6c, 0x6c, 0xe1, 0xe0, 0x64, 0x1f, 0xe1, 0x02, 0x89, 0x76,
- 0x73, 0x74, 0x61, 0x63, 0xeb, 0xe0, 0x7c, 0x5c, 0xe7, 0xe0, 0x43, 0xb6,
- 0xad, 0x05, 0x06, 0x0a, 0x11, 0x86, 0x77, 0x65, 0xe2, 0xe0, 0x60, 0xc5,
- 0x74, 0x68, 0x65, 0x2d, 0x77, 0x65, 0xe2, 0xe0, 0x6b, 0x1b, 0xf2, 0x02,
- 0x85, 0x69, 0xef, 0xe0, 0x6f, 0x10, 0x61, 0x6e, 0x63, 0x68, 0x65, 0xf2,
- 0xe0, 0x6d, 0x94, 0x6b, 0x33, 0xf3, 0xe0, 0x6f, 0x01, 0x61, 0x70, 0x74,
- 0x69, 0x62, 0xec, 0xe0, 0x75, 0x68, 0xed, 0x0a, 0x06, 0x05, 0x05, 0x21,
- 0x06, 0x07, 0xe0, 0x7b, 0xfa, 0xf5, 0x56, 0x0f, 0xe0, 0x25, 0x51, 0x6f,
- 0x74, 0xe5, 0xca, 0x21, 0x6e, 0x69, 0xf7, 0xd1, 0xdb, 0xe9, 0x08, 0x06,
- 0x0b, 0x60, 0x3a, 0x9c, 0xc9, 0x5d, 0x74, 0x61, 0xed, 0xe0, 0x36, 0xbe,
- 0x68, 0x61, 0x63, 0x68, 0x69, 0x6d, 0x61, 0xee, 0xe0, 0x77, 0x83, 0x2e,
- 0xee, 0x60, 0x78, 0x93, 0xc0, 0xf4, 0x67, 0x2e, 0xec, 0xe0, 0x55, 0xf6,
- 0xe5, 0x60, 0x2f, 0x0d, 0xe0, 0x4a, 0x48, 0xe1, 0x06, 0x04, 0x03, 0xe0,
- 0x53, 0x3f, 0xe8, 0xe0, 0x76, 0x79, 0xe5, 0xd5, 0xbb, 0x63, 0x68, 0x69,
- 0xae, 0x60, 0x72, 0x3a, 0xc7, 0x28, 0xec, 0x0a, 0x06, 0x04, 0x05, 0x05,
- 0x07, 0x0d, 0xe0, 0x7a, 0xbc, 0x73, 0x7a, 0xf4, 0xe0, 0x5f, 0x4d, 0xec,
- 0xe0, 0x7a, 0xe7, 0x6b, 0xf5, 0xe0, 0x26, 0xb7, 0x65, 0xe3, 0xe0, 0x21,
- 0x54, 0x64, 0x6e, 0x61, 0xf6, 0xe0, 0x7a, 0x1f, 0x62, 0x69, 0xe1, 0x02,
- 0x81, 0x2d, 0x74, 0x65, 0x6d, 0xf0, 0xe0, 0x63, 0xc1, 0xe1, 0x04, 0xe0,
- 0x6a, 0x06, 0x79, 0x61, 0xee, 0x5e, 0x19, 0xe0, 0x5d, 0x8f, 0xeb, 0x0c,
- 0x0f, 0x18, 0x09, 0x44, 0xf6, 0x60, 0x41, 0x2f, 0xe0, 0x30, 0x61, 0xf5,
- 0x04, 0x04, 0xdf, 0xcf, 0xf4, 0xe0, 0x37, 0x95, 0x69, 0x7a, 0xf5, 0xe0,
- 0x37, 0xc0, 0xe9, 0x04, 0xe0, 0x64, 0xe0, 0xee, 0x02, 0x83, 0xef, 0xc5,
- 0x19, 0x61, 0x77, 0xe1, 0x04, 0xe0, 0x7b, 0x75, 0xae, 0x60, 0x77, 0xa6,
- 0xc2, 0xbf, 0xe5, 0x04, 0xe0, 0x21, 0x31, 0x67, 0xe1, 0xc9, 0x9b, 0xe1,
- 0x04, 0x05, 0x0e, 0x88, 0x7a, 0xe1, 0xe0, 0x2b, 0x64, 0x79, 0xe1, 0x04,
- 0xe0, 0x78, 0xcf, 0x6d, 0x61, 0xae, 0x60, 0x76, 0x5d, 0xc3, 0xe7, 0x77,
- 0x61, 0xae, 0x60, 0x70, 0x6f, 0xc5, 0xd2, 0xe7, 0xcf, 0xfa, 0x6a, 0xe9,
- 0x04, 0xe0, 0x73, 0xf8, 0xf9, 0xe0, 0x2a, 0x9d, 0xe9, 0x07, 0x04, 0x08,
- 0x0b, 0xe0, 0x77, 0x25, 0x7a, 0xf5, 0xd1, 0xad, 0x74, 0x61, 0xae, 0x60,
- 0x6a, 0x45, 0xcf, 0xd6, 0xf3, 0x04, 0xe0, 0x3a, 0x77, 0x68, 0x69, 0xe4,
- 0xe0, 0x42, 0xae, 0xf2, 0x02, 0x84, 0xed, 0xe0, 0x3c, 0x62, 0x61, 0xf3,
- 0xe0, 0x3e, 0x60, 0xe8, 0x0a, 0x05, 0x06, 0x0e, 0x60, 0x39, 0xcb, 0xe0,
- 0x3c, 0x3f, 0x74, 0x61, 0xf7, 0xc8, 0x75, 0x6b, 0x75, 0xf2, 0xe0, 0x42,
- 0x8c, 0xe9, 0x04, 0xe0, 0x4f, 0x23, 0x72, 0x61, 0xae, 0x60, 0x42, 0x2e,
- 0xe0, 0x2d, 0xf0, 0x61, 0xf2, 0xe0, 0x5f, 0x1c, 0xe7, 0x08, 0x10, 0x0b,
- 0x07, 0x1f, 0xe0, 0x54, 0x16, 0xf5, 0x02, 0x88, 0x6e, 0x69, 0xae, 0x60,
- 0x70, 0x5f, 0xc4, 0x0a, 0x63, 0xe8, 0xe0, 0x76, 0x49, 0xef, 0x02, 0x84,
- 0xf3, 0xe0, 0x23, 0xd6, 0xf2, 0xe0, 0x30, 0x0a, 0xe9, 0x60, 0x3a, 0xff,
- 0xe0, 0x36, 0x12, 0xe1, 0x0a, 0x0a, 0x06, 0x60, 0x3a, 0x07, 0x48, 0xce,
- 0xd2, 0x95, 0x77, 0xe1, 0x04, 0xe0, 0x30, 0x11, 0xf2, 0xe0, 0x41, 0xe4,
- 0x73, 0x61, 0xf7, 0xe0, 0x35, 0xf4, 0xeb, 0xe0, 0x72, 0xd7, 0xae, 0x60,
- 0x4e, 0x95, 0xe0, 0x2a, 0x9c, 0xe6, 0x03, 0x08, 0xaa, 0x75, 0x6e, 0x61,
- 0x74, 0xef, 0xe0, 0x43, 0x08, 0xe6, 0x02, 0xa3, 0x69, 0xe3, 0x02, 0x91,
- 0x69, 0x61, 0x6c, 0xae, 0x04, 0xe0, 0x6a, 0xb9, 0x61, 0x63, 0x61, 0x64,
- 0x65, 0xed, 0xe0, 0x6a, 0x04, 0xe5, 0x04, 0xe0, 0x7a, 0x6b, 0x2d, 0x6f,
- 0x6e, 0x2d, 0x74, 0xe8, 0xe0, 0x6d, 0x22, 0xae, 0xe0, 0x5e, 0xd0, 0xae,
- 0x09, 0x04, 0x07, 0x60, 0x2f, 0xd3, 0xe0, 0x49, 0x83, 0xea, 0xe0, 0x7a,
- 0x16, 0x66, 0x61, 0x73, 0xe8, 0xe0, 0x5b, 0x15, 0xe2, 0xe0, 0x78, 0xa7,
- 0xe4, 0x09, 0x0a, 0x60, 0x54, 0x83, 0x4a, 0xcb, 0xcf, 0x04, 0x65, 0xf3,
- 0x04, 0xe0, 0x56, 0xb6, 0xf3, 0xe0, 0x56, 0xb5, 0xe1, 0x02, 0x84, 0xf7,
- 0xe0, 0x2a, 0x87, 0xf4, 0xe0, 0x3d, 0xa4, 0xe3, 0x05, 0x02, 0x02, 0x0d,
- 0x84, 0xf3, 0x84, 0xf0, 0x82, 0x69, 0x2e, 0x63, 0x75, 0x73, 0x74, 0x6f,
- 0x6d, 0x65, 0xf2, 0xe0, 0x55, 0xbb, 0xe8, 0xe0, 0x3f, 0xc9, 0xe5, 0x02,
- 0x86, 0x6c, 0x6f, 0xf4, 0xe0, 0x61, 0x98, 0x61, 0x6e, 0x6f, 0x67, 0x72,
- 0x61, 0x70, 0x68, 0xe9, 0x04, 0xe0, 0x5f, 0xc5, 0xf1, 0xe0, 0x69, 0x2b,
- 0xe2, 0x05, 0x08, 0x05, 0x04, 0x8c, 0xf5, 0x04, 0xe0, 0x75, 0x61, 0xf3,
- 0xde, 0xbd, 0x73, 0x65, 0xf2, 0xda, 0x93, 0xee, 0xe0, 0x74, 0x94, 0xe9,
- 0x06, 0x60, 0x78, 0x8a, 0xc1, 0x49, 0x68, 0xe9, 0xe0, 0x6e, 0xe6, 0xe1,
- 0x02, 0x85, 0x6e, 0x61, 0xfa, 0xd4, 0xd9, 0x6d, 0x61, 0xae, 0x60, 0x4d,
- 0xf4, 0xd0, 0x89, 0xe1, 0x02, 0x85, 0x72, 0xe1, 0xe0, 0x5e, 0x59, 0x6d,
- 0x69, 0x73, 0x68, 0x69, 0x72, 0x61, 0x73, 0x61, 0xf4, 0xe0, 0x78, 0x67,
- 0xee, 0x21, 0x18, 0x0b, 0x30, 0x1a, 0x1a, 0x14, 0x08, 0x41, 0xf3, 0x04,
- 0x1b, 0x09, 0x41, 0x46, 0x1c, 0x1d, 0x12, 0x41, 0xd6, 0x0b, 0x05, 0x42,
- 0xd7, 0x58, 0x9c, 0x57, 0xe2, 0x54, 0xa4, 0x51, 0xae, 0xa6, 0x1f, 0xc3,
- 0x06, 0x05, 0x05, 0xe0, 0x6b, 0x54, 0x78, 0x74, 0x74, 0xe5, 0x82, 0x66,
- 0xf2, 0xe0, 0x71, 0xeb, 0x65, 0x1f, 0x43, 0xe5, 0xc8, 0xea, 0xfa, 0x04,
- 0xe0, 0x79, 0x6a, 0xae, 0x60, 0x6d, 0x24, 0xc6, 0xdf, 0xf9, 0x0a, 0x07,
- 0x04, 0x04, 0x0c, 0x56, 0x9c, 0xe0, 0x5d, 0xc2, 0x75, 0x7a, 0x65, 0xee,
- 0xe0, 0x41, 0x1d, 0xf3, 0xe0, 0x6b, 0x8d, 0xee, 0xe0, 0x71, 0x7b, 0xe3,
- 0x04, 0xe0, 0x79, 0x45, 0x2e, 0xed, 0x60, 0x65, 0xde, 0xd1, 0x3d, 0xe1,
- 0x02, 0x84, 0xee, 0xe0, 0x78, 0x96, 0xe1, 0xc5, 0xa7, 0xf5, 0x07, 0x04,
- 0x52, 0x93, 0xe0, 0x66, 0x95, 0xef, 0xe0, 0x60, 0x30, 0x6d, 0xe1, 0x04,
- 0xe0, 0x21, 0x2c, 0x74, 0x61, 0xae, 0x60, 0x71, 0x70, 0xc6, 0x97, 0xf4,
- 0x06, 0x04, 0x06, 0xe0, 0x77, 0xe5, 0xf2, 0xe0, 0x78, 0x59, 0x64, 0x6c,
- 0x6c, 0xae, 0xc0, 0x6c, 0xae, 0x60, 0x76, 0x78, 0x04, 0x41, 0x85, 0x14,
- 0x88, 0xf3, 0x07, 0x04, 0x60, 0x5f, 0x6c, 0xd8, 0x80, 0xf7, 0xe0, 0x76,
- 0x64, 0x75, 0x70, 0x64, 0x61, 0xf4, 0xe0, 0x69, 0xd3, 0xf2, 0x4b, 0xf9,
- 0x60, 0x6b, 0xec, 0xc1, 0x06, 0xef, 0x16, 0x0c, 0x26, 0x0c, 0x39, 0x14,
- 0x40, 0x6f, 0x09, 0x08, 0x40, 0x4c, 0x04, 0x08, 0x14, 0x08, 0x2d, 0x14,
- 0x08, 0xe0, 0x77, 0x05, 0x7a, 0x61, 0x77, 0x61, 0x6f, 0x6e, 0x73, 0x65,
- 0xee, 0xe0, 0x76, 0x3d, 0xf7, 0x0a, 0x05, 0x07, 0x60, 0x4e, 0xa9, 0x59,
- 0xc1, 0xd0, 0x40, 0x72, 0xf5, 0xe0, 0x76, 0xd7, 0x61, 0x72, 0x75, 0xe4,
- 0xe0, 0x6a, 0xe9, 0x2d, 0x64, 0x6e, 0x73, 0xae, 0x06, 0x60, 0x76, 0x5d,
- 0xc2, 0x1f, 0xf4, 0xe0, 0x69, 0x34, 0xf6, 0x06, 0x52, 0xc8, 0xe0, 0x62,
- 0xd9, 0x65, 0x63, 0xef, 0xce, 0x41, 0xf4, 0x07, 0x06, 0x0f, 0x15, 0xe0,
- 0x77, 0xa4, 0x74, 0x65, 0xf2, 0xe0, 0x6d, 0x86, 0xef, 0x05, 0x05, 0xe0,
- 0x71, 0x61, 0x67, 0x61, 0xf7, 0xdd, 0x6c, 0xe4, 0xe0, 0x37, 0x76, 0x69,
- 0xe3, 0x02, 0x86, 0x69, 0x61, 0xf3, 0xe0, 0x76, 0x4b, 0x65, 0x61, 0x62,
- 0x6c, 0x65, 0x2e, 0x6e, 0xe5, 0xe0, 0x5a, 0x68, 0x61, 0x69, 0x72, 0x65,
- 0xf3, 0xd1, 0x47, 0xf3, 0x02, 0x86, 0x68, 0x69, 0xf2, 0xe0, 0x40, 0x75,
- 0xe5, 0x04, 0xe0, 0x77, 0x0d, 0x67, 0x61, 0xf7, 0xe0, 0x2e, 0xb6, 0xf2,
- 0x04, 0x24, 0x06, 0x89, 0xf4, 0x04, 0xe0, 0x71, 0xbf, 0xe8, 0x08, 0x0e,
- 0x60, 0x4c, 0xaa, 0xe0, 0x29, 0x4d, 0x77, 0x65, 0x73, 0x74, 0x65, 0x72,
- 0x6e, 0x6d, 0x75, 0x74, 0xf5, 0xe0, 0x51, 0x37, 0x66, 0x6c, 0x61, 0x6e,
- 0xeb, 0xe0, 0x67, 0x67, 0x66, 0x6f, 0xec, 0xe0, 0x70, 0x60, 0x65, 0x2d,
- 0x6f, 0x67, 0x2d, 0xf5, 0xe0, 0x71, 0xd5, 0xe4, 0x07, 0x0c, 0x04, 0x0c,
- 0xe0, 0x71, 0xaf, 0x72, 0xe5, 0x02, 0x84, 0xe9, 0xe0, 0x3e, 0x7b, 0xad,
- 0xe0, 0x6b, 0xd1, 0x6b, 0xe1, 0xdd, 0x85, 0x65, 0x73, 0x74, 0x65, 0x2d,
- 0x69, 0x64, 0x63, 0xae, 0xe0, 0x25, 0x9d, 0xad, 0x03, 0x04, 0x86, 0xef,
- 0xe0, 0x71, 0xaa, 0x66, 0x72, 0xef, 0xe0, 0x72, 0x9d, 0x61, 0xf5, 0xe0,
- 0x49, 0x19, 0xef, 0x04, 0xe0, 0x65, 0xbc, 0xf0, 0xe0, 0x76, 0xf8, 0x6e,
- 0x6f, 0x69, 0x63, 0xe8, 0xe0, 0x36, 0xd8, 0xed, 0x06, 0x0a, 0x05, 0xe0,
- 0x36, 0xbf, 0x65, 0xae, 0x04, 0xe0, 0x2b, 0x67, 0xe3, 0xe0, 0x74, 0xa6,
- 0x62, 0xf2, 0xe0, 0x50, 0xb4, 0xae, 0x14, 0x04, 0x05, 0x07, 0x06, 0x05,
- 0x5b, 0x31, 0x60, 0x50, 0xf1, 0x3c, 0x40, 0xf7, 0x40, 0xb8, 0x45, 0xcc,
- 0xc0, 0x79, 0xf4, 0xe0, 0x75, 0xdb, 0xf2, 0x60, 0x76, 0xa8, 0x8f, 0xf0,
- 0x60, 0x75, 0xd5, 0x40, 0xb2, 0xab, 0xee, 0x60, 0x6c, 0x42, 0xc9, 0xc4,
- 0x62, 0xf2, 0xe0, 0x67, 0x1d, 0xe1, 0x60, 0x75, 0xc6, 0xc1, 0xb4, 0xeb,
- 0xe0, 0x6c, 0x82, 0x69, 0x70, 0xae, 0x60, 0x59, 0x07, 0xc9, 0x1c, 0xe8,
- 0x02, 0x8c, 0xef, 0x02, 0x85, 0x73, 0xf4, 0xe0, 0x62, 0x15, 0xae, 0xe0,
- 0x59, 0x5a, 0x65, 0xea, 0xe0, 0x35, 0x67, 0xe7, 0x48, 0x30, 0x60, 0x26,
- 0x6c, 0xc7, 0xe1, 0xe4, 0x02, 0xa2, 0xe5, 0x02, 0x8d, 0x73, 0x2e, 0x6b,
- 0x38, 0x73, 0xae, 0x60, 0x22, 0xca, 0x09, 0xe0, 0x20, 0x1c, 0x62, 0x61,
- 0x6c, 0x61, 0x6e, 0x63, 0x65, 0x72, 0x2e, 0x6c, 0x69, 0x6e, 0x6f, 0x64,
- 0xe5, 0xe0, 0x77, 0x16, 0x61, 0xae, 0x60, 0x3f, 0xa5, 0xe0, 0x36, 0x3f,
- 0xe2, 0x03, 0x06, 0x86, 0x75, 0x73, 0xe8, 0xe0, 0x50, 0xdc, 0x6f, 0x72,
- 0xe9, 0xe0, 0x73, 0x29, 0x65, 0xef, 0xca, 0x38, 0xae, 0x60, 0x71, 0xa6,
- 0x43, 0xfd, 0xc1, 0x62, 0x2d, 0x69, 0x70, 0xae, 0x0a, 0x60, 0x58, 0x9f,
- 0x4f, 0x3e, 0x4c, 0xd8, 0xc2, 0x1f, 0xe3, 0x60, 0x67, 0xc0, 0xcc, 0x7e,
- 0xed, 0xe0, 0x33, 0x74, 0xec, 0x05, 0x0d, 0xe0, 0x76, 0xda, 0xae, 0x06,
- 0x60, 0x71, 0x7a, 0xc4, 0x73, 0xe3, 0x60, 0x67, 0x8d, 0xce, 0x4c, 0x2d,
- 0x61, 0x6d, 0x73, 0xad, 0xe0, 0x41, 0x59, 0xea, 0x04, 0xe0, 0x71, 0xed,
- 0xf3, 0xe0, 0x6a, 0xfb, 0xe9, 0x0f, 0x09, 0x0d, 0x40, 0x95, 0x07, 0x12,
- 0x23, 0x1f, 0x14, 0x60, 0x6d, 0x06, 0xc8, 0x99, 0x79, 0x6f, 0x64, 0x6f,
- 0x67, 0x61, 0xf7, 0xc9, 0x88, 0xf4, 0x02, 0x84, 0xf4, 0xe0, 0x4d, 0x0c,
- 0x65, 0x72, 0xef, 0xe0, 0x6f, 0x85, 0xf3, 0x05, 0x10, 0xe0, 0x72, 0xe9,
- 0xf3, 0x05, 0x05, 0xe0, 0x4c, 0xf2, 0x68, 0xe9, 0xe0, 0x33, 0xc1, 0xe1,
- 0x60, 0x74, 0xf0, 0x83, 0x68, 0xe9, 0x0c, 0x04, 0x05, 0x0c, 0x19, 0x04,
- 0x10, 0x03, 0x0a, 0x1c, 0xc4, 0x06, 0xf7, 0xe0, 0x70, 0x1d, 0x74, 0x6f,
- 0xf3, 0xc9, 0x4e, 0xef, 0x04, 0xe0, 0x71, 0xec, 0x6b, 0x6f, 0x70, 0xf0,
- 0xe0, 0x5a, 0x96, 0x6e, 0xef, 0x03, 0x07, 0x87, 0x73, 0x68, 0x69, 0xed,
- 0xe0, 0x35, 0x43, 0x6f, 0x6d, 0x6f, 0xf4, 0xe0, 0x71, 0x05, 0x6d, 0x69,
- 0xf9, 0xe0, 0x34, 0x8a, 0x6d, 0xe5, 0xdc, 0x3f, 0x6b, 0xe1, 0x03, 0xd1,
- 0x5a, 0xf4, 0x04, 0xe0, 0x6b, 0x6f, 0x73, 0x75, 0xf2, 0xe0, 0x3e, 0x48,
- 0xe9, 0xde, 0x46, 0x68, 0x61, 0x72, 0x61, 0xae, 0x60, 0x6f, 0xc8, 0xc2,
- 0x9f, 0xe1, 0x04, 0x05, 0x08, 0x85, 0x7a, 0xe1, 0xe0, 0x32, 0x17, 0x77,
- 0x61, 0x6b, 0x75, 0xf2, 0xe0, 0x71, 0x29, 0x72, 0xe9, 0xe0, 0x6c, 0x5a,
- 0x69, 0xfa, 0xe0, 0x71, 0x5b, 0xae, 0x60, 0x6b, 0x37, 0xc9, 0xa2, 0x72,
- 0x61, 0x73, 0xe1, 0xe0, 0x3e, 0x81, 0xee, 0x02, 0x8b, 0xef, 0x04, 0xe0,
- 0x2d, 0x2d, 0x6d, 0x69, 0xf9, 0xe0, 0x72, 0x06, 0xea, 0xe0, 0x74, 0xf2,
- 0xeb, 0x07, 0x09, 0x04, 0x09, 0xe0, 0x74, 0xfc, 0xef, 0x04, 0xe0, 0x74,
- 0x4a, 0x6c, 0xe1, 0xdc, 0xeb, 0xeb, 0xe0, 0x65, 0xe3, 0xe9, 0x04, 0xe0,
- 0x74, 0xc2, 0xf4, 0xe0, 0x67, 0x48, 0x61, 0xe8, 0xe0, 0x3d, 0xfa, 0xe9,
- 0x05, 0x04, 0x04, 0x04, 0x83, 0xfa, 0xe0, 0x3d, 0xb7, 0xed, 0xe0, 0x6b,
- 0x30, 0x6b, 0xe1, 0xd8, 0x58, 0xe8, 0xcf, 0x93, 0x67, 0x61, 0x74, 0x61,
- 0xae, 0x60, 0x72, 0x3e, 0xc2, 0x6b, 0xe5, 0x02, 0x8d, 0x72, 0x75, 0x63,
- 0x68, 0x6f, 0x6d, 0x6f, 0x73, 0x63, 0xe9, 0xe0, 0x71, 0xb9, 0xf0, 0xe0,
- 0x63, 0xae, 0xe3, 0x05, 0x0b, 0xe0, 0x74, 0xa4, 0x68, 0x69, 0x6e, 0x61,
- 0x6e, 0xae, 0x60, 0x59, 0xa8, 0xce, 0xdc, 0xae, 0x06, 0x60, 0x5d, 0xc5,
- 0xd4, 0x1f, 0xf4, 0xe0, 0x73, 0xdc, 0xe8, 0x08, 0x06, 0x05, 0x60, 0x70,
- 0x8f, 0xc3, 0x10, 0x73, 0x2e, 0xf5, 0xe0, 0x73, 0xa7, 0x6c, 0xe6, 0xe0,
- 0x4f, 0x39, 0x2d, 0x73, 0x65, 0x72, 0xf6, 0xe0, 0x72, 0xb0, 0xe7, 0x07,
- 0x05, 0x60, 0x69, 0x5a, 0xcc, 0x00, 0x72, 0xef, 0xe0, 0x5f, 0xa9, 0xef,
- 0x04, 0xe0, 0x75, 0x55, 0xae, 0x60, 0x2c, 0xe1, 0x60, 0x35, 0x49, 0x47,
- 0xf7, 0xc7, 0x90, 0xe6, 0x07, 0x03, 0x60, 0x74, 0x37, 0xc1, 0x08, 0xf3,
- 0xc5, 0xf9, 0xec, 0x60, 0x55, 0x1d, 0xe0, 0x20, 0x21, 0xe5, 0x13, 0x05,
- 0x11, 0x35, 0x0a, 0x40, 0xfd, 0x2d, 0x0f, 0x05, 0x06, 0x09, 0x06, 0x08,
- 0x60, 0x68, 0x30, 0xcb, 0x44, 0x79, 0xe1, 0xe0, 0x30, 0xd5, 0xf8, 0x04,
- 0xe0, 0x70, 0xe4, 0xf4, 0x04, 0xe0, 0x75, 0x15, 0x64, 0x69, 0x72, 0xe5,
- 0xe0, 0x52, 0xde, 0xf7, 0x09, 0x05, 0x0d, 0x05, 0x07, 0x06, 0xe0, 0x74,
- 0xe0, 0x79, 0xef, 0xe0, 0x4e, 0x29, 0xf3, 0x06, 0x60, 0x74, 0x8e, 0xc0,
- 0x6a, 0x70, 0x61, 0xf0, 0xe0, 0x6e, 0x71, 0x70, 0xef, 0xe0, 0x6e, 0x52,
- 0x6d, 0x65, 0x78, 0xe9, 0xe0, 0x6a, 0xe1, 0x6a, 0x65, 0xf2, 0xe0, 0x35,
- 0x4a, 0x68, 0x61, 0x6d, 0x70, 0xf3, 0xd5, 0xad, 0xf5, 0x04, 0xe0, 0x6e,
- 0x3f, 0x73, 0xf4, 0xe0, 0x63, 0xa4, 0xf4, 0x0d, 0x07, 0x06, 0x40, 0xda,
- 0x60, 0x3f, 0x57, 0x53, 0x16, 0xe0, 0x21, 0x6d, 0x6c, 0x69, 0x66, 0xf9,
- 0xe0, 0x73, 0xe8, 0x66, 0x6c, 0xe9, 0xe0, 0x6d, 0xf8, 0xae, 0x1f, 0x08,
- 0x0c, 0x12, 0x0e, 0x0c, 0x14, 0x0a, 0x05, 0x0c, 0x0f, 0x05, 0x0a, 0x0c,
- 0x0f, 0x4a, 0xc4, 0x60, 0x3b, 0xf8, 0x40, 0x6e, 0x2a, 0x29, 0x4b, 0x27,
- 0x04, 0x03, 0x37, 0xda, 0xf9, 0xf5, 0x60, 0x72, 0xb6, 0x0d, 0x2f, 0xc0,
- 0x9b, 0xf4, 0x60, 0x72, 0xb1, 0x1f, 0x11, 0x0c, 0x40, 0x7a, 0x19, 0x21,
- 0xb3, 0xf3, 0x60, 0x69, 0x3b, 0x41, 0xf0, 0x47, 0x81, 0x1b, 0x03, 0x14,
- 0x40, 0x7d, 0x19, 0x05, 0x1c, 0xc0, 0xe9, 0xf0, 0x60, 0x72, 0x9a, 0x03,
- 0x18, 0x17, 0x03, 0x40, 0x7a, 0x19, 0x05, 0x2b, 0xa4, 0xee, 0x60, 0x6a,
- 0x3c, 0x48, 0x46, 0x40, 0x5d, 0x40, 0xc9, 0xc0, 0xb6, 0xed, 0x60, 0x6d,
- 0x94, 0x43, 0xb1, 0x41, 0x31, 0x03, 0x07, 0x03, 0x18, 0x17, 0x40, 0x7d,
- 0x1e, 0x1c, 0x0f, 0xc0, 0x73, 0xeb, 0x60, 0x72, 0x62, 0x03, 0x39, 0x03,
- 0x1e, 0xc1, 0x7f, 0xea, 0x60, 0x73, 0x4b, 0x8f, 0xe9, 0x60, 0x53, 0x78,
- 0x5e, 0xe5, 0x18, 0x03, 0x03, 0x17, 0xc0, 0xe7, 0xe7, 0x60, 0x72, 0x6c,
- 0x17, 0x03, 0x40, 0x7a, 0x28, 0x21, 0x24, 0x40, 0x4f, 0xc0, 0x67, 0xe6,
- 0x60, 0x72, 0x5a, 0x91, 0xe5, 0x60, 0x68, 0xcc, 0x45, 0xdf, 0x44, 0x41,
- 0xc1, 0x23, 0xe3, 0x60, 0x72, 0x2c, 0x1f, 0x1a, 0x03, 0x1e, 0x40, 0x96,
- 0xc0, 0x82, 0xe2, 0x60, 0x6a, 0xa6, 0x47, 0x77, 0x0a, 0x18, 0x1d, 0x40,
- 0x7a, 0x19, 0x05, 0x1c, 0xb3, 0xe1, 0x60, 0x68, 0xa7, 0x41, 0x21, 0x48,
- 0x46, 0x22, 0x03, 0x38, 0x40, 0xa5, 0x24, 0x40, 0x4f, 0xc0, 0x67, 0x2d,
- 0x66, 0x72, 0x65, 0xe1, 0xe0, 0x3f, 0x03, 0xf3, 0x06, 0x07, 0x0b, 0xe0,
- 0x69, 0xdf, 0x73, 0xe5, 0x53, 0xc2, 0xe0, 0x59, 0x2d, 0x6f, 0x64, 0xe4,
- 0x04, 0xe0, 0x65, 0x67, 0xf4, 0xe0, 0x65, 0x63, 0xae, 0x02, 0x8a, 0x62,
- 0x75, 0x73, 0x6b, 0x65, 0x72, 0xf5, 0xe0, 0x70, 0x54, 0x61, 0x6b, 0x65,
- 0x72, 0xf3, 0xcb, 0xf6, 0xf2, 0x04, 0xe0, 0x6e, 0x8a, 0x64, 0x70, 0x6f,
- 0x6c, 0x2e, 0x6f, 0xf6, 0xe0, 0x73, 0x5f, 0x6d, 0xf5, 0xe0, 0x68, 0xa9,
- 0x6b, 0x6f, 0xae, 0xe0, 0x4b, 0x36, 0x64, 0x72, 0x65, 0x2d, 0x65, 0xe9,
- 0xe0, 0x20, 0xe0, 0x62, 0x72, 0xe1, 0xe0, 0x6d, 0xf2, 0x61, 0x74, 0x2d,
- 0x75, 0xf2, 0xe0, 0x6d, 0x85, 0xae, 0x0b, 0x60, 0x2c, 0x16, 0x60, 0x36,
- 0x92, 0x4e, 0xfa, 0xc0, 0xb7, 0xf5, 0x60, 0x71, 0x94, 0xc1, 0xd2, 0xe3,
- 0x04, 0xe0, 0x73, 0x5d, 0xae, 0x60, 0x66, 0xb9, 0xc8, 0x6d, 0xe2, 0x60,
- 0x72, 0x4e, 0x82, 0xe1, 0x1d, 0x04, 0x10, 0x0b, 0x40, 0x7f, 0x10, 0x30,
- 0x0d, 0x08, 0x40, 0x45, 0x40, 0x68, 0x06, 0x40, 0x7a, 0x04, 0x0a, 0x40,
- 0x68, 0x0d, 0x09, 0x08, 0x60, 0x6f, 0x16, 0xc1, 0x6a, 0xf9, 0xe0, 0x68,
- 0x47, 0xf6, 0x0a, 0x60, 0x65, 0x06, 0x44, 0x9b, 0x47, 0x2d, 0xc0, 0xb6,
- 0x6f, 0xe9, 0xe0, 0x71, 0xd2, 0xf5, 0x02, 0x84, 0xf3, 0xe0, 0x2d, 0x7e,
- 0xed, 0xe0, 0x56, 0xed, 0xf4, 0x0a, 0x40, 0x4d, 0x4d, 0x2d, 0x60, 0x30,
- 0x87, 0xc6, 0x28, 0xf5, 0x02, 0x90, 0x75, 0x72, 0x77, 0x65, 0x74, 0x65,
- 0x6e, 0x73, 0x63, 0x68, 0x61, 0x70, 0xf0, 0xe0, 0x5e, 0x2c, 0xf2, 0x07,
- 0x0c, 0x4b, 0x94, 0xe0, 0x63, 0xa8, 0x62, 0x72, 0x75, 0x6b, 0x73, 0x67,
- 0x79, 0x6d, 0xee, 0xe0, 0x62, 0x80, 0xe1, 0x04, 0xe0, 0x72, 0xde, 0xec,
- 0x05, 0x09, 0xe0, 0x70, 0xaf, 0x73, 0x63, 0x69, 0x65, 0x6e, 0xe3, 0xe0,
- 0x6c, 0x3d, 0x68, 0x69, 0x73, 0x74, 0x6f, 0x72, 0xf9, 0x04, 0xe0, 0x70,
- 0x9a, 0x6d, 0x75, 0x73, 0xe5, 0xe0, 0x69, 0x88, 0xe9, 0x02, 0x8b, 0x76,
- 0x65, 0x61, 0x6d, 0x65, 0x72, 0x69, 0xe3, 0xe0, 0x45, 0x1c, 0x6f, 0x6e,
- 0x61, 0xec, 0x05, 0x06, 0xe0, 0x70, 0x76, 0x68, 0x65, 0xf2, 0xe0, 0x2f,
- 0x82, 0x66, 0x69, 0x72, 0x65, 0x61, 0x72, 0xed, 0xe0, 0x6c, 0x05, 0x73,
- 0xf5, 0x04, 0xe0, 0x67, 0xbd, 0x73, 0x68, 0x69, 0x6f, 0x62, 0x61, 0xf2,
- 0xe0, 0x67, 0xb5, 0xf2, 0x07, 0x08, 0x0b, 0x05, 0xe0, 0x67, 0x68, 0x76,
- 0xe9, 0x60, 0x38, 0xbd, 0xe0, 0x30, 0x20, 0xf5, 0x02, 0x84, 0xf4, 0xe0,
- 0x21, 0xf9, 0xf3, 0xe0, 0x30, 0x20, 0x69, 0xf4, 0xe0, 0x30, 0xfa, 0xe1,
- 0x02, 0x87, 0x73, 0x68, 0x69, 0xee, 0xe0, 0x71, 0x1a, 0xae, 0x60, 0x6b,
- 0x17, 0xc6, 0x35, 0xf0, 0x02, 0x85, 0x6f, 0xec, 0xe0, 0x6b, 0x03, 0x6c,
- 0xe5, 0xe0, 0x59, 0xd3, 0x6f, 0x73, 0x68, 0x69, 0xed, 0xe0, 0x65, 0x28,
- 0xee, 0x0a, 0x05, 0x0c, 0x04, 0x04, 0x05, 0x07, 0x03, 0x04, 0x88, 0x79,
- 0xef, 0xe0, 0x39, 0xc6, 0xf4, 0x02, 0x84, 0xef, 0xe0, 0x39, 0xf5, 0x61,
- 0xee, 0xe0, 0x65, 0x13, 0xf0, 0xe0, 0x67, 0x38, 0xee, 0xe0, 0x2a, 0x14,
- 0x6d, 0x6f, 0xeb, 0xd8, 0xb0, 0x6b, 0x6f, 0x6b, 0xf5, 0xe0, 0x6d, 0x09,
- 0xea, 0xd8, 0x5f, 0xe7, 0xe0, 0x6d, 0x5a, 0x62, 0x75, 0xae, 0x60, 0x55,
- 0xa8, 0xc0, 0x74, 0xe1, 0x40, 0xa8, 0xe0, 0x55, 0x82, 0xed, 0x05, 0x0f,
- 0x0a, 0x3f, 0x85, 0xf3, 0x02, 0x88, 0x73, 0x6b, 0x6f, 0x67, 0xe1, 0xe0,
- 0x6c, 0xb0, 0xef, 0xe0, 0x6e, 0x66, 0xe9, 0x04, 0xe0, 0x6d, 0x39, 0x6b,
- 0xe1, 0xe0, 0x28, 0x49, 0xe5, 0x06, 0x03, 0x0a, 0xe0, 0x71, 0xc9, 0xf2,
- 0xc0, 0x8b, 0x67, 0xe1, 0x04, 0xe0, 0x2c, 0x66, 0xf7, 0xe0, 0x39, 0xb8,
- 0xae, 0x13, 0x06, 0x07, 0x06, 0x45, 0xcb, 0x60, 0x34, 0xd6, 0x55, 0xf7,
- 0x40, 0x48, 0x4e, 0x7c, 0x50, 0x43, 0xc0, 0x58, 0xf0, 0x60, 0x70, 0xfd,
- 0xc0, 0xb2, 0xed, 0x60, 0x6e, 0xa0, 0x41, 0x3e, 0xaf, 0xe5, 0x60, 0x70,
- 0x83, 0xc1, 0x23, 0xe1, 0xe0, 0x6f, 0xc4, 0x64, 0x61, 0xec, 0xd3, 0x58,
- 0x61, 0xf3, 0xe0, 0x51, 0xa8, 0x6c, 0x63, 0x68, 0xe9, 0xc3, 0xf7, 0xeb,
- 0x03, 0x04, 0x86, 0xec, 0xe0, 0x35, 0x82, 0x69, 0x6a, 0xe9, 0xe0, 0x36,
- 0x77, 0xe1, 0x0c, 0x0f, 0x03, 0x1d, 0x0f, 0x13, 0x08, 0x5b, 0x7e, 0xe0,
- 0x49, 0xba, 0xf4, 0x04, 0x05, 0xc7, 0x8f, 0x73, 0xf5, 0xe0, 0x28, 0xca,
- 0x6f, 0xed, 0xe0, 0x6d, 0x7c, 0xf3, 0xd3, 0x9e, 0xee, 0x02, 0x93, 0xef,
- 0x03, 0x05, 0x85, 0x74, 0xef, 0xe0, 0x6a, 0x43, 0x6a, 0xef, 0xe0, 0x69,
- 0xa3, 0xae, 0x60, 0x6e, 0xa5, 0xa4, 0x69, 0x69, 0x6b, 0x61, 0xf7, 0xde,
- 0x1a, 0xed, 0x05, 0x04, 0xe0, 0x30, 0x64, 0x75, 0xf2, 0xc4, 0x12, 0x69,
- 0xe3, 0xe0, 0x47, 0xc7, 0xe7, 0x05, 0x40, 0x77, 0xd6, 0xba, 0x61, 0x77,
- 0x61, 0xae, 0x60, 0x66, 0x4f, 0x46, 0x2c, 0x42, 0x2a, 0xc1, 0x6e, 0x64,
- 0x6f, 0x6d, 0x61, 0xf2, 0xe0, 0x2f, 0x2b, 0xae, 0x60, 0x66, 0x77, 0xc3,
- 0xd4, 0xe9, 0xe0, 0x55, 0x3c, 0x68, 0xe1, 0x04, 0xe0, 0x6d, 0x3b, 0xf2,
- 0xe0, 0x36, 0xc4, 0xe7, 0x03, 0x0c, 0x89, 0xef, 0x04, 0xe0, 0x6d, 0x2e,
- 0x79, 0xe1, 0x57, 0x13, 0xe0, 0x59, 0xe8, 0xe9, 0x04, 0xe0, 0x6b, 0xfa,
- 0xf3, 0xe0, 0x63, 0xb3, 0xe1, 0x09, 0x07, 0x10, 0x0c, 0x0b, 0x0f, 0x04,
- 0xda, 0x05, 0x74, 0xef, 0x45, 0xd3, 0xe0, 0x69, 0x91, 0xf3, 0x02, 0x84,
- 0xf5, 0xe0, 0x6a, 0x6a, 0x61, 0x6b, 0x69, 0xae, 0x60, 0x55, 0x8b, 0xda,
- 0x37, 0xf2, 0x04, 0xe0, 0x2f, 0x58, 0x65, 0x79, 0x61, 0xed, 0xe0, 0x2f,
- 0x54, 0x6f, 0x6b, 0xe1, 0x04, 0xe0, 0x6d, 0x3e, 0x6b, 0xf9, 0xdd, 0x4c,
- 0x6e, 0xef, 0x02, 0x85, 0x68, 0xe1, 0xe0, 0x20, 0x44, 0xae, 0x60, 0x6e,
- 0x25, 0xc1, 0x77, 0xe9, 0xe0, 0x38, 0x37, 0x68, 0xe1, 0xc3, 0xe3, 0x63,
- 0x68, 0x69, 0x6b, 0x61, 0x74, 0x73, 0x75, 0x75, 0xf2, 0xe0, 0x66, 0xdc,
- 0xe2, 0x04, 0xe0, 0x70, 0x8c, 0x61, 0xf2, 0xd6, 0xfd, 0x61, 0x6d, 0x65,
- 0x73, 0x6a, 0xe5, 0xd1, 0x93, 0x34, 0xf5, 0xe0, 0x5e, 0x98, 0x34, 0xf4,
- 0xe0, 0x57, 0xd0, 0xed, 0x26, 0x23, 0x41, 0x9d, 0x09, 0x07, 0x40, 0xcb,
- 0x13, 0x17, 0x0d, 0x06, 0x41, 0xc7, 0x07, 0x10, 0x0e, 0x06, 0x0d, 0x43,
- 0x4b, 0x07, 0x41, 0x59, 0x0b, 0x28, 0x0b, 0x60, 0x3b, 0x36, 0x4a, 0xa1,
- 0x5d, 0xaa, 0x41, 0x31, 0xc0, 0xd2, 0x9f, 0x02, 0x84, 0x44, 0xc1, 0xcb,
- 0x11, 0xc3, 0x02, 0x8a, 0xe5, 0x02, 0x84, 0xf3, 0xe0, 0x68, 0xb4, 0xec,
- 0xcb, 0x5c, 0xe1, 0x02, 0x89, 0x74, 0x74, 0x61, 0x2d, 0x76, 0x1f, 0xc3,
- 0xc9, 0xeb, 0xec, 0xcb, 0x65, 0xf9, 0x19, 0x05, 0x05, 0x0f, 0x40, 0x59,
- 0x09, 0x21, 0x05, 0x19, 0x07, 0x07, 0x07, 0x05, 0x32, 0x08, 0x28, 0x0d,
- 0x15, 0x06, 0x60, 0x68, 0xc8, 0xc5, 0xec, 0x77, 0xe9, 0xe0, 0x5b, 0xb1,
- 0x76, 0xee, 0xe0, 0x49, 0x4c, 0xf4, 0x02, 0x87, 0x75, 0x6c, 0x65, 0xe1,
- 0xe0, 0x6f, 0xa5, 0x69, 0xf3, 0xe0, 0x5e, 0x16, 0xf3, 0x03, 0x39, 0x8c,
- 0x70, 0x72, 0x65, 0x61, 0x64, 0x73, 0x68, 0x6f, 0x70, 0xae, 0x10, 0x08,
- 0x05, 0x60, 0x4d, 0xc9, 0x44, 0x5b, 0x4a, 0x52, 0x05, 0x49, 0x6d, 0x49,
- 0x9d, 0x91, 0xee, 0x60, 0x65, 0xf5, 0x49, 0x76, 0xc0, 0x4d, 0xe9, 0x60,
- 0x6f, 0x95, 0x9c, 0xe3, 0x06, 0x60, 0x60, 0x91, 0xce, 0xfc, 0xef, 0x04,
- 0xe0, 0x6d, 0x0b, 0xed, 0x48, 0x2a, 0xe0, 0x67, 0x93, 0x68, 0x6f, 0xf0,
- 0x04, 0xe0, 0x3a, 0xdf, 0x69, 0xe6, 0xe0, 0x51, 0xd0, 0x65, 0x63, 0x75,
- 0x72, 0x69, 0x74, 0x79, 0x63, 0x61, 0x6d, 0x65, 0x72, 0xe1, 0xe0, 0x5c,
- 0xca, 0x72, 0x61, 0x76, 0x65, 0x6e, 0xe4, 0xe0, 0x67, 0x14, 0xf0, 0x04,
- 0x03, 0x04, 0x87, 0xf3, 0xdc, 0xaf, 0xe9, 0xe0, 0x56, 0xe5, 0x68, 0x6f,
- 0x74, 0xef, 0xe0, 0x38, 0x26, 0xe5, 0x02, 0x86, 0x74, 0x73, 0xae, 0xe0,
- 0x51, 0x86, 0x70, 0xae, 0xe0, 0x4a, 0xca, 0x6f, 0xeb, 0xe0, 0x6b, 0xf8,
- 0xed, 0x02, 0x88, 0x65, 0x64, 0x69, 0x61, 0xf0, 0xe0, 0x43, 0xd0, 0x61,
- 0x69, 0x6c, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0xf4, 0xe0, 0x5b,
- 0xbc, 0x6b, 0x6f, 0x6c, 0xe1, 0xe0, 0x54, 0x64, 0x6a, 0x69, 0x6e, 0xef,
- 0xe0, 0x5d, 0x67, 0x69, 0x70, 0x68, 0xef, 0xe0, 0x22, 0x2c, 0x68, 0x6f,
- 0xed, 0xcf, 0x5f, 0xe6, 0x06, 0x08, 0x06, 0x10, 0xc0, 0x82, 0x74, 0x70,
- 0xae, 0x60, 0x50, 0xdc, 0xdc, 0x16, 0x72, 0x69, 0xf4, 0xe0, 0x38, 0xce,
- 0x6f, 0x72, 0x75, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x75, 0x6e, 0x69,
- 0xf4, 0xe0, 0x5e, 0xad, 0x61, 0x73, 0x74, 0xae, 0x04, 0xe0, 0x51, 0x0b,
- 0x73, 0xf0, 0xe0, 0x4f, 0x83, 0x65, 0x66, 0x66, 0x65, 0xe3, 0xe0, 0x63,
- 0x47, 0xe4, 0x07, 0x06, 0x06, 0x07, 0x06, 0xcc, 0x79, 0x72, 0x6f, 0xe2,
- 0xe0, 0x38, 0x67, 0x6f, 0x62, 0xe9, 0xe0, 0x20, 0x41, 0x69, 0x73, 0x73,
- 0xe5, 0xe0, 0x50, 0xb1, 0x64, 0x6e, 0x73, 0xae, 0xcd, 0xa3, 0x61, 0x74,
- 0x74, 0xef, 0xe0, 0x6c, 0x1b, 0xe3, 0x02, 0x87, 0x6c, 0x6f, 0x75, 0x64,
- 0xae, 0xc7, 0xb2, 0xe4, 0xdc, 0xa5, 0xe1, 0x02, 0x86, 0x73, 0x75, 0xf3,
- 0xe0, 0x61, 0xe8, 0x63, 0x74, 0x69, 0x76, 0x65, 0x64, 0x69, 0x72, 0x65,
- 0xe3, 0xd1, 0xe4, 0xae, 0x60, 0x67, 0x35, 0xc2, 0x1e, 0xad, 0x06, 0x05,
- 0x06, 0x0a, 0xc7, 0x1e, 0x77, 0xe1, 0xe0, 0x44, 0x6a, 0x72, 0x6f, 0xf5,
- 0xe0, 0x24, 0x5b, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0xf9, 0xe0, 0x4a,
- 0x40, 0x66, 0x69, 0x72, 0x65, 0x77, 0x61, 0xec, 0xdf, 0x50, 0xf8, 0x04,
- 0xe0, 0x6e, 0x8a, 0xae, 0xe0, 0x6b, 0x4e, 0xf7, 0x60, 0x2f, 0xd1, 0xe0,
- 0x3e, 0xb4, 0xf5, 0x10, 0x15, 0x40, 0x4d, 0x20, 0x06, 0x12, 0x07, 0x06,
- 0x04, 0x05, 0x53, 0x3a, 0xe0, 0x5a, 0x84, 0xf4, 0x02, 0x88, 0x75, 0x61,
- 0xec, 0x60, 0x5d, 0x39, 0xd1, 0x2f, 0x73, 0xf5, 0x04, 0xe0, 0x31, 0xad,
- 0xfa, 0xe0, 0x2c, 0xeb, 0xf3, 0x04, 0x15, 0x21, 0x8c, 0x69, 0xe3, 0x07,
- 0x05, 0x60, 0x6c, 0x1c, 0xc2, 0x2a, 0x69, 0xe1, 0xe0, 0x47, 0xf3, 0x61,
- 0xae, 0x60, 0x5d, 0x16, 0xcf, 0x0f, 0xe5, 0x04, 0xe0, 0x22, 0xfb, 0x75,
- 0xed, 0x07, 0x0a, 0x60, 0x64, 0x23, 0xca, 0x04, 0x76, 0x65, 0x72, 0x65,
- 0x6e, 0x69, 0xe7, 0xe0, 0x57, 0xec, 0xae, 0x60, 0x4b, 0xa6, 0x5c, 0x6f,
- 0x44, 0xec, 0xb2, 0x61, 0x73, 0x68, 0xe9, 0x04, 0xe0, 0x69, 0x07, 0xed,
- 0xe0, 0x2a, 0x16, 0xae, 0x60, 0x66, 0x6e, 0xc6, 0xeb, 0xf2, 0x03, 0x0a,
- 0x84, 0xef, 0x02, 0x84, 0xf4, 0xe0, 0x29, 0xd1, 0xf2, 0xcf, 0xfd, 0xed,
- 0xe0, 0x58, 0x12, 0xe1, 0x04, 0x04, 0xd8, 0x55, 0xf4, 0xe0, 0x35, 0x30,
- 0x6b, 0x61, 0xed, 0xdc, 0x9b, 0x6f, 0xf3, 0x60, 0x5c, 0x34, 0x82, 0xee,
- 0x03, 0x04, 0x85, 0xe9, 0xe0, 0x24, 0xb9, 0x63, 0xe9, 0xe0, 0x6a, 0x35,
- 0x61, 0xeb, 0xe0, 0x2c, 0xff, 0x6c, 0x68, 0x6f, 0xf5, 0xe0, 0x38, 0x2c,
- 0xeb, 0x5a, 0x61, 0xe0, 0x48, 0xe6, 0x69, 0xeb, 0xdd, 0x26, 0x67, 0xe9,
- 0xe0, 0x69, 0x10, 0x65, 0xee, 0x03, 0xc8, 0xb3, 0x63, 0xe8, 0xe0, 0x58,
- 0xe9, 0xf4, 0x08, 0x60, 0x6c, 0x0d, 0x40, 0xe7, 0xc0, 0xb7, 0xae, 0x60,
- 0x68, 0x3e, 0x03, 0x03, 0x41, 0x30, 0xc2, 0xcd, 0xf3, 0x07, 0x04, 0x60,
- 0x6b, 0xe0, 0xc1, 0xb5, 0xeb, 0xe0, 0x6a, 0xa5, 0xae, 0x60, 0x57, 0x16,
- 0x51, 0x12, 0x03, 0x41, 0x33, 0xc2, 0xcd, 0xf2, 0x06, 0x60, 0x6c, 0x8f,
- 0xc0, 0xf4, 0x61, 0x67, 0xef, 0xe0, 0x2f, 0x1f, 0xf0, 0x60, 0x6c, 0xc3,
- 0xc0, 0xb9, 0xef, 0x1a, 0x05, 0x0b, 0x13, 0x27, 0x26, 0x40, 0x4c, 0x11,
- 0x40, 0x54, 0x08, 0x0d, 0x04, 0x1e, 0x13, 0x20, 0x06, 0x08, 0x60, 0x6a,
- 0x43, 0x40, 0xa5, 0xc0, 0xdb, 0x1f, 0x43, 0xe5, 0xc1, 0x89, 0x7a, 0x69,
- 0x6c, 0x6c, 0x61, 0x2d, 0x69, 0xef, 0xe0, 0x64, 0x9d, 0xf6, 0x04, 0xe0,
- 0x6d, 0x47, 0xe9, 0x04, 0xe0, 0x6c, 0x67, 0x6d, 0x69, 0x65, 0x6e, 0x74,
- 0xef, 0xe0, 0x6b, 0x1c, 0xf4, 0x02, 0x9f, 0xef, 0x08, 0x07, 0x0b, 0x56,
- 0x67, 0xe0, 0x56, 0xb4, 0x79, 0x61, 0x6d, 0xe1, 0xe0, 0x68, 0x1e, 0x72,
- 0x63, 0x79, 0x63, 0x6c, 0xe5, 0x60, 0x6a, 0xf6, 0xc0, 0x57, 0xe2, 0xe0,
- 0x2c, 0x65, 0x65, 0xe7, 0xe0, 0x24, 0x5c, 0xf3, 0x0b, 0x0d, 0x05, 0x60,
- 0x33, 0x41, 0x60, 0x27, 0xc2, 0xce, 0x6d, 0xea, 0x02, 0x86, 0x1f, 0x43,
- 0xf8, 0xe0, 0x5e, 0xaa, 0xef, 0xe0, 0x5e, 0xa6, 0x65, 0xf5, 0xe0, 0x67,
- 0xcb, 0x63, 0x6f, 0xf7, 0x60, 0x6a, 0xc7, 0xc2, 0x2a, 0xf2, 0x06, 0x06,
- 0x0c, 0x04, 0x26, 0x83, 0x74, 0x67, 0xe1, 0xe0, 0x3b, 0x9b, 0xef, 0x04,
- 0xe0, 0x2a, 0xb8, 0x74, 0x73, 0x75, 0xeb, 0xe0, 0x24, 0xc4, 0xed, 0xe0,
- 0x66, 0x59, 0xe9, 0x04, 0x10, 0x04, 0x87, 0xf9, 0x02, 0x85, 0x6f, 0x73,
- 0xe8, 0xd7, 0x40, 0xe1, 0x04, 0xe0, 0x65, 0xee, 0xed, 0xd1, 0xb0, 0x6f,
- 0xeb, 0xc1, 0x62, 0x6d, 0x61, 0x63, 0xe8, 0xe0, 0x63, 0x54, 0x67, 0x75,
- 0xe3, 0xe0, 0x28, 0x86, 0xe5, 0xc9, 0xec, 0x64, 0x6f, 0xf6, 0xe0, 0x5c,
- 0xcd, 0xef, 0x04, 0xe0, 0x65, 0x03, 0x6e, 0x73, 0x63, 0x61, 0x6c, 0x65,
- 0xae, 0x60, 0x5f, 0x47, 0xcd, 0x2a, 0xee, 0x0b, 0x23, 0x10, 0x06, 0x06,
- 0x04, 0x60, 0x57, 0x3b, 0xc2, 0x48, 0x7a, 0xe1, 0x06, 0x09, 0x0b, 0xe0,
- 0x6a, 0xfd, 0xe5, 0x02, 0x91, 0x64, 0x65, 0x6c, 0x6c, 0xe1, 0x8c, 0xad,
- 0x02, 0x88, 0x65, 0x2d, 0x64, 0x65, 0x6c, 0x6c, 0x61, 0x2d, 0x62, 0x72,
- 0x69, 0xe1, 0xe0, 0x49, 0x84, 0xf4, 0x02, 0x85, 0x72, 0xe5, 0xe0, 0x62,
- 0xd7, 0x69, 0x63, 0x65, 0x6c, 0xec, 0xe0, 0x62, 0x4c, 0x6d, 0x6f, 0xf5,
- 0xe0, 0x37, 0x32, 0x67, 0x6f, 0x6c, 0xe9, 0xd9, 0xc6, 0xe5, 0xe0, 0x6a,
- 0x15, 0x61, 0xf3, 0xe0, 0x6b, 0x33, 0xed, 0x60, 0x66, 0xc1, 0x41, 0x8e,
- 0xc3, 0xeb, 0xec, 0x07, 0x60, 0x32, 0xf6, 0xe0, 0x37, 0xcb, 0x69, 0xf3,
- 0xe0, 0x66, 0x06, 0xeb, 0xe0, 0x61, 0x51, 0xe4, 0x08, 0x0b, 0x07, 0x60,
- 0x50, 0x6a, 0xc4, 0x89, 0xe5, 0x06, 0x60, 0x5c, 0x08, 0xc8, 0xdf, 0xec,
- 0xe0, 0x2d, 0x34, 0xe1, 0x60, 0x2c, 0x5f, 0xe0, 0x3f, 0xae, 0xae, 0xc8,
- 0xbd, 0xe3, 0x02, 0x89, 0x6b, 0x2e, 0x70, 0x73, 0x74, 0xed, 0xe0, 0x45,
- 0xa5, 0x68, 0x69, 0x7a, 0xf5, 0xe0, 0x63, 0xfb, 0xe2, 0x04, 0xe0, 0x23,
- 0x38, 0xe9, 0x06, 0x60, 0x6a, 0x8b, 0xc1, 0x5a, 0xae, 0x07, 0x06, 0x60,
- 0x5a, 0x0d, 0xc6, 0x89, 0xf4, 0x60, 0x69, 0xff, 0xc0, 0xb9, 0xee, 0x60,
- 0x6a, 0xd0, 0xc1, 0x05, 0x61, 0x72, 0xe5, 0xe0, 0x39, 0xe6, 0xae, 0x60,
- 0x67, 0x94, 0x40, 0x57, 0xc2, 0x76, 0xad, 0x02, 0x89, 0x73, 0x69, 0x65,
- 0x6d, 0x65, 0xee, 0xe0, 0x4e, 0x55, 0x69, 0x2d, 0x72, 0xe1, 0xe0, 0x61,
- 0xd9, 0xee, 0x60, 0x47, 0xde, 0xe0, 0x23, 0xd1, 0xed, 0x04, 0xe0, 0x5b,
- 0x42, 0xe1, 0x04, 0xe0, 0x6b, 0x9f, 0x66, 0x61, 0xee, 0xe0, 0x37, 0x06,
- 0xec, 0x06, 0x60, 0x69, 0xbf, 0xc1, 0xd3, 0xe2, 0x60, 0x4b, 0x70, 0xe0,
- 0x20, 0x21, 0xeb, 0x47, 0xaf, 0xe0, 0x63, 0xdb, 0xea, 0x02, 0x84, 0x1f,
- 0x43, 0xf8, 0x82, 0x6f, 0x6e, 0xe4, 0xe0, 0x25, 0x4f, 0xe9, 0x18, 0x1c,
- 0x40, 0x56, 0x34, 0x40, 0x4d, 0x0e, 0x40, 0xdf, 0x0a, 0x40, 0xa4, 0x0d,
- 0x23, 0x0a, 0x07, 0x0c, 0x22, 0x18, 0x05, 0x10, 0xd3, 0x03, 0x7a, 0xf5,
- 0x04, 0x07, 0x06, 0x85, 0x73, 0x61, 0x77, 0xe1, 0xe0, 0x33, 0xd2, 0x6e,
- 0x61, 0xed, 0xe0, 0x63, 0x88, 0x6d, 0xe1, 0xe0, 0x20, 0x8f, 0xe8, 0xe0,
- 0x66, 0x38, 0xf9, 0x02, 0x93, 0xef, 0x02, 0x84, 0xf4, 0xe0, 0x4f, 0xe0,
- 0x73, 0x68, 0x69, 0xae, 0x60, 0x60, 0x93, 0x41, 0x3e, 0x44, 0xb4, 0xac,
- 0xe1, 0x09, 0x0d, 0x05, 0x07, 0x08, 0x11, 0xe0, 0x60, 0x24, 0xfa, 0x03,
- 0xd0, 0xd1, 0x61, 0x6b, 0x69, 0xae, 0x60, 0x5e, 0x0c, 0xcc, 0x00, 0x77,
- 0xe1, 0xe0, 0x21, 0x97, 0x73, 0x68, 0x69, 0xf2, 0xe0, 0x33, 0x44, 0x6d,
- 0x61, 0xae, 0x60, 0x60, 0x2b, 0xc0, 0x6f, 0xeb, 0x02, 0xa3, 0xef, 0x02,
- 0x84, 0x6e, 0xef, 0xd1, 0xf1, 0xae, 0x60, 0x33, 0x75, 0xe0, 0x2c, 0xa6,
- 0xe4, 0xe0, 0x4f, 0x94, 0xf4, 0x06, 0x15, 0x0b, 0xe0, 0x6a, 0xc6, 0x73,
- 0xf5, 0x03, 0x05, 0x84, 0x6b, 0xe5, 0xe0, 0x67, 0x64, 0xe5, 0xe0, 0x63,
- 0x96, 0x62, 0x69, 0x73, 0xe8, 0xe0, 0x69, 0x54, 0xef, 0x07, 0x60, 0x24,
- 0xca, 0xe0, 0x3f, 0x30, 0xf9, 0xc5, 0xae, 0xe1, 0x02, 0x84, 0xee, 0xe0,
- 0x2e, 0x3d, 0xeb, 0x52, 0xcc, 0xe0, 0x52, 0xde, 0xf3, 0x05, 0x04, 0x09,
- 0x0a, 0x88, 0x75, 0xe7, 0xd1, 0x24, 0xf3, 0x04, 0xe0, 0x35, 0x98, 0xe9,
- 0xe0, 0x51, 0xc1, 0x68, 0x69, 0x6d, 0x61, 0xae, 0x60, 0x61, 0x45, 0xc4,
- 0xb5, 0x63, 0x6f, 0x6e, 0x66, 0x75, 0xf3, 0xdb, 0x6e, 0xe1, 0x04, 0x05,
- 0x12, 0x83, 0x77, 0xe1, 0xe0, 0x2d, 0xdd, 0x74, 0x6f, 0xae, 0x09, 0x60,
- 0x31, 0xbc, 0x60, 0x2f, 0x0c, 0xc4, 0x99, 0xf3, 0x60, 0x61, 0x1a, 0xc4,
- 0x78, 0xf3, 0xd2, 0xf4, 0x6b, 0x69, 0x2e, 0xef, 0x60, 0x65, 0x7c, 0xc3,
- 0xbc, 0x72, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0xae, 0x60, 0x4b, 0xfd, 0x40,
- 0x62, 0xdb, 0x17, 0xee, 0x07, 0x0b, 0x16, 0x07, 0x11, 0x06, 0x86, 0xf4,
- 0x04, 0xe0, 0x6a, 0x51, 0x65, 0x72, 0xe5, 0xe0, 0x53, 0x8b, 0xef, 0x08,
- 0x06, 0x04, 0x53, 0x9b, 0xe0, 0x4e, 0xd7, 0x6b, 0x61, 0xed, 0xe0, 0x2d,
- 0xcc, 0xe8, 0xe0, 0x69, 0x00, 0xe2, 0xcf, 0x45, 0x6e, 0x65, 0x73, 0xef,
- 0xe0, 0x5e, 0x5d, 0xe9, 0x06, 0x60, 0x53, 0xea, 0xd6, 0x3d, 0xf3, 0x04,
- 0xe0, 0x44, 0xd0, 0x69, 0x74, 0xe5, 0xcb, 0x29, 0xe5, 0x60, 0x36, 0x6b,
- 0xc4, 0xbd, 0x63, 0x6f, 0xed, 0xe0, 0x3b, 0x33, 0xe1, 0x07, 0x08, 0x40,
- 0x7d, 0xe0, 0x31, 0xba, 0x74, 0x6f, 0xae, 0x60, 0x67, 0x5c, 0xc1, 0x70,
- 0xed, 0x03, 0xc0, 0x74, 0xe9, 0x10, 0x0a, 0x07, 0x05, 0x0a, 0x06, 0x0c,
- 0x07, 0x05, 0x06, 0x0f, 0x08, 0x4f, 0xdc, 0xc4, 0x60, 0x79, 0x61, 0x6d,
- 0x61, 0x73, 0x68, 0x69, 0xf2, 0xd6, 0x79, 0x75, 0x6f, 0x6e, 0x75, 0xed,
- 0xd9, 0x3e, 0x74, 0xe1, 0xe0, 0x64, 0x83, 0x73, 0x61, 0x6e, 0x72, 0x69,
- 0x6b, 0xf5, 0xe0, 0x31, 0x0c, 0x6f, 0x67, 0x75, 0xee, 0xd0, 0xb1, 0xed,
- 0x02, 0x85, 0x69, 0x6e, 0xef, 0xd3, 0x23, 0xe1, 0xe0, 0x61, 0xc4, 0xe9,
- 0x03, 0xd1, 0xc0, 0xf3, 0xdf, 0x18, 0x65, 0xe3, 0xe0, 0x3d, 0xdc, 0x62,
- 0x6f, 0xf3, 0xe0, 0x68, 0x66, 0xe1, 0x04, 0x04, 0xcf, 0xf9, 0xf7, 0xe0,
- 0x5c, 0xcd, 0x73, 0x68, 0x69, 0xe7, 0xd9, 0xf5, 0xae, 0x60, 0x5c, 0x84,
- 0x42, 0x38, 0xc6, 0x2c, 0x2d, 0x61, 0x6c, 0x70, 0xf3, 0xe0, 0x40, 0x1d,
- 0x61, 0xf4, 0xe0, 0x63, 0x18, 0x6b, 0x61, 0xed, 0xd9, 0xcc, 0x6d, 0xe1,
- 0x04, 0xe0, 0x64, 0xc8, 0xf4, 0xe0, 0x21, 0x66, 0xec, 0x08, 0x06, 0x06,
- 0x60, 0x5f, 0xdd, 0xc9, 0x83, 0x69, 0x74, 0xe1, 0xe0, 0x5f, 0x4e, 0x61,
- 0xee, 0x60, 0x67, 0xf9, 0x81, 0xae, 0x20, 0x06, 0x0a, 0x07, 0x06, 0x08,
- 0x0a, 0x09, 0x09, 0x07, 0x06, 0x06, 0x08, 0x08, 0x60, 0x2c, 0x9a, 0x5a,
- 0xf9, 0x3e, 0x40, 0x48, 0x4f, 0x9f, 0x46, 0x10, 0x49, 0x0b, 0x40, 0x5d,
- 0xc0, 0x4a, 0xf6, 0x60, 0x5d, 0xfa, 0xca, 0x69, 0xf4, 0x60, 0x67, 0x5b,
- 0x03, 0x1f, 0x11, 0x40, 0xc0, 0xb3, 0xf3, 0x60, 0x67, 0x8d, 0x40, 0x7d,
- 0x99, 0xf2, 0x60, 0x67, 0x4d, 0xc0, 0x8b, 0xf0, 0x60, 0x67, 0x69, 0x17,
- 0x40, 0x96, 0xb0, 0xee, 0x60, 0x67, 0x3c, 0x40, 0x5d, 0x40, 0x96, 0xc0,
- 0xe9, 0xed, 0x60, 0x66, 0x01, 0x41, 0x31, 0x3c, 0xc1, 0xa0, 0xeb, 0x60,
- 0x67, 0x29, 0x22, 0x41, 0x04, 0xc0, 0xb6, 0xe9, 0x60, 0x48, 0x45, 0x5f,
- 0x03, 0x97, 0xe7, 0x60, 0x67, 0xd2, 0x19, 0xb0, 0xe5, 0x60, 0x5d, 0xac,
- 0xcb, 0x43, 0xe3, 0x60, 0x67, 0x32, 0x17, 0x03, 0xc0, 0xb4, 0xe2, 0x60,
- 0x67, 0x41, 0x40, 0x9b, 0x1c, 0xb3, 0xe1, 0x60, 0x5d, 0x96, 0x49, 0x67,
- 0x25, 0x40, 0xdd, 0xa4, 0xeb, 0x04, 0xe0, 0x64, 0xc0, 0xe1, 0x03, 0xc3,
- 0xda, 0xf3, 0xe0, 0x67, 0x7f, 0xe8, 0x04, 0xe0, 0x5a, 0x0b, 0xe1, 0x02,
- 0x8c, 0xf2, 0x04, 0xe0, 0x63, 0xfb, 0x61, 0xae, 0x60, 0x5e, 0x13, 0xc5,
- 0x98, 0x6d, 0x61, 0xae, 0x60, 0x3c, 0xde, 0x60, 0x21, 0x62, 0x40, 0xb7,
- 0x45, 0x32, 0xc3, 0x41, 0x67, 0x72, 0x61, 0x74, 0x69, 0x6f, 0xee, 0xe0,
- 0x52, 0x6c, 0x66, 0x75, 0x6e, 0xe5, 0xe0, 0x62, 0x25, 0xe5, 0x04, 0xe0,
- 0x67, 0x7b, 0xec, 0x60, 0x2c, 0x82, 0xe0, 0x27, 0x98, 0xe4, 0x06, 0x0a,
- 0x09, 0xe0, 0x52, 0xc4, 0x74, 0x72, 0x65, 0x2d, 0x67, 0x61, 0xf5, 0xe0,
- 0x33, 0xfd, 0x6f, 0x72, 0x69, 0xae, 0x60, 0x60, 0xc0, 0xc6, 0x6c, 0x61,
- 0x74, 0x6c, 0x61, 0xee, 0xe0, 0x4e, 0x33, 0xe3, 0x02, 0x8f, 0x72, 0xef,
- 0x02, 0x84, 0x73, 0xef, 0xc6, 0x2e, 0x6c, 0x69, 0x67, 0xe8, 0xe0, 0x64,
- 0x19, 0x68, 0x69, 0xe7, 0xe0, 0x3a, 0xb7, 0x62, 0xf5, 0xe0, 0x5d, 0x75,
- 0xe1, 0x02, 0x89, 0xf3, 0x04, 0xe0, 0x4c, 0xe1, 0xf4, 0xe0, 0x5a, 0x78,
- 0xed, 0xe0, 0x66, 0xb8, 0xae, 0x60, 0x63, 0xfe, 0x42, 0xcd, 0xc0, 0x5d,
- 0xe7, 0x60, 0x40, 0x76, 0xe0, 0x27, 0xb6, 0xe5, 0x13, 0x19, 0x18, 0x06,
- 0x1d, 0x2c, 0x27, 0x06, 0x06, 0x40, 0x74, 0x60, 0x36, 0xac, 0x60, 0x27,
- 0x52, 0xc8, 0xed, 0xf3, 0x02, 0x8e, 0xf3, 0x02, 0x87, 0x77, 0x69, 0x74,
- 0xe8, 0xe0, 0x3e, 0x61, 0xe9, 0xe0, 0x60, 0xd9, 0x61, 0x76, 0x65, 0x72,
- 0xe4, 0xe0, 0x64, 0x55, 0xf2, 0x05, 0x05, 0x08, 0xd5, 0x40, 0x1f, 0x43,
- 0xe5, 0xd5, 0x4b, 0x73, 0x65, 0x69, 0x6e, 0xe5, 0xe0, 0x38, 0xf5, 0x63,
- 0x6b, 0xed, 0xdc, 0x27, 0xee, 0x60, 0x67, 0x78, 0xc0, 0x68, 0xed, 0x06,
- 0x05, 0x07, 0xe0, 0x66, 0xed, 0x73, 0xe5, 0xe0, 0x5c, 0x12, 0x6f, 0x72,
- 0x69, 0xe1, 0xe0, 0x58, 0x00, 0x62, 0x65, 0x72, 0x73, 0x2e, 0x6c, 0xe9,
- 0xe0, 0x37, 0xae, 0xec, 0x0c, 0x05, 0x07, 0x60, 0x5b, 0x7e, 0x41, 0x29,
- 0x43, 0x71, 0xc1, 0x56, 0x68, 0xf5, 0xe0, 0x64, 0x2b, 0x62, 0x6f, 0x75,
- 0xf2, 0xe0, 0x52, 0x3f, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x6c, 0x65,
- 0x74, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0xe1, 0xe0, 0x66, 0x46, 0xe9,
- 0x02, 0x88, 0x77, 0x61, 0xae, 0x60, 0x5d, 0x1f, 0xc2, 0xbe, 0xee, 0x02,
- 0x87, 0x66, 0x6f, 0x72, 0xf5, 0xe0, 0x4e, 0xba, 0xad, 0x02, 0x87, 0x76,
- 0x69, 0x67, 0xef, 0xe0, 0x43, 0x1a, 0x69, 0x73, 0x65, 0x72, 0xf6, 0xe0,
- 0x43, 0x13, 0x67, 0x75, 0xf2, 0xe0, 0x62, 0x5a, 0xe5, 0x60, 0x4e, 0x79,
- 0xd7, 0xc7, 0xe4, 0x07, 0x40, 0x42, 0x0a, 0xe0, 0x67, 0x0b, 0xe9, 0x04,
- 0x0b, 0x0e, 0x88, 0x7a, 0x69, 0x6e, 0x68, 0x69, 0x73, 0x74, 0x6f, 0xf2,
- 0xd3, 0x75, 0xef, 0x02, 0x81, 0x2d, 0x63, 0x61, 0x6d, 0x70, 0x69, 0x64,
- 0xe1, 0xe0, 0x5c, 0x8b, 0xe3, 0x04, 0xe0, 0x5d, 0xb0, 0xe9, 0xdb, 0xdd,
- 0xe1, 0x05, 0x0d, 0xe0, 0x67, 0x1e, 0x74, 0x65, 0x63, 0x68, 0xae, 0x04,
- 0xe0, 0x43, 0x1b, 0xe2, 0xe0, 0x56, 0xb2, 0xae, 0x60, 0x63, 0x62, 0x41,
- 0x6c, 0x26, 0xc1, 0xc0, 0x65, 0x63, 0x69, 0x6e, 0xae, 0x60, 0x5b, 0xf2,
- 0xc7, 0xf4, 0xae, 0x0c, 0x06, 0x07, 0x60, 0x60, 0x43, 0x40, 0x9d, 0x43,
- 0x3f, 0xc2, 0x1a, 0xf3, 0x60, 0x65, 0x48, 0xc0, 0xaf, 0xf0, 0x60, 0x5d,
- 0x12, 0x48, 0xdf, 0x9b, 0xe5, 0x60, 0x5b, 0xac, 0xca, 0x69, 0xae, 0x0c,
- 0x05, 0x06, 0x60, 0x54, 0x05, 0x41, 0x04, 0x4c, 0x64, 0xc3, 0xfd, 0xf5,
- 0x60, 0x65, 0x0a, 0x83, 0xf4, 0x60, 0x64, 0xfb, 0xc1, 0xce, 0xf3, 0x60,
- 0x64, 0xff, 0xc0, 0xe9, 0xe4, 0x04, 0xe0, 0x66, 0xc8, 0xae, 0x60, 0x5b,
- 0x8c, 0xc7, 0x05, 0xe3, 0x07, 0x08, 0x07, 0x09, 0xe0, 0x66, 0xa2, 0xf0,
- 0x04, 0xe0, 0x41, 0x59, 0xf2, 0xc1, 0x60, 0x6b, 0x69, 0x6e, 0xf3, 0xe0,
- 0x5a, 0xc1, 0x64, 0x69, 0x72, 0xae, 0x60, 0x51, 0x5b, 0xd2, 0x5d, 0xae,
- 0x60, 0x56, 0x35, 0x4b, 0x06, 0xc3, 0xfd, 0xe2, 0x04, 0xe0, 0x65, 0x8f,
- 0xae, 0x60, 0x65, 0x2b, 0xc0, 0x62, 0xe1, 0x17, 0x15, 0x0c, 0x40, 0x73,
- 0x36, 0x40, 0x62, 0x12, 0x04, 0x40, 0x41, 0x0a, 0x2b, 0x16, 0x24, 0x1a,
- 0x07, 0x0d, 0x1e, 0xe0, 0x64, 0x39, 0xfa, 0x03, 0x05, 0x87, 0x75, 0xf2,
- 0xe0, 0x55, 0x54, 0x6f, 0x77, 0x73, 0xfa, 0xe0, 0x29, 0x87, 0x65, 0xf0,
- 0xe0, 0x40, 0xd9, 0x79, 0x66, 0x69, 0x72, 0x73, 0x74, 0xae, 0x60, 0x57,
- 0x41, 0xcc, 0xd8, 0xf4, 0x07, 0x0f, 0x40, 0x58, 0xe0, 0x65, 0x2e, 0xf4,
- 0x04, 0xe0, 0x61, 0xde, 0x61, 0x2d, 0x76, 0x61, 0x72, 0x6a, 0xea, 0xe0,
- 0x54, 0x8e, 0x73, 0xf5, 0x0a, 0x06, 0x04, 0x05, 0x12, 0x03, 0x10, 0x03,
- 0x04, 0x86, 0x7a, 0x61, 0xeb, 0xe0, 0x5c, 0xd2, 0x79, 0xe1, 0xd0, 0xc2,
- 0x75, 0xf2, 0xe0, 0x4a, 0xdf, 0xf3, 0x02, 0x8b, 0x68, 0xe9, 0x04, 0xe0,
- 0x22, 0x2e, 0x67, 0xe5, 0xe0, 0x61, 0x68, 0x61, 0xeb, 0xd0, 0xd6, 0xee,
- 0xd4, 0xe7, 0xed, 0x02, 0x89, 0x6f, 0x74, 0x6f, 0xae, 0x60, 0x60, 0xb5,
- 0xc2, 0xcd, 0xe1, 0xe0, 0x4a, 0x2b, 0xeb, 0xcf, 0x60, 0xe5, 0xe0, 0x61,
- 0x0e, 0xe4, 0x60, 0x62, 0x01, 0xc2, 0xb1, 0xe2, 0x02, 0x84, 0x75, 0xf3,
- 0xce, 0x73, 0xe1, 0xe0, 0x2e, 0x42, 0xe5, 0xe0, 0x3f, 0x66, 0xf3, 0x09,
- 0x04, 0x0d, 0x0a, 0x07, 0x06, 0xe0, 0x5a, 0xb3, 0xf5, 0xe0, 0x24, 0xb9,
- 0x73, 0xe1, 0x02, 0x81, 0x2d, 0x63, 0x61, 0x72, 0x72, 0xe1, 0xe0, 0x3f,
- 0x4b, 0x68, 0x69, 0xeb, 0x4c, 0xae, 0x60, 0x3d, 0x40, 0xcb, 0xd7, 0x66,
- 0x6a, 0x6f, 0xf2, 0xe0, 0x24, 0xbb, 0x65, 0x72, 0xe1, 0xe0, 0x54, 0x40,
- 0x61, 0xeb, 0xdf, 0x00, 0xf2, 0x0d, 0x0b, 0x0f, 0x07, 0x05, 0x05, 0x0e,
- 0x16, 0x60, 0x49, 0x25, 0xda, 0xc1, 0x79, 0xec, 0x04, 0xe0, 0x36, 0xc7,
- 0x68, 0xf5, 0xe0, 0x3f, 0x06, 0xf5, 0x02, 0x85, 0x6d, 0x6f, 0xf2, 0xd5,
- 0xb6, 0x67, 0x61, 0x6d, 0xe5, 0xe0, 0x58, 0x6d, 0x73, 0x68, 0x61, 0xec,
- 0xe0, 0x53, 0x53, 0x72, 0xe9, 0xe0, 0x64, 0x56, 0x6e, 0xe1, 0xe0, 0x36,
- 0xba, 0x6b, 0xe5, 0x04, 0xe0, 0x5a, 0x78, 0xf4, 0x60, 0x50, 0xc9, 0x52,
- 0xd1, 0xc1, 0xd3, 0xe9, 0x02, 0x88, 0x74, 0x69, 0xed, 0x60, 0x5b, 0x59,
- 0xc6, 0x60, 0xee, 0x02, 0x84, 0xe7, 0xe0, 0x5c, 0xe8, 0xe5, 0xe0, 0x53,
- 0x6d, 0x63, 0xe8, 0xe0, 0x5f, 0x2b, 0xf0, 0x04, 0xe0, 0x65, 0x46, 0x2e,
- 0x66, 0x61, 0x73, 0x74, 0x6c, 0xf9, 0x60, 0x33, 0x7e, 0xe0, 0x31, 0x9d,
- 0x6f, 0xf2, 0xd4, 0x14, 0xee, 0x10, 0x05, 0x04, 0x05, 0x0d, 0x07, 0x60,
- 0x35, 0x63, 0x60, 0x29, 0x68, 0x41, 0x3d, 0xc4, 0xfa, 0x74, 0xef, 0xe0,
- 0x2a, 0xbb, 0xf3, 0xe0, 0x43, 0xa6, 0x6e, 0xef, 0xe0, 0x57, 0xfe, 0xe7,
- 0x04, 0xe0, 0x64, 0x27, 0x79, 0x73, 0x68, 0x6c, 0xe1, 0xe0, 0x5f, 0xc6,
- 0x63, 0x68, 0x65, 0xf3, 0xe0, 0x5b, 0x04, 0xe1, 0x02, 0x84, 0xf5, 0xe0,
- 0x4c, 0xf7, 0x67, 0x65, 0x6d, 0xe5, 0xe0, 0x3c, 0xaa, 0x6d, 0x75, 0x72,
- 0x6f, 0x67, 0x61, 0xf7, 0xe0, 0x2c, 0x7e, 0xec, 0x08, 0x07, 0x08, 0x06,
- 0x03, 0xe0, 0x2b, 0x16, 0x73, 0x65, 0x6c, 0xf6, 0xe0, 0x63, 0xe9, 0x6f,
- 0x70, 0x6f, 0x6c, 0xf3, 0xe0, 0x3c, 0x0c, 0x6c, 0x6f, 0xf2, 0xe0, 0x39,
- 0x54, 0xe2, 0xc4, 0xbd, 0x61, 0x74, 0x76, 0x75, 0x6f, 0x70, 0xed, 0xe0,
- 0x5d, 0xec, 0xeb, 0x03, 0x07, 0x87, 0x75, 0x72, 0x61, 0x7a, 0xe1, 0xdf,
- 0xfe, 0x69, 0x6e, 0x6f, 0xe8, 0xe0, 0x23, 0xe4, 0xe5, 0xe0, 0x2b, 0x34,
- 0xe9, 0x0a, 0x05, 0x0b, 0x04, 0x60, 0x35, 0x3e, 0xe0, 0x25, 0x29, 0x7a,
- 0x75, 0xf2, 0xca, 0x4d, 0xee, 0x04, 0xe0, 0x5c, 0xfc, 0x74, 0x65, 0xee,
- 0xe0, 0x60, 0x7a, 0xec, 0xe0, 0x60, 0xa0, 0x62, 0x61, 0xf2, 0xc9, 0x7b,
- 0xe7, 0x03, 0x06, 0x8b, 0x6e, 0x65, 0xf4, 0xe0, 0x44, 0x86, 0x65, 0x6e,
- 0x74, 0x6f, 0x73, 0x69, 0x74, 0xe5, 0xe0, 0x55, 0xb2, 0x61, 0xfa, 0xe0,
- 0x60, 0x0b, 0x65, 0x62, 0x61, 0x73, 0xe8, 0xd4, 0xb1, 0xe4, 0x04, 0xe0,
- 0x62, 0x35, 0x72, 0x69, 0xe4, 0x60, 0x62, 0x32, 0xc2, 0x2a, 0xe3, 0x04,
- 0x04, 0x06, 0x8b, 0xf9, 0xe0, 0x62, 0x7e, 0x68, 0x69, 0xe4, 0xe0, 0x5f,
- 0x37, 0xe5, 0x02, 0x84, 0xf2, 0xe0, 0x54, 0x5f, 0xe9, 0xe0, 0x58, 0x61,
- 0xe1, 0xe0, 0x30, 0x43, 0xae, 0x60, 0x5e, 0xcc, 0x03, 0xc1, 0x33, 0xec,
- 0x20, 0x1d, 0x12, 0x0d, 0x40, 0x6e, 0x35, 0x15, 0x41, 0x2b, 0x04, 0x06,
- 0x09, 0x41, 0x2a, 0x0d, 0x40, 0xbd, 0x1b, 0x40, 0xef, 0x60, 0x33, 0x88,
- 0x52, 0xce, 0x4f, 0x37, 0x47, 0x81, 0xc1, 0x1c, 0x1f, 0xc3, 0x04, 0x0b,
- 0x04, 0x85, 0xf8, 0x05, 0x41, 0x33, 0xdb, 0xd2, 0x64, 0xe9, 0xe0, 0x55,
- 0xab, 0xe6, 0xe0, 0x35, 0x42, 0x64, 0xee, 0xe0, 0x5d, 0x66, 0xe1, 0xc4,
- 0xe9, 0xf9, 0x04, 0xe0, 0x63, 0xef, 0xee, 0x02, 0x84, 0xf8, 0xe0, 0x4b,
- 0x7e, 0xe7, 0x60, 0x55, 0x91, 0xc8, 0x1f, 0xf6, 0x06, 0x60, 0x48, 0xeb,
- 0xda, 0xf0, 0xae, 0x60, 0x48, 0xf8, 0xd5, 0x7c, 0xf5, 0x12, 0x04, 0x10,
- 0x11, 0x04, 0x0f, 0x12, 0x50, 0x3b, 0x60, 0x29, 0xf1, 0x57, 0xdf, 0x44,
- 0xbc, 0xcc, 0xb1, 0xfa, 0xe0, 0x53, 0xb6, 0xf8, 0x02, 0x84, 0xf5, 0xe0,
- 0x3d, 0x41, 0xe5, 0x04, 0xe0, 0x63, 0xb2, 0xed, 0xe0, 0x2d, 0x82, 0xee,
- 0x02, 0x84, 0xee, 0xe0, 0x58, 0xb3, 0xe4, 0x04, 0xe0, 0x62, 0xae, 0x62,
- 0xe5, 0xe0, 0x35, 0x71, 0xeb, 0xe0, 0x57, 0x80, 0xe7, 0x05, 0x04, 0xe0,
- 0x60, 0x44, 0xf3, 0xe0, 0x60, 0x47, 0x61, 0xee, 0xe0, 0x3a, 0xba, 0xe3,
- 0x03, 0x06, 0x84, 0x65, 0x72, 0xee, 0xe0, 0x5f, 0xdc, 0xe3, 0xe0, 0x5d,
- 0xdd, 0xe1, 0xe0, 0x4b, 0x8f, 0xe2, 0x05, 0x06, 0xe0, 0x52, 0xd3, 0x6c,
- 0x69, 0xee, 0xe0, 0x52, 0xc6, 0x61, 0x72, 0xf4, 0xc6, 0xf7, 0xf4, 0x05,
- 0x27, 0xe0, 0x63, 0x3a, 0xe4, 0x06, 0x60, 0x62, 0x54, 0xc1, 0x06, 0xae,
- 0x0a, 0x06, 0x04, 0x58, 0x6b, 0x60, 0x37, 0xb0, 0xc7, 0xf7, 0xf5, 0x60,
- 0x54, 0x18, 0xcd, 0x66, 0xe7, 0xe0, 0x61, 0xc8, 0xe3, 0x04, 0xe0, 0x61,
- 0x9f, 0x6f, 0x2e, 0xe9, 0xe0, 0x61, 0x82, 0xae, 0x60, 0x48, 0x57, 0x55,
- 0x7c, 0xc3, 0xfd, 0xf0, 0x06, 0x43, 0x39, 0xe0, 0x56, 0xe4, 0xec, 0x04,
- 0xe0, 0x63, 0x26, 0x66, 0x69, 0x6e, 0x61, 0x6e, 0xe3, 0xe0, 0x30, 0x20,
- 0xef, 0x14, 0x07, 0x05, 0x0d, 0x05, 0x0a, 0x0e, 0x07, 0x05, 0x2d, 0x17,
- 0x15, 0x07, 0x3b, 0x04, 0x07, 0x24, 0xe0, 0x60, 0x92, 0x79, 0x61, 0x6c,
- 0xe9, 0xe0, 0x4d, 0x73, 0x77, 0xe9, 0xe0, 0x4c, 0x45, 0x76, 0xe5, 0x06,
- 0x60, 0x4a, 0xbe, 0xd8, 0x36, 0x73, 0xe9, 0xe0, 0x3b, 0x04, 0x75, 0xf6,
- 0xe0, 0x5f, 0x45, 0xf4, 0x04, 0xe0, 0x54, 0x8f, 0xf4, 0x60, 0x61, 0xfa,
- 0x8f, 0xf3, 0x02, 0x84, 0xe5, 0xe0, 0x3a, 0x3d, 0x61, 0x6e, 0x67, 0xe5,
- 0xe0, 0x4c, 0xf5, 0x72, 0x65, 0x6e, 0xf3, 0xe0, 0x35, 0x22, 0x70, 0xf0,
- 0xe0, 0x5d, 0x98, 0xee, 0x04, 0x1f, 0xd0, 0x85, 0xe4, 0x02, 0x86, 0x72,
- 0x69, 0xee, 0xe0, 0x5a, 0x4c, 0x6f, 0xee, 0x04, 0xe0, 0x62, 0xb2, 0xae,
- 0x04, 0xe0, 0x60, 0x84, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0x61, 0x70, 0xf0,
- 0xe0, 0x44, 0x8c, 0xad, 0x04, 0xe0, 0x2d, 0x2e, 0xb2, 0xe0, 0x2d, 0x2e,
- 0xed, 0x07, 0x09, 0x60, 0x4a, 0x2f, 0xc6, 0xd8, 0x62, 0x61, 0x72, 0xe4,
- 0x60, 0x5a, 0x82, 0xc2, 0x68, 0xae, 0x60, 0x61, 0x1e, 0xc0, 0x76, 0xec,
- 0x04, 0xe0, 0x62, 0x7d, 0xe9, 0x02, 0x88, 0x74, 0x61, 0x70, 0x75, 0xee,
- 0xe0, 0x54, 0xa2, 0x70, 0xef, 0xe0, 0x54, 0x89, 0x68, 0x6d, 0x75, 0xf3,
- 0xe0, 0x4d, 0x1c, 0xe7, 0x05, 0x08, 0xe0, 0x61, 0x9f, 0x6f, 0x69, 0x70,
- 0xae, 0x60, 0x62, 0x1e, 0xb7, 0xe9, 0x02, 0x87, 0x73, 0x74, 0x69, 0xe3,
- 0xe0, 0x5e, 0xbc, 0xee, 0x02, 0x85, 0x74, 0xef, 0xe0, 0x4c, 0xfc, 0x6c,
- 0x69, 0x6e, 0x65, 0xae, 0x09, 0x60, 0x3e, 0x31, 0x60, 0x23, 0x33, 0xc0,
- 0x64, 0xf3, 0x04, 0xe0, 0x52, 0xc4, 0x65, 0x72, 0x76, 0x69, 0xe3, 0xe0,
- 0x61, 0xe5, 0xe6, 0xe0, 0x61, 0x06, 0x64, 0xe9, 0x60, 0x53, 0xcd, 0xcc,
- 0xee, 0xe3, 0x05, 0x04, 0xe0, 0x5d, 0xe0, 0xeb, 0xe0, 0x4f, 0x5e, 0x61,
- 0xec, 0x02, 0x87, 0x7a, 0x6f, 0x6e, 0xe5, 0xe0, 0x3e, 0xcb, 0xe8, 0x04,
- 0xe0, 0x40, 0x37, 0x6f, 0x73, 0x74, 0x2e, 0x64, 0x61, 0xf0, 0xe0, 0x3c,
- 0xa1, 0xe1, 0x04, 0xe0, 0x3e, 0xe5, 0xe2, 0x60, 0x50, 0x40, 0x82, 0xee,
- 0xe0, 0x4f, 0x67, 0xec, 0x60, 0x56, 0xa9, 0xca, 0x48, 0xeb, 0x04, 0xe0,
- 0x61, 0xe3, 0xb3, 0xe0, 0x4f, 0xfc, 0xe9, 0x12, 0x15, 0x0a, 0x05, 0x40,
- 0x42, 0x2b, 0x13, 0x14, 0x0f, 0x16, 0x07, 0x04, 0x60, 0x5f, 0x7a, 0xc1,
- 0x6a, 0xf6, 0x05, 0x05, 0xe0, 0x60, 0xe6, 0x6f, 0xf2, 0xe0, 0x57, 0x13,
- 0x69, 0x6e, 0xe7, 0x60, 0x3f, 0xee, 0x5f, 0xa6, 0xc2, 0x2a, 0x74, 0x74,
- 0x6c, 0x65, 0x73, 0x74, 0xe1, 0xe0, 0x4f, 0x9e, 0x70, 0xf3, 0xe0, 0x60,
- 0x0a, 0xee, 0x07, 0x0c, 0x16, 0x14, 0xe0, 0x53, 0x5c, 0x6f, 0x64, 0x65,
- 0x6f, 0x62, 0x6a, 0x65, 0x63, 0xf4, 0xe0, 0x3f, 0xf7, 0xeb, 0x04, 0xe0,
- 0x61, 0x8f, 0x79, 0x61, 0x72, 0xe4, 0x04, 0xe0, 0x4a, 0x18, 0x2d, 0x63,
- 0x6c, 0x6f, 0x75, 0xe4, 0xe0, 0x5b, 0xa4, 0xe4, 0x03, 0x05, 0x87, 0x1f,
- 0xc3, 0xe0, 0x5d, 0xf3, 0xe5, 0x60, 0x2c, 0x33, 0xe0, 0x35, 0x41, 0xe1,
- 0xe0, 0x5d, 0xe9, 0x63, 0xef, 0xc6, 0x7d, 0xed, 0x05, 0x05, 0xe0, 0x60,
- 0x71, 0x69, 0xf4, 0xe0, 0x42, 0xb3, 0xe1, 0x03, 0x05, 0x84, 0x6e, 0xef,
- 0xe0, 0x4f, 0xb0, 0xae, 0xe0, 0x43, 0x55, 0x2d, 0x63, 0x69, 0x74, 0x79,
- 0xae, 0x06, 0x60, 0x61, 0x04, 0x03, 0x83, 0x72, 0x6f, 0x63, 0xeb, 0xe0,
- 0x60, 0xf6, 0x6c, 0xec, 0x04, 0xe0, 0x5f, 0x94, 0xe5, 0x02, 0x84, 0xf3,
- 0xe0, 0x55, 0x0a, 0x68, 0x61, 0xed, 0xe0, 0x20, 0xad, 0x6b, 0xe5, 0x04,
- 0xe0, 0x61, 0x22, 0xf3, 0x02, 0x87, 0x63, 0x61, 0x6e, 0xe4, 0xe0, 0x43,
- 0x38, 0x2d, 0xf0, 0xd2, 0x91, 0xe7, 0x05, 0x04, 0xe0, 0x5f, 0xa0, 0xf5,
- 0xe0, 0x5b, 0x69, 0x68, 0xf4, 0xe0, 0x4c, 0x64, 0x66, 0xe5, 0x05, 0x06,
- 0xe0, 0x60, 0xf8, 0x73, 0x74, 0xf9, 0xe0, 0x5f, 0xa1, 0x69, 0x6e, 0x73,
- 0x75, 0x72, 0xe1, 0xe0, 0x3b, 0x99, 0x65, 0xf2, 0x60, 0x4a, 0xff, 0xd4,
- 0xfa, 0xe4, 0xe0, 0x5f, 0x2f, 0x62, 0xae, 0x18, 0x0b, 0x04, 0x4c, 0x6d,
- 0x13, 0x60, 0x3a, 0x63, 0x0b, 0x07, 0x04, 0x03, 0x04, 0x1c, 0x0a, 0x02,
- 0x06, 0x10, 0x06, 0x0b, 0x0d, 0xd5, 0x4f, 0xee, 0x60, 0x47, 0x1e, 0x03,
- 0x03, 0x03, 0x12, 0x0b, 0x13, 0x83, 0xe8, 0xe0, 0x59, 0x1c, 0xe4, 0x41,
- 0x7d, 0xe0, 0x45, 0xce, 0xe7, 0x02, 0x84, 0xe2, 0xe0, 0x5f, 0x8d, 0xae,
- 0x60, 0x45, 0xcb, 0xd9, 0xd3, 0xe5, 0x14, 0x07, 0x04, 0x0a, 0x04, 0x04,
- 0x0a, 0x0a, 0x09, 0x17, 0x0f, 0x06, 0x15, 0x15, 0x60, 0x31, 0x46, 0xe0,
- 0x2d, 0x53, 0x7a, 0x61, 0x6a, 0xf3, 0xe0, 0x4f, 0x07, 0xf8, 0xe0, 0x5c,
- 0x55, 0x77, 0x69, 0x73, 0x6d, 0x69, 0x6c, 0xec, 0xe0, 0x59, 0xfd, 0xf6,
- 0xe0, 0x4b, 0x1f, 0xf3, 0xe0, 0x52, 0x70, 0xee, 0x04, 0xe0, 0x26, 0xbe,
- 0x75, 0xe7, 0xe0, 0x5f, 0x1e, 0xec, 0x04, 0xe0, 0x5f, 0xae, 0x75, 0xf8,
- 0xe0, 0x49, 0xa2, 0xeb, 0x04, 0xe0, 0x5b, 0x2c, 0xf3, 0xe0, 0x26, 0xa9,
- 0xe9, 0x03, 0x08, 0x87, 0x74, 0x75, 0x6e, 0x67, 0xf3, 0xe0, 0x20, 0x1f,
- 0xf2, 0x60, 0x26, 0x99, 0xe0, 0x2b, 0x33, 0xeb, 0xe0, 0x4a, 0xe7, 0xe7,
- 0x08, 0x60, 0x39, 0x4c, 0x60, 0x26, 0x03, 0xb1, 0x6e, 0x69, 0xe3, 0xe0,
- 0x52, 0x72, 0x66, 0x72, 0xe1, 0xe0, 0x5e, 0x60, 0xe3, 0x03, 0x05, 0x86,
- 0x7a, 0xee, 0xe0, 0x26, 0x12, 0x6c, 0x65, 0xf2, 0xe0, 0x54, 0xdd, 0xe3,
- 0x60, 0x59, 0xfa, 0xc4, 0xb8, 0xe2, 0x03, 0x06, 0x85, 0x74, 0x69, 0xed,
- 0xe0, 0x28, 0x80, 0x6f, 0xf2, 0xe0, 0x4e, 0x84, 0x65, 0x73, 0xe2, 0xe0,
- 0x58, 0xbe, 0xe1, 0x05, 0x06, 0x03, 0xdf, 0xcb, 0xf3, 0x60, 0x51, 0x9e,
- 0xcd, 0x83, 0xee, 0xdf, 0xd0, 0x64, 0x70, 0x61, 0x67, 0x65, 0xf3, 0xe0,
- 0x47, 0x45, 0xe3, 0x07, 0x07, 0x60, 0x5e, 0x72, 0xc1, 0x6a, 0x75, 0x62,
- 0x65, 0xad, 0xe0, 0x35, 0x71, 0xec, 0x02, 0x85, 0x73, 0x74, 0x61, 0x67,
- 0x65, 0xae, 0xe0, 0x25, 0xfe, 0xe1, 0x17, 0x06, 0x10, 0x0b, 0x12, 0x08,
- 0x0b, 0x05, 0x04, 0x40, 0x42, 0x0e, 0x06, 0x06, 0x06, 0x07, 0x0d, 0x0b,
- 0x60, 0x5a, 0x08, 0xc4, 0xe0, 0xfa, 0x60, 0x47, 0xc5, 0xd6, 0x88, 0xf7,
- 0x05, 0x04, 0xe0, 0x5f, 0xa8, 0xf9, 0xe0, 0x4c, 0xf0, 0xae, 0x60, 0x5c,
- 0x04, 0xc2, 0xb7, 0x76, 0xe1, 0x04, 0xe0, 0x51, 0x44, 0x67, 0xe9, 0xe0,
- 0x5c, 0x16, 0xf4, 0x05, 0x05, 0xe0, 0x5f, 0x8c, 0x72, 0xef, 0xe0, 0x5d,
- 0xf4, 0x69, 0xee, 0x60, 0x59, 0xe8, 0xc4, 0xb8, 0xf3, 0x03, 0xc0, 0x9b,
- 0xe1, 0xe0, 0x56, 0x4d, 0xf2, 0x07, 0x60, 0x25, 0xc2, 0xe0, 0x33, 0x7c,
- 0xf3, 0xdd, 0xec, 0x71, 0xf5, 0xe0, 0x56, 0x2f, 0xf0, 0xe0, 0x4e, 0x4f,
- 0xee, 0x08, 0x04, 0x04, 0x1f, 0x0e, 0xe0, 0x58, 0x97, 0xf8, 0xe0, 0x58,
- 0xb8, 0xe7, 0xe0, 0x4e, 0x50, 0xe4, 0x08, 0x06, 0x06, 0x60, 0x58, 0xae,
- 0xc6, 0x95, 0x72, 0x6f, 0xf6, 0xe0, 0x4c, 0x91, 0x69, 0x6e, 0x67, 0xae,
- 0xd8, 0x19, 0x2d, 0x34, 0x2d, 0x73, 0x61, 0x6c, 0xe5, 0xe0, 0x22, 0x9e,
- 0xe3, 0x04, 0xe0, 0x54, 0x41, 0x61, 0xf3, 0x04, 0xe0, 0x4c, 0x72, 0xe8,
- 0xda, 0x0e, 0x62, 0xe9, 0xc3, 0xe4, 0xed, 0x04, 0xe0, 0x4c, 0x67, 0x62,
- 0x6f, 0x72, 0x67, 0x68, 0xe9, 0xe0, 0x42, 0x7e, 0x6b, 0x61, 0xf3, 0xe0,
- 0x5e, 0xac, 0x6a, 0x6f, 0xec, 0xe0, 0x2a, 0x00, 0x68, 0x70, 0xf0, 0xe0,
- 0x58, 0x34, 0x63, 0x61, 0x69, 0xf8, 0xe0, 0x5d, 0xfd, 0xe2, 0x02, 0x87,
- 0xef, 0x60, 0x36, 0xe4, 0xe0, 0x21, 0x95, 0xae, 0xc4, 0x64, 0x61, 0x6b,
- 0x65, 0x73, 0x76, 0x75, 0x65, 0xed, 0xe0, 0x5b, 0xe2, 0x2d, 0x73, 0x70,
- 0xe5, 0xe0, 0x22, 0x06, 0x2d, 0x6f, 0x2d, 0x67, 0x2d, 0x69, 0xad, 0xe0,
- 0x34, 0x99, 0xeb, 0x22, 0x12, 0x3a, 0x0a, 0x36, 0x41, 0x4d, 0x09, 0x04,
- 0x40, 0x7a, 0x0d, 0x41, 0x8c, 0x2e, 0x0c, 0x1b, 0x41, 0x8c, 0x3f, 0x06,
- 0x04, 0x40, 0x4e, 0x05, 0x43, 0xbd, 0x1b, 0x60, 0x29, 0x0d, 0xe0, 0x29,
- 0x7c, 0x1f, 0xc3, 0x04, 0xe0, 0x2a, 0xdf, 0x61, 0x72, 0x1f, 0x43, 0x61,
- 0x1f, 0x45, 0x61, 0x6a, 0xef, 0xc8, 0x51, 0xf9, 0x09, 0x07, 0x60, 0x43,
- 0xa1, 0x56, 0x10, 0xc4, 0xe0, 0x75, 0x72, 0x61, 0xe7, 0xe0, 0x26, 0x6a,
- 0xef, 0x05, 0x08, 0xe0, 0x42, 0x74, 0x77, 0x61, 0xae, 0x60, 0x59, 0x6b,
- 0xc4, 0x05, 0xf4, 0x02, 0x86, 0xef, 0x60, 0x5d, 0x6f, 0xc1, 0x10, 0xe1,
- 0x02, 0x8a, 0xee, 0x02, 0x83, 0xe7, 0xcb, 0x08, 0xe1, 0xe0, 0x51, 0x59,
- 0x6d, 0xe2, 0xe0, 0x26, 0x4f, 0xf7, 0x04, 0xe0, 0x5e, 0x63, 0xf0, 0x43,
- 0xdd, 0xdb, 0xd1, 0xf6, 0x06, 0x07, 0x18, 0xe0, 0x43, 0x55, 0x1f, 0x43,
- 0xe6, 0x24, 0xe0, 0x4f, 0xb0, 0xe9, 0x02, 0x8d, 0xf4, 0x02, 0x86, 0xf3,
- 0x60, 0x53, 0x4b, 0xc3, 0x71, 0x65, 0xf3, 0xdc, 0x7c, 0xee, 0x04, 0xe0,
- 0x25, 0x11, 0xee, 0xc1, 0x97, 0xe1, 0x07, 0x04, 0x60, 0x4f, 0xac, 0xc3,
- 0x86, 0xee, 0xe0, 0x4f, 0xd6, 0xec, 0xe0, 0x48, 0x85, 0xf5, 0x13, 0x05,
- 0x05, 0x0b, 0x24, 0x40, 0x4a, 0x08, 0x40, 0x47, 0x3a, 0x09, 0x0e, 0x0d,
- 0x4b, 0xd4, 0xe0, 0x46, 0x6c, 0x7a, 0x75, 0xed, 0xdc, 0xa6, 0x77, 0x61,
- 0xee, 0xc8, 0xce, 0xf4, 0x04, 0xe0, 0x21, 0xfd, 0x63, 0x68, 0xe1, 0xe0,
- 0x2e, 0x70, 0xf3, 0x06, 0x08, 0x0b, 0xe0, 0x4c, 0xfe, 0x74, 0x61, 0x6e,
- 0x61, 0xe9, 0xe0, 0x5b, 0x00, 0x68, 0xe9, 0x04, 0xe0, 0x52, 0xff, 0xed,
- 0x55, 0xd7, 0xc6, 0x19, 0x61, 0x74, 0x73, 0x75, 0xae, 0x60, 0x56, 0x31,
- 0xc3, 0x22, 0xf2, 0x06, 0x03, 0x23, 0x06, 0x04, 0x84, 0x75, 0xed, 0xb6,
- 0xef, 0x07, 0x03, 0x07, 0x09, 0x03, 0xcb, 0x34, 0xf4, 0xc7, 0xb4, 0x6d,
- 0x61, 0x74, 0x73, 0xf5, 0xd6, 0xd3, 0x69, 0xf3, 0x04, 0xe0, 0x4d, 0xb9,
- 0xe8, 0xdb, 0xc6, 0xe7, 0xd2, 0xfb, 0x62, 0xe5, 0xe0, 0x25, 0x79, 0x69,
- 0x79, 0xe1, 0xe0, 0x51, 0xc5, 0xe7, 0xe0, 0x50, 0x7a, 0xe5, 0xe0, 0x53,
- 0x00, 0xe1, 0x02, 0x85, 0x74, 0xe5, 0xe0, 0x46, 0xf9, 0x73, 0x68, 0x69,
- 0xeb, 0xe0, 0x52, 0xfd, 0x6f, 0x6b, 0x67, 0x72, 0xef, 0xe0, 0x24, 0x14,
- 0xee, 0x05, 0x14, 0x03, 0x07, 0x9c, 0x73, 0xf4, 0x05, 0x04, 0xe0, 0x5b,
- 0x4e, 0xf5, 0xe0, 0x53, 0x7e, 0x73, 0x61, 0x6d, 0x6d, 0x6c, 0xf5, 0xe0,
- 0x47, 0x35, 0xef, 0xd4, 0x9c, 0x6e, 0x65, 0x70, 0xf0, 0xe0, 0x59, 0x81,
- 0xe9, 0x04, 0x0b, 0x05, 0x84, 0xf4, 0x02, 0x83, 0xef, 0xc5, 0xa6, 0x61,
- 0xe3, 0xe0, 0x5a, 0xab, 0x73, 0x61, 0xeb, 0xd8, 0xdb, 0xed, 0xe0, 0x4e,
- 0x9f, 0xe7, 0xdd, 0x89, 0x64, 0x65, 0x6e, 0xae, 0xe0, 0x2c, 0x87, 0xed,
- 0x03, 0x04, 0x8e, 0xe9, 0xe0, 0x25, 0x1e, 0xe5, 0x02, 0x85, 0x6e, 0xe1,
- 0xe0, 0x4a, 0xc9, 0x6a, 0x69, 0xed, 0xe0, 0x22, 0x1d, 0xe1, 0x06, 0x05,
- 0x07, 0x0a, 0xda, 0xf2, 0x74, 0x6f, 0xf2, 0xd8, 0xfd, 0x6e, 0x6f, 0xae,
- 0x60, 0x52, 0x7b, 0xb5, 0x6d, 0x6f, 0x74, 0x6f, 0xae, 0x60, 0x56, 0xa7,
- 0xc5, 0x5e, 0x6b, 0x6f, 0x67, 0xe5, 0xe0, 0x59, 0x09, 0x6c, 0x65, 0x75,
- 0x76, 0x65, 0xee, 0xe0, 0x45, 0x93, 0xea, 0x04, 0xe0, 0x25, 0x76, 0xf5,
- 0x04, 0xe0, 0x4c, 0x0f, 0x6b, 0xf5, 0xc5, 0xca, 0xe4, 0x02, 0x83, 0xef,
- 0xc2, 0xd6, 0x61, 0x6d, 0x61, 0x74, 0xf3, 0xd6, 0xe7, 0x63, 0x68, 0x69,
- 0x6e, 0x6f, 0xf4, 0xe0, 0x23, 0xf4, 0x74, 0x69, 0x73, 0x74, 0x6f, 0xf2,
- 0xe0, 0x3e, 0xf2, 0x73, 0xae, 0xd2, 0x4c, 0xf2, 0x0c, 0x09, 0x05, 0x16,
- 0x0c, 0x0a, 0x29, 0x60, 0x5a, 0xa9, 0xc1, 0xb5, 0x1f, 0xc3, 0x02, 0x82,
- 0xf8, 0x97, 0xe5, 0xc0, 0x55, 0x79, 0xed, 0xe0, 0x41, 0xd3, 0xef, 0x02,
- 0x8b, 0x6b, 0x73, 0x74, 0x61, 0x64, 0x65, 0x6c, 0xf6, 0xe0, 0x57, 0x77,
- 0x64, 0x73, 0x68, 0x65, 0xf2, 0xe0, 0x28, 0xf6, 0x69, 0x73, 0x74, 0x69,
- 0x61, 0x6e, 0xf3, 0x60, 0x50, 0x6f, 0xc6, 0x7b, 0xe5, 0x04, 0xe0, 0x5a,
- 0xd7, 0x6c, 0xec, 0xe0, 0x36, 0x50, 0xe1, 0x04, 0x0f, 0x06, 0x88, 0x73,
- 0xee, 0x02, 0x87, 0x6f, 0x64, 0x61, 0xf2, 0xe0, 0x5b, 0x29, 0xe9, 0xe0,
- 0x22, 0x6c, 0x6b, 0x6f, 0xf7, 0xe0, 0x4b, 0xc7, 0x67, 0x65, 0xf2, 0x60,
- 0x48, 0x21, 0xcd, 0x13, 0x61, 0x6e, 0x67, 0xe8, 0xe0, 0x2a, 0x79, 0xae,
- 0x60, 0x41, 0x7b, 0x55, 0x7c, 0x43, 0xfd, 0xc1, 0x62, 0xf0, 0x08, 0x41,
- 0xc5, 0x60, 0x58, 0xe8, 0xc1, 0x9e, 0xed, 0xe0, 0x5c, 0x49, 0xef, 0x13,
- 0x11, 0x04, 0x1a, 0x18, 0x25, 0x0d, 0x06, 0x0f, 0x33, 0x40, 0x4e, 0x09,
- 0x13, 0x15, 0x05, 0x10, 0x05, 0x89, 0xfa, 0x02, 0x84, 0xef, 0xe0, 0x5c,
- 0x26, 0xe1, 0x05, 0x60, 0x52, 0x6b, 0x84, 0xeb, 0xe0, 0x40, 0x13, 0xf9,
- 0xe0, 0x52, 0x69, 0xf5, 0x04, 0x05, 0x05, 0x86, 0x7a, 0xf5, 0xe0, 0x56,
- 0xff, 0x79, 0x61, 0xed, 0xc5, 0xeb, 0x6e, 0x6f, 0xf3, 0xe0, 0x40, 0xeb,
- 0x68, 0x6f, 0xeb, 0xc5, 0xa2, 0xf4, 0x04, 0xe0, 0x40, 0x2b, 0xef, 0x03,
- 0x03, 0x86, 0xf5, 0xc4, 0x76, 0x68, 0x69, 0xf2, 0xe0, 0x4e, 0xd9, 0xae,
- 0x60, 0x57, 0x64, 0xc1, 0xe3, 0xf3, 0x04, 0x06, 0x11, 0x83, 0x75, 0x67,
- 0xe5, 0xe0, 0x32, 0x75, 0xe8, 0x06, 0x40, 0xe8, 0xe0, 0x48, 0x37, 0xe9,
- 0x03, 0xd9, 0xb9, 0x6d, 0x69, 0xfa, 0xe0, 0x57, 0xec, 0xe5, 0xd7, 0xbd,
- 0xe1, 0x46, 0x29, 0xe0, 0x4c, 0x45, 0xf2, 0x02, 0x83, 0xf9, 0xd7, 0x70,
- 0x69, 0x79, 0x61, 0xed, 0xe0, 0x51, 0x2d, 0x70, 0x65, 0xf2, 0xe0, 0x22,
- 0x04, 0xef, 0x02, 0x84, 0xf2, 0xe0, 0x4d, 0x00, 0x62, 0x69, 0x6e, 0x2e,
- 0xe5, 0xe0, 0x3c, 0x66, 0xee, 0x07, 0x08, 0x11, 0x0b, 0xe0, 0x4a, 0xe0,
- 0x79, 0x76, 0x65, 0x6c, 0xef, 0xe0, 0x5b, 0x2e, 0xf3, 0x02, 0x86, 0x75,
- 0x6c, 0x61, 0xf4, 0xdc, 0xda, 0x6b, 0x6f, 0x77, 0x6f, 0xec, 0xe0, 0x4d,
- 0xc3, 0x67, 0xf3, 0x04, 0xe0, 0x2d, 0xd7, 0x76, 0xe9, 0xe0, 0x46, 0x1f,
- 0x61, 0x6e, 0xae, 0x60, 0x56, 0xe9, 0x89, 0xed, 0x05, 0x06, 0x0b, 0x13,
- 0x87, 0x76, 0x75, 0xf8, 0xe0, 0x4b, 0x03, 0xef, 0x02, 0x84, 0xf2, 0xe0,
- 0x4e, 0x25, 0x6e, 0xef, 0xd0, 0xc1, 0x6d, 0x75, 0xee, 0x04, 0xe0, 0x5a,
- 0x5f, 0x61, 0x6c, 0x66, 0x6f, 0x72, 0x62, 0x75, 0x6e, 0xe4, 0xe0, 0x4a,
- 0xe5, 0x66, 0x6f, 0x72, 0xe2, 0xe0, 0x4a, 0xde, 0xe1, 0x06, 0x0f, 0x4b,
- 0x30, 0xcc, 0x18, 0x74, 0x73, 0xf5, 0x06, 0x60, 0x54, 0x1c, 0xc7, 0x15,
- 0x73, 0x68, 0x69, 0xed, 0xd2, 0x7e, 0x67, 0x61, 0x6e, 0xe5, 0xe0, 0x58,
- 0xa0, 0x6c, 0x6f, 0x62, 0x72, 0x7a, 0xe5, 0xe0, 0x2d, 0x3e, 0xeb, 0x03,
- 0x06, 0x85, 0x75, 0x62, 0x75, 0xee, 0xda, 0x80, 0x6f, 0x6e, 0xef, 0xc5,
- 0x4a, 0xe1, 0xe0, 0x56, 0x7d, 0xe7, 0x02, 0x84, 0xe5, 0xe0, 0x3f, 0x18,
- 0xe1, 0x02, 0x85, 0x6e, 0xe5, 0xe0, 0x58, 0x50, 0xae, 0x60, 0x50, 0x1c,
- 0xc4, 0x0e, 0x66, 0xf5, 0xe0, 0x31, 0x82, 0xe5, 0x02, 0x84, 0xec, 0xe0,
- 0x49, 0xda, 0x62, 0x65, 0x6e, 0x68, 0x61, 0xf6, 0xe0, 0x50, 0xeb, 0x64,
- 0x61, 0xe9, 0xd9, 0x7b, 0x63, 0x68, 0x69, 0xae, 0x60, 0x55, 0xcb, 0xc3,
- 0xfb, 0xe2, 0x03, 0x07, 0x83, 0x69, 0x65, 0x72, 0x7a, 0xf9, 0xdd, 0xe3,
- 0xe5, 0xc0, 0xdc, 0x61, 0x79, 0x61, 0x73, 0xe8, 0xc3, 0x08, 0xee, 0x06,
- 0x09, 0x0b, 0xe0, 0x5a, 0xa0, 0x78, 0x2d, 0x73, 0x65, 0x72, 0xf6, 0xe0,
- 0x3c, 0x28, 0x6f, 0x77, 0x73, 0x69, 0x74, 0x61, 0x6c, 0xec, 0xe0, 0x4b,
- 0x88, 0x69, 0x67, 0x68, 0x74, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x2e, 0x73,
- 0x79, 0x73, 0x74, 0x65, 0xed, 0xe0, 0x5a, 0x48, 0xed, 0x06, 0x60, 0x3f,
- 0xa3, 0xda, 0xe3, 0x70, 0x73, 0xf0, 0xdb, 0xcf, 0xec, 0x04, 0x06, 0x06,
- 0x86, 0x1f, 0x43, 0xe6, 0xe0, 0x53, 0x3b, 0x6f, 0x64, 0x7a, 0xeb, 0xde,
- 0x6b, 0x65, 0x70, 0xf0, 0xe0, 0x59, 0x79, 0xe1, 0xe0, 0x53, 0x2b, 0xe9,
- 0x16, 0x05, 0x15, 0x0b, 0x40, 0x90, 0x27, 0x17, 0x1c, 0x12, 0x0d, 0x18,
- 0x04, 0x0a, 0x04, 0x0c, 0x0a, 0x60, 0x57, 0xdb, 0xc1, 0x06, 0x7a, 0xf5,
- 0xe0, 0x4d, 0x38, 0xf9, 0x03, 0xd9, 0x14, 0xef, 0x04, 0xe0, 0x56, 0x48,
- 0xf3, 0x06, 0x56, 0x57, 0xe0, 0x28, 0x17, 0x61, 0xf4, 0xe0, 0x4f, 0x4e,
- 0xf7, 0x03, 0xc4, 0xf3, 0xe9, 0x60, 0x20, 0x0a, 0xe0, 0x3a, 0x26, 0xf4,
- 0x02, 0x84, 0x63, 0xe8, 0xdb, 0x88, 0xe1, 0x0b, 0x06, 0x04, 0x0a, 0x0b,
- 0x08, 0x17, 0x10, 0x14, 0x08, 0x8b, 0x79, 0x61, 0xed, 0xe0, 0x50, 0x5c,
- 0x75, 0xf2, 0xd1, 0xfd, 0x73, 0x68, 0x69, 0x6f, 0x62, 0x61, 0xf2, 0xe0,
- 0x4f, 0x76, 0x6e, 0x61, 0x6b, 0x61, 0x67, 0x75, 0x73, 0x75, 0xeb, 0xd9,
- 0x45, 0xed, 0x04, 0xe0, 0x58, 0xd9, 0xef, 0xc4, 0x4b, 0xeb, 0x02, 0x88,
- 0x79, 0x75, 0x73, 0x68, 0x75, 0xae, 0xc2, 0x07, 0xe1, 0x03, 0xd5, 0x1b,
- 0x74, 0x61, 0xae, 0x60, 0x4c, 0xcf, 0xc8, 0x68, 0xe8, 0x02, 0x89, 0x69,
- 0x72, 0x6f, 0x73, 0x68, 0xe9, 0xe0, 0x4d, 0xea, 0xe1, 0xe0, 0x50, 0x0a,
- 0x67, 0xe1, 0x02, 0x88, 0x77, 0x61, 0xae, 0x60, 0x4c, 0xb3, 0xc8, 0x05,
- 0x74, 0x61, 0xae, 0x60, 0x50, 0x0c, 0xc1, 0xe9, 0x64, 0x61, 0x69, 0x74,
- 0xef, 0xe0, 0x55, 0xe1, 0xe1, 0x02, 0x84, 0x6b, 0xe9, 0xd9, 0x10, 0xe9,
- 0xe0, 0x51, 0xac, 0xae, 0x60, 0x4c, 0x8d, 0x4a, 0x6a, 0xc1, 0x70, 0xf3,
- 0x03, 0x14, 0x88, 0xef, 0x05, 0x06, 0xe0, 0x57, 0x03, 0x73, 0x61, 0x6b,
- 0xe9, 0xce, 0xf0, 0x66, 0x75, 0x6b, 0x75, 0x73, 0xe8, 0xd0, 0x16, 0x68,
- 0x69, 0x77, 0x61, 0xe4, 0xe0, 0x3e, 0x48, 0x61, 0x72, 0x61, 0xfa, 0xe0,
- 0x20, 0x88, 0xf2, 0x07, 0x05, 0x60, 0x3d, 0x92, 0xc9, 0xe3, 0x79, 0xf5,
- 0xe0, 0x51, 0xb9, 0x6f, 0x76, 0x6f, 0x67, 0x72, 0x61, 0xe4, 0xe0, 0x3e,
- 0x7c, 0xee, 0x07, 0x05, 0x05, 0x04, 0xe0, 0x55, 0x77, 0x6f, 0xeb, 0xe0,
- 0x4f, 0x98, 0x6b, 0xef, 0xe0, 0x53, 0xf9, 0xe7, 0xe0, 0x4d, 0x86, 0xe4,
- 0x60, 0x46, 0x8a, 0xd1, 0x61, 0xed, 0x05, 0x04, 0xe0, 0x59, 0x36, 0xef,
- 0xe0, 0x55, 0x4e, 0xe9, 0x04, 0xe0, 0x20, 0x41, 0xee, 0xdc, 0xca, 0xec,
- 0x06, 0x60, 0x34, 0xad, 0xcc, 0x21, 0x61, 0x74, 0xe9, 0xe0, 0x29, 0x6e,
- 0xeb, 0x03, 0x0a, 0x83, 0xf5, 0x03, 0xc4, 0x1e, 0x63, 0x68, 0xe9, 0xe0,
- 0x52, 0xa8, 0xef, 0xd2, 0x24, 0x69, 0x72, 0x61, 0xf2, 0xe0, 0x4a, 0x7a,
- 0xea, 0xe0, 0x2d, 0x51, 0x68, 0xef, 0x03, 0xce, 0x64, 0x6b, 0xf5, 0xe0,
- 0x54, 0xfd, 0xe5, 0xe0, 0x3e, 0x0e, 0x64, 0xf3, 0x04, 0xe0, 0x58, 0xf1,
- 0xae, 0x60, 0x54, 0xba, 0xc2, 0x0d, 0x63, 0x6b, 0x73, 0x2d, 0x61, 0x73,
- 0xf3, 0xe0, 0x2a, 0x5c, 0x62, 0x69, 0x63, 0x68, 0xf5, 0xc4, 0x3c, 0xe8,
- 0x09, 0x07, 0x16, 0x08, 0x60, 0x3d, 0xc8, 0xca, 0x81, 0x70, 0x6c, 0x61,
- 0xf9, 0xe0, 0x3c, 0x13, 0x6d, 0x65, 0x6c, 0xee, 0x02, 0x88, 0x79, 0x74,
- 0x73, 0x6b, 0xf9, 0xe0, 0x3d, 0xcb, 0x69, 0x74, 0x73, 0x6b, 0xe9, 0xe0,
- 0x3d, 0xd2, 0x65, 0x72, 0x73, 0x6f, 0xee, 0xe0, 0x3d, 0xcb, 0xe1, 0x02,
- 0x86, 0x72, 0xeb, 0x60, 0x3d, 0xb6, 0x83, 0x6b, 0x61, 0x73, 0xf3, 0xe0,
- 0x57, 0x4b, 0xe7, 0x60, 0x42, 0x1b, 0xd6, 0x7f, 0xe6, 0xe0, 0x57, 0x89,
- 0xe5, 0x0c, 0x0a, 0x05, 0x20, 0x06, 0x07, 0x60, 0x34, 0x8c, 0xe0, 0x23,
- 0xbc, 0x79, 0x6d, 0x61, 0x63, 0x68, 0x69, 0xee, 0xe0, 0x2f, 0x87, 0x74,
- 0xf2, 0xe0, 0x3b, 0xef, 0x72, 0x72, 0xf9, 0x03, 0x0b, 0x88, 0x70, 0x72,
- 0x6f, 0x70, 0x65, 0x72, 0x74, 0xe9, 0xe0, 0x4d, 0x0d, 0x6c, 0x6f, 0x67,
- 0x69, 0xf3, 0xe0, 0x51, 0x54, 0x68, 0x6f, 0x74, 0xe5, 0xe0, 0x46, 0x2a,
- 0xf0, 0x5c, 0x4c, 0xe0, 0x2b, 0xd9, 0x6d, 0x62, 0x75, 0xe3, 0xe0, 0x53,
- 0x20, 0x69, 0xf3, 0xe0, 0x41, 0xa2, 0x64, 0xe4, 0xe0, 0x56, 0xc1, 0xe1,
- 0x1a, 0x19, 0x04, 0x40, 0xa3, 0x0f, 0x40, 0x41, 0x40, 0x64, 0x40, 0x6a,
- 0x06, 0x40, 0x55, 0x40, 0xc5, 0x14, 0x26, 0x1f, 0x0e, 0x28, 0x10, 0xe0,
- 0x46, 0x06, 0xfa, 0x05, 0x05, 0xe0, 0x20, 0x4f, 0x75, 0xee, 0xe0, 0x20,
- 0x40, 0x69, 0x6d, 0x69, 0x65, 0x72, 0x7a, 0x2d, 0x64, 0x6f, 0x6c, 0xee,
- 0xe0, 0x46, 0xf0, 0x79, 0xe1, 0xd1, 0x14, 0x77, 0xe1, 0x0e, 0x03, 0x05,
- 0x06, 0x0d, 0x03, 0x1c, 0x10, 0x12, 0x04, 0x10, 0x07, 0x10, 0x88, 0xfa,
- 0xd6, 0xd2, 0x75, 0xe5, 0xe0, 0x50, 0x2b, 0x74, 0x61, 0xee, 0xe0, 0x3c,
- 0xa3, 0x73, 0x61, 0x6b, 0x69, 0xae, 0x03, 0xdf, 0x1e, 0x6a, 0xf0, 0xe0,
- 0x47, 0x7c, 0xf2, 0xd7, 0x04, 0xee, 0x03, 0x0c, 0x87, 0x69, 0x73, 0x68,
- 0x69, 0xae, 0x60, 0x4d, 0x58, 0x43, 0x33, 0xc3, 0x3a, 0x65, 0x68, 0x6f,
- 0xee, 0xe0, 0x4e, 0x67, 0x61, 0xe2, 0xe0, 0x52, 0x6a, 0xed, 0x02, 0x88,
- 0x69, 0x6e, 0x61, 0x6d, 0xe9, 0xe0, 0x4a, 0xa4, 0x61, 0xf4, 0xe0, 0x4d,
- 0x1f, 0xeb, 0x02, 0x84, 0x69, 0xf4, 0xc1, 0xb9, 0x61, 0x6d, 0x69, 0x2e,
- 0x6e, 0xe1, 0x60, 0x50, 0x5d, 0xc4, 0xbe, 0x6a, 0xe9, 0xd5, 0x78, 0xe9,
- 0x02, 0x86, 0x69, 0x73, 0xe8, 0xe0, 0x49, 0xae, 0xae, 0x60, 0x20, 0x0a,
- 0xe0, 0x30, 0x3e, 0x68, 0x61, 0x72, 0xe1, 0xe0, 0x3b, 0x96, 0xe7, 0x02,
- 0x85, 0x75, 0x63, 0xe8, 0xc6, 0x6a, 0x6f, 0x65, 0xae, 0x60, 0x4d, 0x07,
- 0xc1, 0x09, 0x63, 0x68, 0x69, 0x6e, 0x61, 0xe7, 0xc6, 0x26, 0xe2, 0xe0,
- 0x4f, 0xb5, 0xf5, 0x02, 0x89, 0x74, 0x6f, 0x6b, 0x65, 0x69, 0xee, 0xe0,
- 0x50, 0x24, 0xe6, 0xd8, 0xb9, 0xf4, 0x03, 0x21, 0x8a, 0x73, 0xf5, 0x04,
- 0x07, 0x03, 0x87, 0x79, 0x61, 0x6d, 0xe1, 0xe0, 0x2b, 0x76, 0xf5, 0xce,
- 0x8f, 0x73, 0x68, 0x69, 0xeb, 0xe0, 0x52, 0x28, 0x72, 0x61, 0x67, 0x69,
- 0xae, 0x60, 0x4d, 0x7e, 0xc2, 0x72, 0xef, 0x02, 0x83, 0xf7, 0xda, 0x46,
- 0xf2, 0xe0, 0x3b, 0x16, 0xe1, 0x05, 0x07, 0xe0, 0x55, 0xdb, 0x73, 0x68,
- 0x69, 0xee, 0xe0, 0x4f, 0x6a, 0x67, 0x61, 0xed, 0xc1, 0x91, 0xf3, 0x07,
- 0x06, 0x1e, 0x1f, 0xe0, 0x31, 0x75, 0x7a, 0x75, 0xe2, 0xe0, 0x45, 0xeb,
- 0xf5, 0x04, 0x03, 0x08, 0x87, 0xf9, 0xd6, 0x29, 0x6d, 0x69, 0x67, 0x61,
- 0x75, 0xf2, 0xd1, 0x99, 0x6b, 0x61, 0x62, 0xe5, 0xe0, 0x4c, 0x64, 0x67,
- 0xe1, 0x60, 0x4c, 0x0c, 0xc6, 0x5a, 0x68, 0xe9, 0x04, 0x09, 0x08, 0x85,
- 0x77, 0xe1, 0x45, 0x89, 0x59, 0xae, 0xe0, 0x36, 0x64, 0x6d, 0x61, 0xae,
- 0x60, 0x4d, 0x27, 0xc2, 0xe0, 0x68, 0x61, 0xf2, 0xcd, 0x3e, 0xe2, 0xcd,
- 0x3b, 0xe1, 0x07, 0x05, 0x41, 0xca, 0xe0, 0x50, 0xe5, 0x6f, 0xeb, 0xe0,
- 0x51, 0xc6, 0x6d, 0xe1, 0x04, 0xe0, 0x4f, 0xe6, 0x74, 0x73, 0xf5, 0xe0,
- 0x4e, 0xec, 0xf2, 0x08, 0x0d, 0x06, 0x05, 0x06, 0x05, 0x0f, 0x85, 0xf5,
- 0x02, 0x83, 0xed, 0xdf, 0x1d, 0x69, 0x7a, 0x61, 0xf7, 0xe0, 0x3b, 0x40,
- 0x74, 0x75, 0xfa, 0xe0, 0x45, 0x79, 0x70, 0xe1, 0xe0, 0x3f, 0xd7, 0xed,
- 0x60, 0x4b, 0x8f, 0xc3, 0x71, 0x6c, 0xf3, 0xe0, 0x4b, 0x88, 0xe9, 0x04,
- 0x04, 0xc5, 0xda, 0xf9, 0xe0, 0x3a, 0xa9, 0x6b, 0x61, 0xf4, 0xe0, 0x2e,
- 0x5f, 0x65, 0xec, 0xe0, 0x55, 0x1f, 0xe1, 0x04, 0x0a, 0x10, 0x87, 0xf4,
- 0x04, 0xe0, 0x52, 0xbe, 0x73, 0xf5, 0xe0, 0x4c, 0xb4, 0xf3, 0x02, 0x84,
- 0x75, 0x79, 0xe1, 0xac, 0x6a, 0xef, 0x04, 0xe0, 0x4c, 0xb4, 0xe8, 0xdc,
- 0x94, 0x67, 0x61, 0x6e, 0xe4, 0xe0, 0x54, 0xfa, 0x63, 0xef, 0xc4, 0x80,
- 0x70, 0x73, 0xe9, 0xe0, 0x2f, 0x9a, 0xee, 0x0a, 0x05, 0x05, 0x0e, 0x07,
- 0x05, 0x06, 0x12, 0xc5, 0x8d, 0x7a, 0x61, 0xeb, 0xde, 0x07, 0x75, 0xed,
- 0xe0, 0x4b, 0x59, 0xef, 0x02, 0x85, 0x79, 0xe1, 0xe0, 0x50, 0xd1, 0x6e,
- 0x6a, 0xe9, 0xe0, 0x49, 0x04, 0x6e, 0xe1, 0x60, 0x4a, 0x6b, 0xc3, 0xff,
- 0x6d, 0x61, 0xeb, 0xd3, 0x52, 0xe9, 0x60, 0x4a, 0x93, 0xc3, 0xb6, 0xe5,
- 0x02, 0x8a, 0x79, 0x61, 0x6d, 0x61, 0xae, 0x60, 0x4b, 0x8b, 0xc5, 0xd0,
- 0x67, 0x61, 0xf3, 0xd4, 0x8f, 0xe1, 0x06, 0x54, 0x52, 0xe0, 0x3d, 0xaf,
- 0x7a, 0x61, 0x77, 0xe1, 0xe0, 0x4e, 0xd8, 0xed, 0x05, 0x0e, 0x40, 0x92,
- 0x8d, 0xef, 0x04, 0x03, 0xd4, 0x6a, 0xe5, 0xce, 0xf0, 0xae, 0x60, 0x48,
- 0xc7, 0xc9, 0x9a, 0xe9, 0x0c, 0x0f, 0x16, 0x04, 0x0f, 0x06, 0x20, 0x05,
- 0x0b, 0x06, 0x05, 0x86, 0xf4, 0x02, 0x86, 0x73, 0x75, 0xe5, 0xe0, 0x44,
- 0xdd, 0x6f, 0x6e, 0xe4, 0xe0, 0x4c, 0x05, 0xf3, 0x03, 0x09, 0x85, 0xf5,
- 0x04, 0xe0, 0x4e, 0xe2, 0x6e, 0xe1, 0xdd, 0x9f, 0x68, 0xe9, 0xe0, 0x43,
- 0xe4, 0x61, 0xf4, 0xdd, 0xdd, 0x6f, 0xeb, 0xd5, 0x0b, 0x6e, 0xef, 0x02,
- 0x85, 0x79, 0x61, 0xed, 0xdd, 0x2a, 0x6b, 0x61, 0xf7, 0xe0, 0x4a, 0xc0,
- 0x6d, 0x69, 0xee, 0xe0, 0x4b, 0xde, 0xeb, 0x03, 0x07, 0x87, 0x6f, 0x61,
- 0x6e, 0xe9, 0xe0, 0x30, 0x4e, 0x69, 0x74, 0x61, 0x79, 0xe1, 0xcb, 0xc3,
- 0x61, 0x77, 0x61, 0xae, 0x04, 0xe0, 0x4c, 0x0d, 0xe8, 0x60, 0x51, 0x67,
- 0xc2, 0xf2, 0x6a, 0x69, 0xed, 0xcb, 0xd2, 0xe9, 0x02, 0x85, 0x7a, 0x75,
- 0xed, 0xc4, 0x51, 0xe3, 0xdc, 0xe8, 0x67, 0x6f, 0xf2, 0xe0, 0x51, 0x4c,
- 0x66, 0x75, 0xf2, 0xdc, 0xa5, 0x61, 0x6d, 0xe1, 0xe0, 0x4e, 0xdb, 0xae,
- 0x5c, 0x87, 0xe0, 0x33, 0xb8, 0xe5, 0x02, 0x86, 0x79, 0x61, 0x6d, 0xe1,
- 0xca, 0xa1, 0x6f, 0xeb, 0xdd, 0x1d, 0xe1, 0x03, 0x04, 0x85, 0x6b, 0xf5,
- 0xc5, 0x8c, 0x69, 0x73, 0xe8, 0xdd, 0xa8, 0x67, 0x61, 0xf9, 0xd3, 0xb8,
- 0xec, 0x03, 0x05, 0x86, 0x75, 0xe7, 0xe0, 0x53, 0xcd, 0x6d, 0x79, 0xeb,
- 0xe0, 0x45, 0x43, 0x69, 0xf3, 0xe0, 0x3e, 0x5b, 0xeb, 0x05, 0x04, 0x04,
- 0x07, 0x86, 0x75, 0xe4, 0xdc, 0x45, 0x6f, 0xe7, 0xca, 0x63, 0x69, 0x6e,
- 0x6f, 0xeb, 0xe0, 0x50, 0x11, 0x65, 0x67, 0x61, 0xf7, 0xdd, 0x55, 0x61,
- 0x6d, 0x69, 0x67, 0x61, 0x68, 0x61, 0xf2, 0xe0, 0x26, 0x85, 0xe9, 0x07,
- 0x05, 0x04, 0x05, 0xe0, 0x2b, 0x66, 0x7a, 0xf5, 0xe0, 0x39, 0xab, 0xf4,
- 0xe0, 0x2f, 0x9e, 0x73, 0xe5, 0xe0, 0x48, 0xf6, 0x6e, 0x61, 0x6e, 0xae,
- 0x60, 0x4b, 0x1b, 0xc5, 0x06, 0x68, 0xef, 0x04, 0xe0, 0x3e, 0x24, 0x6b,
- 0x75, 0xae, 0x60, 0x4a, 0x4c, 0xc3, 0x63, 0xe7, 0x02, 0x8c, 0x6f, 0x73,
- 0x68, 0x69, 0x6d, 0x61, 0xae, 0x60, 0x4f, 0x61, 0xc4, 0x44, 0xe1, 0x06,
- 0x60, 0x4d, 0x93, 0xc3, 0x4c, 0x6d, 0xe9, 0x05, 0x05, 0xe0, 0x4f, 0x91,
- 0x6e, 0xef, 0xe0, 0x4f, 0xa8, 0x69, 0x73, 0xe8, 0xe0, 0x45, 0xe9, 0xe4,
- 0x03, 0xc3, 0x60, 0xef, 0x02, 0x84, 0xed, 0xe0, 0x39, 0x59, 0x67, 0x61,
- 0xf7, 0xcc, 0x76, 0x61, 0xf3, 0xe0, 0x2f, 0xe7, 0x38, 0x73, 0xae, 0x06,
- 0x09, 0x60, 0x20, 0x1c, 0x87, 0x70, 0x6c, 0x2d, 0x77, 0x61, 0xf7, 0xe0,
- 0x20, 0x25, 0x6e, 0x6c, 0x2d, 0x61, 0x6d, 0xf3, 0xe0, 0x20, 0x1c, 0x31,
- 0x32, 0xae, 0x15, 0x07, 0x0c, 0x04, 0x08, 0x04, 0x0a, 0x60, 0x32, 0x3e,
- 0x48, 0x24, 0x04, 0x1c, 0x0a, 0x02, 0x16, 0x06, 0x02, 0x09, 0x8d, 0xf7,
- 0x60, 0x3a, 0xbd, 0x2e, 0xd1, 0xc0, 0xf6, 0x05, 0x60, 0x3a, 0xd9, 0x86,
- 0xe9, 0x60, 0x4f, 0x60, 0xc4, 0xe0, 0xf5, 0xe0, 0x3a, 0xd2, 0xf4, 0x60,
- 0x3a, 0x6d, 0x40, 0x41, 0xd8, 0xd1, 0xf3, 0xe0, 0x3a, 0xbf, 0xee, 0x60,
- 0x3a, 0x7f, 0x03, 0x03, 0x03, 0x12, 0x1e, 0x83, 0xe9, 0x06, 0x60, 0x3a,
- 0x92, 0x03, 0xa3, 0xec, 0x60, 0x4f, 0x39, 0xc4, 0xe0, 0xea, 0x16, 0x09,
- 0x3a, 0x18, 0x1c, 0x40, 0x80, 0x04, 0x06, 0x1e, 0x12, 0x04, 0x40, 0xb4,
- 0x0a, 0x29, 0x23, 0x51, 0xc1, 0xe0, 0x32, 0x36, 0x1f, 0x43, 0xf8, 0x03,
- 0xc0, 0xb9, 0xf2, 0xc0, 0xb5, 0xf5, 0x09, 0x04, 0x06, 0x04, 0x0f, 0x0e,
- 0xe0, 0x3b, 0xb8, 0xf2, 0xe0, 0x52, 0xfc, 0x6e, 0x69, 0xf0, 0xe0, 0x41,
- 0x28, 0xe9, 0xe0, 0x3d, 0x68, 0xe5, 0x02, 0x84, 0xe7, 0xe0, 0x31, 0xd8,
- 0x64, 0x69, 0x73, 0x63, 0xe8, 0xe0, 0x4d, 0x3b, 0xe4, 0x02, 0x87, 0x79,
- 0x67, 0x61, 0xf2, 0xe0, 0x24, 0xf3, 0xe1, 0xe0, 0x28, 0x46, 0x2e, 0xed,
- 0xe0, 0x52, 0xeb, 0x73, 0xae, 0x06, 0x60, 0x4f, 0xd3, 0xc1, 0x9d, 0x77,
- 0x70, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x70, 0x6f, 0x77, 0x65, 0xf2,
- 0xe0, 0x4a, 0xa8, 0xf0, 0x0a, 0x07, 0x60, 0x23, 0xeb, 0x4d, 0x5c, 0xe0,
- 0x22, 0x49, 0x6d, 0x6f, 0x72, 0xe7, 0xe0, 0x50, 0x23, 0xae, 0x60, 0x40,
- 0xc4, 0x47, 0x29, 0x46, 0x3c, 0xc5, 0x43, 0xef, 0x11, 0x04, 0x09, 0x10,
- 0x0d, 0x04, 0x08, 0x06, 0x09, 0x06, 0x07, 0x07, 0x60, 0x49, 0x76, 0xc9,
- 0xa5, 0x7a, 0xe9, 0xde, 0xdc, 0xf9, 0x04, 0xe0, 0x53, 0x6b, 0xef, 0xe0,
- 0x46, 0x55, 0x75, 0x72, 0x6e, 0x61, 0xec, 0x04, 0xe0, 0x51, 0x0e, 0x69,
- 0xf3, 0x60, 0x4a, 0x25, 0xc4, 0xfd, 0xf4, 0x04, 0xe0, 0x53, 0x52, 0x65,
- 0x6c, 0x75, 0x6c, 0xf5, 0xe0, 0x3b, 0xde, 0xf3, 0xe0, 0x44, 0x95, 0xf2,
- 0x04, 0xe0, 0x52, 0x88, 0xf0, 0xcd, 0x9c, 0x6c, 0x73, 0xf4, 0xe0, 0x48,
- 0x44, 0x69, 0x6e, 0x76, 0x69, 0x6c, 0xec, 0xe0, 0x3b, 0x33, 0x68, 0x61,
- 0x6e, 0xe1, 0xda, 0xf3, 0x67, 0x61, 0x73, 0xfa, 0xe0, 0x52, 0xbb, 0x65,
- 0x74, 0x73, 0xf5, 0xe0, 0x4f, 0xa3, 0xe2, 0x03, 0x04, 0x89, 0xf5, 0xe0,
- 0x3f, 0x41, 0xf3, 0x04, 0xe0, 0x53, 0x0e, 0xae, 0xe0, 0x51, 0xe8, 0x6f,
- 0xea, 0xdb, 0x84, 0xee, 0xe0, 0x51, 0x5b, 0xed, 0x60, 0x42, 0x9f, 0xcf,
- 0x66, 0xec, 0x06, 0x60, 0x40, 0x6b, 0xd0, 0xd2, 0x73, 0x2d, 0x73, 0x74,
- 0xef, 0x03, 0x02, 0x82, 0xb3, 0x84, 0xb2, 0x82, 0x31, 0x2e, 0x65, 0x6c,
- 0x61, 0x73, 0x74, 0xf8, 0xe0, 0x52, 0xbd, 0xe9, 0x04, 0xe0, 0x51, 0xef,
- 0x6e, 0x73, 0x65, 0x6b, 0x69, 0x6b, 0x6f, 0x67, 0x65, 0xee, 0xe0, 0x48,
- 0x2c, 0xe6, 0xe0, 0x4b, 0x15, 0xe5, 0x10, 0x0f, 0x07, 0x05, 0x05, 0x09,
- 0x06, 0x40, 0x60, 0x06, 0x04, 0x52, 0x36, 0xe0, 0x3f, 0xe8, 0xf7, 0x02,
- 0x88, 0x69, 0x73, 0xe8, 0x60, 0x47, 0xa4, 0xc8, 0xe3, 0x65, 0xec, 0xd9,
- 0x37, 0x76, 0x6e, 0x61, 0xeb, 0xe0, 0x47, 0xad, 0x74, 0xfa, 0xe0, 0x51,
- 0x7b, 0x73, 0xf3, 0xe0, 0x46, 0x40, 0x72, 0x75, 0x73, 0x61, 0x6c, 0xe5,
- 0xe0, 0x49, 0x5a, 0x6f, 0xee, 0x60, 0x36, 0x3a, 0x85, 0xec, 0x03, 0x09,
- 0x97, 0x6c, 0x79, 0x62, 0x65, 0x61, 0xee, 0xe0, 0x4a, 0xe6, 0xe5, 0x02,
- 0x84, 0xee, 0xe0, 0x44, 0xad, 0xae, 0x08, 0x60, 0x34, 0x65, 0x47, 0x44,
- 0xd6, 0x52, 0x63, 0xec, 0x60, 0x3a, 0xfd, 0xc8, 0x32, 0x61, 0x73, 0x74,
- 0x69, 0x63, 0xae, 0x04, 0x0e, 0x0c, 0x8e, 0xf4, 0x04, 0xe0, 0x29, 0xfc,
- 0x73, 0x75, 0x6b, 0x61, 0x65, 0xf2, 0xe0, 0x27, 0x3f, 0x73, 0x61, 0x76,
- 0x65, 0x69, 0x6e, 0x63, 0x6c, 0x6f, 0xf5, 0xdc, 0xe4, 0x72, 0x65, 0x67,
- 0x72, 0x75, 0x68, 0x6f, 0x73, 0x74, 0x69, 0xee, 0xe0, 0x38, 0xf5, 0x64,
- 0x6f, 0x67, 0x61, 0x64, 0x6f, 0xae, 0xe0, 0x4d, 0xd9, 0x66, 0x66, 0x65,
- 0xf2, 0xd0, 0x9c, 0xe5, 0xe0, 0x51, 0x25, 0x64, 0x2e, 0x77, 0x61, 0x66,
- 0x61, 0xe9, 0xe0, 0x4d, 0x7d, 0xe4, 0x04, 0xe0, 0x23, 0x5b, 0x65, 0xf6,
- 0xe0, 0x4d, 0x73, 0xe3, 0x05, 0x1b, 0xe0, 0x48, 0x95, 0x6c, 0x6f, 0x75,
- 0xe4, 0x02, 0x87, 0xae, 0x02, 0x8d, 0xeb, 0xe0, 0x3f, 0x1b, 0x2d, 0x76,
- 0x65, 0x72, 0x2d, 0x6a, 0x70, 0x63, 0x2e, 0x69, 0xeb, 0xe0, 0x20, 0x1c,
- 0x2e, 0x6e, 0x65, 0x65, 0xee, 0xe0, 0x51, 0x8e, 0xe1, 0x0a, 0x05, 0x0e,
- 0x52, 0x9a, 0x60, 0x29, 0x20, 0xc5, 0xd6, 0x77, 0x6f, 0xf2, 0xd5, 0xca,
- 0xed, 0x04, 0x03, 0xdd, 0xd0, 0xe9, 0xd0, 0x43, 0x62, 0x79, 0xec, 0xe0,
- 0x50, 0x75, 0x67, 0xf5, 0xe0, 0x40, 0x93, 0xae, 0x05, 0x17, 0xe0, 0x51,
- 0x9f, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x66, 0x6f, 0x72, 0x63, 0x65, 0xae,
- 0x04, 0xe0, 0x51, 0x86, 0x63, 0x6f, 0x6d, 0x2e, 0xe3, 0xe0, 0x41, 0x35,
- 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x68, 0x69, 0x66, 0xf4, 0xe0, 0x4e,
- 0xdd, 0xe9, 0x23, 0x40, 0x45, 0x05, 0x40, 0x4b, 0x1d, 0x40, 0x7f, 0x43,
- 0x29, 0x19, 0x22, 0x16, 0x42, 0x8a, 0x40, 0x54, 0x21, 0x3b, 0x27, 0x05,
- 0x12, 0x06, 0x08, 0x40, 0x40, 0x40, 0x54, 0x1c, 0x0d, 0x06, 0xe0, 0x27,
- 0xc4, 0xfa, 0x03, 0x3a, 0x84, 0xf5, 0x05, 0x08, 0xe0, 0x48, 0x01, 0x6e,
- 0x6f, 0x6b, 0x75, 0xee, 0xe0, 0x48, 0x03, 0xed, 0x02, 0x8c, 0xef, 0x04,
- 0xe0, 0x4c, 0x69, 0x7a, 0x61, 0x6b, 0xe9, 0xe0, 0x4d, 0xd7, 0xe9, 0x04,
- 0x06, 0x05, 0x87, 0x7a, 0x61, 0xeb, 0xe0, 0x42, 0x97, 0x73, 0xe1, 0xe0,
- 0x50, 0x05, 0x6f, 0x74, 0x73, 0xf5, 0xe0, 0x50, 0x00, 0xae, 0x60, 0x4b,
- 0xe4, 0xc4, 0x19, 0x65, 0xee, 0xd6, 0x1e, 0xae, 0xda, 0x2c, 0x79, 0xef,
- 0xe0, 0x4d, 0x28, 0xf7, 0x02, 0x83, 0xe9, 0xd6, 0xfc, 0xe1, 0x08, 0x13,
- 0x08, 0x09, 0x0c, 0x07, 0xcc, 0x17, 0xf4, 0x04, 0x07, 0xd9, 0x68, 0x73,
- 0x75, 0x6b, 0xe9, 0xe0, 0x46, 0x81, 0x65, 0xae, 0x59, 0x87, 0xe0, 0x36,
- 0x73, 0xee, 0x04, 0xe0, 0x3f, 0x67, 0xf5, 0xcd, 0x11, 0xed, 0x03, 0xcb,
- 0x9a, 0x69, 0xfa, 0xe0, 0x46, 0x71, 0xeb, 0x04, 0xe0, 0x42, 0x3e, 0xf5,
- 0x03, 0xcc, 0xaf, 0xee, 0xd0, 0x27, 0x66, 0x75, 0x6e, 0xe5, 0xe0, 0x46,
- 0x10, 0x64, 0xe5, 0xe0, 0x47, 0x27, 0xf6, 0x04, 0x04, 0xcb, 0x2e, 0xe7,
- 0xe0, 0x4b, 0x56, 0x61, 0x6e, 0xef, 0x02, 0x85, 0x76, 0xef, 0xe0, 0x4f,
- 0x7a, 0x2d, 0x66, 0x72, 0x61, 0x6e, 0x6b, 0xe9, 0xe0, 0x27, 0xed, 0xf4,
- 0x0c, 0x05, 0x11, 0x05, 0x0c, 0x29, 0x1b, 0x60, 0x4d, 0x38, 0xc3, 0x0e,
- 0x73, 0xae, 0xe0, 0x34, 0x75, 0xef, 0x05, 0x03, 0xe0, 0x47, 0x46, 0xed,
- 0xd5, 0x98, 0x69, 0x67, 0x61, 0x77, 0xe1, 0xe0, 0x4d, 0x23, 0x69, 0xe7,
- 0xe0, 0x38, 0x40, 0x63, 0x6f, 0x75, 0x6c, 0x64, 0x62, 0x65, 0x77, 0x6f,
- 0xf2, 0xc3, 0xbc, 0xe1, 0x08, 0x07, 0x05, 0x04, 0x0a, 0xe0, 0x4f, 0xff,
- 0x79, 0x61, 0x6e, 0x61, 0xe7, 0xce, 0x8a, 0x6e, 0xef, 0xe0, 0x4b, 0xc7,
- 0xed, 0xe0, 0x4c, 0x68, 0xeb, 0x04, 0xe0, 0x41, 0xb8, 0x75, 0xf2, 0xe0,
- 0x48, 0xb9, 0x62, 0x61, 0xf3, 0xe0, 0x4d, 0xb7, 0x31, 0xae, 0x02, 0x8a,
- 0x65, 0x75, 0x72, 0x2e, 0x61, 0x72, 0x75, 0x62, 0x61, 0x2e, 0x6a, 0x65,
- 0x6e, 0x76, 0x2d, 0x61, 0x72, 0x75, 0x62, 0xe1, 0xe0, 0x38, 0xda, 0xae,
- 0x60, 0x24, 0x40, 0xe0, 0x26, 0x9f, 0xf3, 0x0d, 0x05, 0x1d, 0x18, 0x07,
- 0x0e, 0x23, 0x1e, 0x1f, 0x08, 0xe0, 0x4f, 0x7a, 0x75, 0xed, 0xe0, 0x34,
- 0x1d, 0xf4, 0x06, 0x05, 0x0a, 0xe0, 0x50, 0x16, 0x6d, 0xe5, 0xe0, 0x25,
- 0xe1, 0x65, 0x69, 0x6e, 0x67, 0x65, 0x65, 0xeb, 0xe0, 0x2b, 0xbe, 0x61,
- 0x6e, 0x62, 0xf5, 0xe0, 0x4e, 0x5a, 0xf3, 0x02, 0x8f, 0x6d, 0x61, 0x72,
- 0x74, 0x65, 0x72, 0x74, 0x68, 0x61, 0x6e, 0x79, 0xef, 0xe0, 0x4f, 0x67,
- 0x68, 0x69, 0xeb, 0xe0, 0x4b, 0x74, 0x6d, 0x61, 0x69, 0xec, 0xe0, 0x4e,
- 0x73, 0xec, 0x02, 0x87, 0x65, 0x6f, 0x66, 0xed, 0xe0, 0x22, 0x53, 0xe1,
- 0xe0, 0x20, 0x14, 0x68, 0xe9, 0x03, 0x08, 0x90, 0x6e, 0x6f, 0x6d, 0x61,
- 0x6b, 0xe9, 0xd7, 0x14, 0x6b, 0xe1, 0x04, 0xe0, 0x4e, 0xb4, 0x77, 0x61,
- 0xae, 0x60, 0x4b, 0x25, 0x40, 0xd9, 0xc2, 0xbf, 0x67, 0x61, 0xeb, 0xe0,
- 0x4b, 0xf2, 0xe5, 0x06, 0x07, 0x06, 0x04, 0xc5, 0x0b, 0x73, 0x61, 0x6b,
- 0xe9, 0xe0, 0x48, 0x04, 0xf2, 0x60, 0x2b, 0xa7, 0xcc, 0x1d, 0xee, 0xe0,
- 0x4a, 0x56, 0x68, 0x61, 0xf2, 0xe0, 0x4b, 0xae, 0xe1, 0x03, 0x06, 0x86,
- 0x68, 0x61, 0xf9, 0xe0, 0x34, 0x52, 0xae, 0x60, 0x4a, 0x43, 0xc1, 0x1d,
- 0xad, 0x02, 0x89, 0x68, 0x6f, 0x63, 0x6b, 0x65, 0x79, 0xee, 0xd8, 0xaf,
- 0xe7, 0xc1, 0xcb, 0xae, 0x50, 0xce, 0x60, 0x39, 0x4d, 0xc3, 0xfd, 0xad,
- 0x0c, 0x0e, 0x26, 0x06, 0x0e, 0x04, 0x0a, 0x09, 0x21, 0x06, 0x06, 0x84,
- 0x77, 0x69, 0x74, 0x68, 0x2d, 0x74, 0x68, 0x65, 0x62, 0x61, 0xee, 0xe0,
- 0x4a, 0xcc, 0x76, 0x65, 0x72, 0x79, 0xad, 0x05, 0x06, 0x06, 0x05, 0x87,
- 0x73, 0x77, 0xe5, 0xe0, 0x20, 0xbe, 0x6e, 0x69, 0xe3, 0xe0, 0x3a, 0xee,
- 0x67, 0x6f, 0xef, 0xdd, 0x34, 0x65, 0x76, 0x69, 0xec, 0xe0, 0x4c, 0xfd,
- 0x62, 0xe1, 0xdd, 0x29, 0x75, 0x62, 0x65, 0x72, 0xec, 0xa1, 0xf3, 0x02,
- 0x86, 0x6c, 0x69, 0xe3, 0xe0, 0x47, 0x04, 0x61, 0x76, 0xe5, 0xdd, 0x15,
- 0x6e, 0x6f, 0x74, 0x2d, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0xe9, 0xe0,
- 0x46, 0x1d, 0xec, 0x03, 0xd5, 0x7f, 0x65, 0xe5, 0xe0, 0x46, 0x0d, 0x69,
- 0x6e, 0x74, 0x6f, 0xad, 0x03, 0x06, 0x8d, 0x67, 0x61, 0xed, 0xe0, 0x4e,
- 0x7f, 0x63, 0x61, 0xf2, 0x04, 0xe0, 0x4e, 0x76, 0x74, 0x6f, 0xef, 0xe0,
- 0x25, 0x50, 0x61, 0x6e, 0xe9, 0xe0, 0x24, 0x51, 0x67, 0x6f, 0xee, 0xe0,
- 0x48, 0x0c, 0x66, 0x6f, 0x75, 0xee, 0xdc, 0xd1, 0x62, 0xf9, 0xd2, 0x41,
- 0xe1, 0x02, 0xb5, 0x6e, 0xad, 0x02, 0x91, 0x65, 0xee, 0x02, 0x86, 0x74,
- 0x65, 0x72, 0xf4, 0xc0, 0x99, 0x67, 0x69, 0x6e, 0xe5, 0xe0, 0x45, 0xfd,
- 0xe1, 0x04, 0x04, 0xc1, 0x9c, 0x72, 0xf4, 0xc1, 0xa3, 0xe3, 0x02, 0x8b,
- 0xf4, 0x04, 0xe0, 0x41, 0xd4, 0x72, 0x65, 0xf3, 0xe0, 0x4e, 0x2e, 0x63,
- 0x6f, 0x75, 0x6e, 0x74, 0xe1, 0xe0, 0x45, 0xa3, 0xad, 0x10, 0x16, 0x16,
- 0x15, 0x2c, 0x10, 0x06, 0x30, 0x08, 0x12, 0x14, 0x11, 0x1a, 0x40, 0x40,
- 0xa7, 0xf4, 0x02, 0x87, 0x68, 0x65, 0x72, 0x61, 0xf0, 0xc1, 0x71, 0xe5,
- 0x02, 0x86, 0x63, 0x68, 0xe9, 0xe0, 0x47, 0xa6, 0x61, 0xe3, 0xbd, 0xf3,
- 0x02, 0x86, 0x74, 0x75, 0xe4, 0xe0, 0x45, 0x72, 0xef, 0x02, 0x84, 0xf8,
- 0xe0, 0x2e, 0x50, 0x63, 0x69, 0x61, 0xec, 0xc1, 0x4f, 0xf2, 0x02, 0x89,
- 0x6f, 0x63, 0x6b, 0x73, 0x74, 0xe1, 0xe0, 0x45, 0x95, 0x65, 0x70, 0x75,
- 0x62, 0x6c, 0x69, 0xe3, 0xc1, 0x18, 0xf0, 0x04, 0x04, 0x0c, 0x8d, 0x6c,
- 0xe1, 0xc0, 0x60, 0x68, 0x6f, 0x74, 0x6f, 0x67, 0x72, 0x61, 0x70, 0xe8,
- 0xe0, 0x45, 0x76, 0x65, 0x72, 0x73, 0x6f, 0x6e, 0x61, 0x6c, 0x74, 0x72,
- 0x61, 0xe9, 0xc0, 0x9f, 0xe1, 0x02, 0x83, 0xf4, 0xc0, 0xf7, 0xe9, 0xe0,
- 0x3e, 0x9c, 0xee, 0x02, 0x86, 0x75, 0x72, 0xf3, 0xe0, 0x47, 0x43, 0x61,
- 0x73, 0x63, 0x61, 0xf2, 0xc0, 0xdb, 0x6d, 0x75, 0x73, 0x69, 0xe3, 0x9b,
- 0xec, 0x04, 0x1c, 0xc5, 0x69, 0xe9, 0x02, 0x8a, 0x6e, 0x75, 0x78, 0x2d,
- 0x75, 0x73, 0xe5, 0xe0, 0x36, 0x25, 0x62, 0x65, 0xf2, 0x02, 0x86, 0x74,
- 0x61, 0x72, 0xe9, 0xc0, 0xb9, 0xe1, 0xe0, 0x48, 0x01, 0xe1, 0x02, 0x85,
- 0x77, 0xf9, 0xe0, 0x45, 0x20, 0x6e, 0x64, 0x73, 0x63, 0xe1, 0xc0, 0xb7,
- 0x6b, 0x6e, 0x69, 0x67, 0xe8, 0xe0, 0x45, 0x2a, 0xe8, 0x02, 0x84, 0xf5,
- 0xe0, 0x3e, 0x47, 0x61, 0x72, 0x64, 0x2d, 0x77, 0x6f, 0x72, 0xeb, 0xe0,
- 0x44, 0xff, 0xe7, 0x03, 0x05, 0x85, 0x75, 0xf2, 0xe0, 0x4d, 0x28, 0x72,
- 0x65, 0xe5, 0xde, 0x16, 0x65, 0x65, 0xeb, 0xe0, 0x3a, 0xdd, 0x66, 0x69,
- 0x6e, 0x61, 0x6e, 0x63, 0x69, 0x61, 0x6c, 0x61, 0x64, 0x76, 0x69, 0xf3,
- 0xe0, 0x40, 0xc5, 0xe4, 0x02, 0x85, 0x6f, 0xe3, 0xe0, 0x40, 0xbc, 0xe5,
- 0x02, 0x87, 0x73, 0x69, 0x67, 0xee, 0xe0, 0x44, 0xc8, 0x6d, 0x6f, 0x63,
- 0x72, 0xe1, 0xe0, 0x44, 0x87, 0xe3, 0x06, 0x0d, 0x03, 0x0b, 0x06, 0x86,
- 0x75, 0x62, 0x69, 0x63, 0x6c, 0x65, 0x2d, 0x73, 0x6c, 0xe1, 0xe0, 0x27,
- 0x85, 0xf0, 0xd7, 0x08, 0x6f, 0x6e, 0x73, 0x65, 0x72, 0x76, 0x61, 0xf4,
- 0xe0, 0x27, 0x76, 0x68, 0x65, 0xe6, 0xe0, 0x3a, 0x8a, 0x65, 0x6c, 0x74,
- 0x69, 0xe3, 0xa5, 0xe1, 0x02, 0x86, 0x74, 0x65, 0xf2, 0xe0, 0x44, 0x89,
- 0x6e, 0x64, 0x69, 0x64, 0x61, 0xf4, 0xe0, 0x38, 0xed, 0xe2, 0x04, 0x09,
- 0x08, 0x8a, 0x75, 0x6c, 0x6c, 0x73, 0x2d, 0x66, 0xe1, 0xdd, 0x97, 0x72,
- 0x75, 0x69, 0x6e, 0xf3, 0xe0, 0x2d, 0x14, 0x6f, 0x6f, 0x6b, 0x6b, 0x65,
- 0x65, 0xf0, 0xe0, 0x44, 0x60, 0x6c, 0x6f, 0x67, 0xe7, 0xe0, 0x44, 0x59,
- 0x61, 0x6e, 0x61, 0x72, 0x63, 0x68, 0x69, 0xf3, 0xe0, 0x44, 0x15, 0xf2,
- 0x08, 0x03, 0x08, 0x60, 0x42, 0xf6, 0xca, 0x0c, 0xf5, 0xca, 0xec, 0x69,
- 0xf3, 0x60, 0x21, 0xa4, 0xe0, 0x2a, 0x59, 0x61, 0xf1, 0xe0, 0x4a, 0xd6,
- 0xf0, 0x03, 0x10, 0x84, 0xe9, 0x02, 0x87, 0x72, 0x61, 0x6e, 0xe7, 0xe0,
- 0x4b, 0xec, 0x66, 0x6f, 0xee, 0xe0, 0x4c, 0x27, 0xb6, 0xe0, 0x21, 0x84,
- 0x2e, 0x6c, 0x69, 0x6e, 0x6f, 0x64, 0xe5, 0xe0, 0x43, 0xd0, 0xef, 0x06,
- 0x08, 0x03, 0xe0, 0x4c, 0xc9, 0x70, 0x73, 0x79, 0x73, 0xae, 0xe0, 0x2a,
- 0xca, 0xe2, 0xdb, 0x0b, 0xae, 0xe0, 0x39, 0xfd, 0xee, 0x19, 0x05, 0x06,
- 0x07, 0x40, 0x63, 0x3a, 0x0f, 0x40, 0x81, 0x40, 0x87, 0x0f, 0x40, 0x46,
- 0x21, 0x46, 0x3c, 0x60, 0x37, 0x1d, 0x4b, 0x46, 0xc1, 0xd0, 0x7a, 0xe1,
- 0xe0, 0x30, 0x97, 0x76, 0x65, 0xf3, 0xe0, 0x44, 0x29, 0x75, 0x79, 0x61,
- 0xed, 0xe0, 0x30, 0xc8, 0xf4, 0x07, 0x04, 0x03, 0x22, 0xe0, 0x4c, 0x68,
- 0xf5, 0xe0, 0x4b, 0x27, 0xec, 0xdd, 0xab, 0xe5, 0x02, 0x98, 0xf2, 0x02,
- 0x90, 0xee, 0x02, 0x85, 0x65, 0xf4, 0xe0, 0x26, 0xa6, 0x61, 0x74, 0x69,
- 0x6f, 0xee, 0xe0, 0x25, 0x8a, 0x61, 0xe3, 0xe0, 0x3f, 0xea, 0x6c, 0x6c,
- 0x69, 0xe7, 0xe0, 0x2c, 0x8c, 0xae, 0x13, 0x06, 0x04, 0x06, 0x08, 0x40,
- 0x71, 0x4d, 0x45, 0x5c, 0x06, 0x45, 0xe2, 0x57, 0x38, 0x42, 0x77, 0xc0,
- 0xd0, 0xf4, 0x60, 0x4a, 0xa9, 0xc0, 0x86, 0xf0, 0xe0, 0x4b, 0x29, 0xec,
- 0x60, 0x4a, 0x79, 0xc0, 0xca, 0xe3, 0x60, 0x49, 0x35, 0x41, 0x8e, 0xc0,
- 0x96, 0xe1, 0x60, 0x4a, 0x5e, 0xc1, 0x26, 0xf3, 0x02, 0x8b, 0x75, 0xf2,
- 0x04, 0xe0, 0x4b, 0x52, 0x61, 0x6e, 0xe3, 0xdb, 0x63, 0xf4, 0x02, 0x86,
- 0x69, 0x74, 0xf5, 0xe0, 0x46, 0x50, 0x61, 0xee, 0x02, 0x89, 0x74, 0x63,
- 0x6c, 0x6f, 0x75, 0xe4, 0xe0, 0x2d, 0x54, 0x63, 0xe5, 0x02, 0x8a, 0x73,
- 0x2e, 0x73, 0x70, 0x61, 0x77, 0xee, 0xe0, 0x28, 0x3f, 0x2e, 0x64, 0x61,
- 0x74, 0xe1, 0xe0, 0x25, 0x4a, 0xe7, 0x06, 0x60, 0x4a, 0x97, 0xc1, 0x5e,
- 0x61, 0x74, 0x6c, 0x61, 0xee, 0xe0, 0x4b, 0x86, 0xe6, 0x04, 0x40, 0x66,
- 0x86, 0xef, 0x04, 0xe0, 0x4b, 0xe3, 0xae, 0x17, 0x06, 0x09, 0x04, 0x0a,
- 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x4c, 0x09, 0x60, 0x2d, 0x31, 0x11,
- 0x4f, 0x30, 0x41, 0x64, 0xc0, 0xc0, 0xf6, 0x60, 0x4a, 0x2c, 0xc0, 0xc3,
- 0xf4, 0x60, 0x49, 0xe7, 0x3f, 0x40, 0x7a, 0xc0, 0x6d, 0xf3, 0xe0, 0x4a,
- 0x06, 0xee, 0x60, 0x41, 0x94, 0x48, 0xa3, 0x40, 0x7a, 0xc0, 0x4f, 0xec,
- 0x60, 0x49, 0xda, 0xc0, 0xcd, 0xeb, 0x60, 0x4a, 0x27, 0xc0, 0xa5, 0xe8,
- 0x60, 0x4a, 0x7d, 0xc0, 0xbc, 0xe5, 0x60, 0x40, 0x57, 0xca, 0x20, 0xe3,
- 0x60, 0x3f, 0xe2, 0xca, 0xc9, 0xe2, 0x60, 0x42, 0x3b, 0xc8, 0x6a, 0xe1,
- 0x60, 0x49, 0xac, 0x41, 0x75, 0xc0, 0x47, 0x69, 0x6e, 0xe9, 0xe0, 0x3a,
- 0x0b, 0xae, 0x07, 0x04, 0x60, 0x39, 0x27, 0xd1, 0x91, 0xed, 0xe0, 0x49,
- 0xa3, 0xe3, 0xe0, 0x4b, 0x07, 0xe4, 0x05, 0x0a, 0x2c, 0xc0, 0x40, 0x75,
- 0x73, 0x74, 0x72, 0xe9, 0x60, 0x40, 0x07, 0xc9, 0x39, 0xe9, 0x03, 0x06,
- 0x88, 0x67, 0x65, 0xee, 0xe0, 0x49, 0x34, 0x65, 0x2e, 0x70, 0x6f, 0xf2,
- 0xe0, 0x37, 0xe8, 0x61, 0xee, 0x05, 0x08, 0xe0, 0x49, 0x11, 0x6d, 0x61,
- 0x72, 0x6b, 0xe5, 0xe0, 0x44, 0xa1, 0xe1, 0x04, 0xe0, 0x49, 0x0c, 0x70,
- 0x6f, 0xec, 0xe0, 0x3e, 0x31, 0xe5, 0x04, 0xe0, 0x3e, 0x7a, 0x70, 0x65,
- 0x6e, 0x64, 0x65, 0x6e, 0x74, 0xad, 0x04, 0x09, 0x08, 0x92, 0x72, 0x65,
- 0x76, 0x69, 0x65, 0xf7, 0xe0, 0x48, 0x62, 0x70, 0x61, 0x6e, 0x65, 0xec,
- 0xe0, 0x48, 0x5a, 0x69, 0x6e, 0x71, 0xf5, 0x02, 0x86, 0x69, 0x72, 0xf9,
- 0xe0, 0x48, 0x4e, 0x65, 0x73, 0xf4, 0xe0, 0x48, 0x48, 0x63, 0x6f, 0x6d,
- 0x6d, 0x69, 0x73, 0x73, 0x69, 0xef, 0xe0, 0x28, 0xdd, 0xae, 0x4e, 0xb2,
- 0x4d, 0x5d, 0x40, 0xcc, 0x56, 0x52, 0xd7, 0x0a, 0xe3, 0x05, 0x04, 0xe0,
- 0x4a, 0xdb, 0xe8, 0xe0, 0x25, 0xc9, 0x2e, 0xe8, 0xe0, 0x48, 0x24, 0xe1,
- 0x08, 0x06, 0x09, 0x08, 0x07, 0x08, 0x09, 0x86, 0x7a, 0x61, 0xf7, 0xe0,
- 0x2e, 0xf6, 0x77, 0x61, 0x73, 0x68, 0x69, 0xf2, 0xe0, 0x46, 0x0e, 0x74,
- 0x73, 0x75, 0x6b, 0xe9, 0xe0, 0x34, 0x17, 0x73, 0x68, 0x69, 0xeb, 0xe0,
- 0x2f, 0x52, 0x6d, 0x69, 0xae, 0x60, 0x40, 0x3b, 0xc0, 0xbb, 0xe7, 0x04,
- 0xe0, 0x47, 0xf6, 0x61, 0xf7, 0xc8, 0xd6, 0x62, 0x65, 0xae, 0xe0, 0x40,
- 0x2e, 0xae, 0x60, 0x41, 0x33, 0x42, 0x96, 0xc4, 0x48, 0xae, 0x0b, 0x06,
- 0x08, 0x59, 0xc1, 0x4e, 0x6c, 0x5c, 0xe3, 0xc4, 0x5a, 0xf5, 0x60, 0x48,
- 0xb0, 0xc0, 0xcd, 0xee, 0x60, 0x48, 0xfd, 0x40, 0x7a, 0xc0, 0xe4, 0x6c,
- 0x6f, 0x6e, 0xe4, 0xe0, 0x2b, 0x3c, 0xad, 0x05, 0x04, 0x09, 0x0a, 0x91,
- 0x76, 0x70, 0xee, 0x8d, 0x74, 0x68, 0x65, 0x2d, 0x62, 0x61, 0xee, 0xd5,
- 0x00, 0x64, 0x73, 0x6c, 0xae, 0x60, 0x48, 0x16, 0x42, 0x04, 0x9b, 0xe2,
- 0x05, 0x06, 0xe0, 0x20, 0x01, 0x75, 0x74, 0xf4, 0xe0, 0x25, 0xec, 0x72,
- 0xe2, 0xe0, 0x25, 0xe9, 0x61, 0x64, 0x64, 0xf2, 0xde, 0xd9, 0xed, 0x0c,
- 0x13, 0x0c, 0x04, 0x04, 0x60, 0x33, 0xfe, 0x54, 0x9f, 0xc1, 0x6a, 0x70,
- 0x65, 0xf2, 0x04, 0xe0, 0x44, 0x84, 0x74, 0x72, 0x69, 0xf8, 0x04, 0xe0,
- 0x4a, 0x17, 0x63, 0xe4, 0xda, 0x78, 0x6d, 0xef, 0x04, 0xe0, 0x4a, 0x15,
- 0x62, 0x69, 0x6c, 0xe9, 0xcb, 0x74, 0x69, 0xfa, 0xd1, 0xd4, 0xe4, 0xe0,
- 0x40, 0xb6, 0xe1, 0x05, 0x03, 0x03, 0x06, 0x89, 0xf2, 0xd1, 0xd6, 0xed,
- 0xda, 0x05, 0x6b, 0x61, 0xee, 0xe0, 0x2e, 0x21, 0x67, 0x65, 0x61, 0x6e,
- 0x64, 0x73, 0xef, 0xc7, 0x02, 0x62, 0x61, 0xf2, 0xc3, 0x3a, 0xec, 0x08,
- 0x0b, 0x07, 0x60, 0x38, 0x26, 0xd1, 0xa6, 0x6f, 0x76, 0x65, 0x63, 0x6f,
- 0x6c, 0x6c, 0xe5, 0xe0, 0x3a, 0xb9, 0x6c, 0x75, 0x73, 0xf4, 0xe0, 0x27,
- 0x16, 0xae, 0x60, 0x44, 0x65, 0xc1, 0x30, 0xeb, 0x05, 0x07, 0x03, 0x07,
- 0x8e, 0x75, 0x73, 0x61, 0xeb, 0xe0, 0x2e, 0x60, 0x6f, 0xed, 0xa8, 0x69,
- 0xae, 0x60, 0x23, 0x0d, 0xcb, 0x61, 0x65, 0x64, 0x61, 0xae, 0x5d, 0xdc,
- 0x60, 0x24, 0x0a, 0x45, 0x3f, 0x41, 0x4c, 0xa2, 0xe1, 0x06, 0x03, 0x05,
- 0xe0, 0x48, 0x9f, 0xf7, 0xc9, 0x02, 0x74, 0xe1, 0xe0, 0x45, 0x94, 0x72,
- 0x75, 0x67, 0xe1, 0xe0, 0x42, 0x49, 0xe9, 0x05, 0x0b, 0x05, 0x06, 0x85,
- 0x7a, 0xf5, 0x02, 0x84, 0xee, 0xe0, 0x2e, 0x24, 0xeb, 0xc8, 0xa6, 0x79,
- 0xe1, 0xe0, 0x2e, 0x1b, 0x74, 0x61, 0xf4, 0xe0, 0x44, 0xc9, 0x6a, 0xe9,
- 0xe0, 0x2e, 0x10, 0xe4, 0x45, 0x5e, 0xe0, 0x28, 0xaf, 0x68, 0x65, 0xf9,
- 0xce, 0x4f, 0x67, 0x6c, 0x65, 0x73, 0x69, 0x61, 0xf3, 0x02, 0x81, 0x2d,
- 0x63, 0x61, 0x72, 0x62, 0xef, 0xe0, 0x31, 0x64, 0xe6, 0x60, 0x2e, 0x69,
- 0xd9, 0x28, 0xe5, 0x60, 0x3d, 0x46, 0x47, 0xf0, 0xc4, 0x10, 0xe4, 0x09,
- 0x06, 0x06, 0x04, 0x60, 0x3c, 0x0f, 0xcd, 0x16, 0x76, 0xae, 0x4b, 0xa3,
- 0xde, 0xec, 0x72, 0x65, 0xf4, 0xe0, 0x42, 0x52, 0x66, 0xae, 0xdb, 0x18,
- 0xae, 0x09, 0x07, 0x06, 0x04, 0x60, 0x44, 0xd4, 0xc1, 0xaa, 0x72, 0x65,
- 0x70, 0xec, 0xe0, 0x30, 0x6e, 0xec, 0x60, 0x46, 0x05, 0xc1, 0x6d, 0xe9,
- 0xe0, 0x48, 0x56, 0x66, 0x6f, 0x72, 0x67, 0x65, 0x72, 0x6f, 0xe3, 0xe0,
- 0x33, 0x4e, 0xe3, 0x09, 0x0a, 0x41, 0x32, 0x49, 0x05, 0xe0, 0x3d, 0xd9,
- 0xf5, 0x04, 0xe0, 0x48, 0xf0, 0x72, 0xf5, 0xe0, 0x2d, 0x51, 0x68, 0xe9,
- 0x04, 0x17, 0x19, 0x85, 0x6e, 0xef, 0x03, 0x04, 0x8a, 0x73, 0xe5, 0xc7,
- 0x74, 0x6d, 0x69, 0x79, 0x61, 0xae, 0x60, 0x44, 0x53, 0xc3, 0x41, 0x68,
- 0xe5, 0xd1, 0x4d, 0x6b, 0xe1, 0x02, 0x91, 0x77, 0xe1, 0x02, 0x87, 0x6d,
- 0x69, 0x73, 0x61, 0xf4, 0xd1, 0x0e, 0xae, 0x60, 0x44, 0xb4, 0xc2, 0xc7,
- 0xe9, 0xe0, 0x3d, 0xe5, 0x68, 0x61, 0xf2, 0xc7, 0x43, 0x62, 0xe1, 0xe0,
- 0x43, 0xfd, 0xe2, 0x07, 0x04, 0x40, 0x93, 0xe0, 0x46, 0x51, 0x69, 0xe7,
- 0xc4, 0x85, 0x61, 0x72, 0xe1, 0x04, 0xe0, 0x43, 0xa1, 0x6b, 0x69, 0xae,
- 0x60, 0x41, 0xc7, 0x45, 0x94, 0xab, 0xe1, 0x04, 0xe0, 0x43, 0xaa, 0x6d,
- 0x61, 0x6c, 0x6c, 0x61, 0xed, 0xd2, 0x19, 0x32, 0x33, 0xb4, 0xe0, 0x33,
- 0x32, 0xae, 0x08, 0x60, 0x33, 0xd0, 0x44, 0x3e, 0xd0, 0x63, 0xf0, 0xe0,
- 0x47, 0x67, 0xe8, 0x19, 0x2e, 0x05, 0x1d, 0x03, 0x3a, 0x0c, 0x0f, 0x11,
- 0x41, 0xbd, 0x06, 0x06, 0x17, 0x12, 0x10, 0x42, 0x24, 0x03, 0x40, 0xe0,
- 0x09, 0x14, 0xd7, 0x1d, 0x1f, 0xc3, 0x07, 0x07, 0x04, 0x0a, 0xe0, 0x47,
- 0x42, 0xf8, 0x03, 0xc0, 0xbf, 0xee, 0xc1, 0x90, 0x66, 0xe7, 0xc7, 0x85,
- 0x64, 0x6b, 0x6b, 0x69, 0x6e, 0x65, 0xee, 0xe0, 0x21, 0x92, 0xe1, 0x03,
- 0x03, 0x86, 0xf0, 0xc6, 0x7f, 0x6d, 0x6d, 0x1f, 0xc3, 0xc6, 0xdb, 0xe2,
- 0xc7, 0xab, 0x7a, 0xe3, 0xe0, 0x47, 0xb4, 0xf9, 0x06, 0x0d, 0x05, 0xe0,
- 0x44, 0x01, 0xf5, 0x02, 0x85, 0x6e, 0xe4, 0xe0, 0x2c, 0x85, 0x67, 0xe1,
- 0xe0, 0x3b, 0x01, 0x6c, 0x6c, 0xe5, 0xd4, 0x5f, 0xe1, 0xe0, 0x46, 0xe3,
- 0xf6, 0xd4, 0x6e, 0xf5, 0x09, 0x06, 0x04, 0x09, 0x11, 0x05, 0xe0, 0x47,
- 0xcf, 0xf2, 0x60, 0x30, 0xe3, 0xd0, 0xdd, 0xee, 0xe0, 0x40, 0x56, 0x6d,
- 0x61, 0x6e, 0x69, 0x74, 0xe9, 0xe0, 0x41, 0x53, 0x69, 0x73, 0x73, 0x69,
- 0x65, 0x72, 0x2d, 0x6a, 0x75, 0x73, 0x74, 0x69, 0x63, 0xe5, 0xe0, 0x44,
- 0xac, 0x67, 0xe8, 0xe0, 0x3c, 0x78, 0xae, 0x60, 0x42, 0x68, 0x45, 0x43,
- 0x9c, 0xf4, 0x04, 0xe0, 0x47, 0xc3, 0x74, 0x70, 0x62, 0xe9, 0xe0, 0x27,
- 0xa0, 0xf3, 0x02, 0x84, 0xe2, 0xe0, 0x3c, 0x74, 0xae, 0x60, 0x20, 0x3e,
- 0x49, 0x7a, 0xc7, 0x7e, 0xf2, 0x06, 0x60, 0x3b, 0xa6, 0xcc, 0x00, 0x61,
- 0x2e, 0x68, 0x65, 0x61, 0x6c, 0xf4, 0xe0, 0x47, 0x65, 0xef, 0x12, 0x0d,
- 0x05, 0x29, 0x40, 0x47, 0x22, 0x12, 0x28, 0x40, 0x6b, 0x23, 0x19, 0x09,
- 0x05, 0xe0, 0x44, 0x1c, 0xf9, 0x04, 0xe0, 0x32, 0x26, 0x6c, 0x61, 0x6e,
- 0x64, 0xe5, 0xe0, 0x40, 0xa5, 0x75, 0xf3, 0xe0, 0x35, 0xdd, 0xf4, 0x05,
- 0x03, 0xe0, 0x47, 0x6e, 0xed, 0xd9, 0x60, 0x65, 0xec, 0x07, 0x0c, 0x60,
- 0x3b, 0xff, 0xc9, 0x87, 0x77, 0x69, 0x74, 0x68, 0x66, 0x6c, 0x69, 0x67,
- 0xe8, 0xe0, 0x3e, 0x58, 0xae, 0x06, 0x60, 0x3c, 0x1f, 0xca, 0xca, 0xf4,
- 0xe0, 0x45, 0x74, 0xf3, 0x02, 0xba, 0xf4, 0x06, 0x07, 0x28, 0xe0, 0x47,
- 0x15, 0x79, 0x68, 0x6f, 0x73, 0xf4, 0xdf, 0xb8, 0x69, 0x6e, 0xe7, 0x05,
- 0x15, 0xe0, 0x47, 0x20, 0xae, 0x02, 0x86, 0x6f, 0x76, 0xe8, 0xe0, 0x23,
- 0x91, 0x6d, 0x79, 0x6a, 0x69, 0x6e, 0x6f, 0x2e, 0x72, 0xf5, 0xe0, 0x47,
- 0x11, 0x2d, 0x63, 0x6c, 0x75, 0x73, 0x74, 0x65, 0xf2, 0xe0, 0x2a, 0x5f,
- 0x65, 0xe4, 0xd8, 0xa6, 0xf0, 0x04, 0xe0, 0x44, 0x55, 0x69, 0xf4, 0xe0,
- 0x20, 0x1d, 0xf2, 0x06, 0x04, 0x12, 0xe0, 0x36, 0x85, 0xf4, 0xe0, 0x38,
- 0xa9, 0xef, 0x05, 0x06, 0xe0, 0x3f, 0x1b, 0x6e, 0x6f, 0xe2, 0xe0, 0x2b,
- 0x1c, 0x6b, 0x61, 0xee, 0xe0, 0x35, 0x55, 0x6e, 0xe9, 0xe0, 0x3d, 0x43,
- 0xf0, 0x02, 0x88, 0x74, 0x6f, 0xae, 0x60, 0x31, 0x93, 0xd3, 0x0a, 0x6c,
- 0x69, 0x78, 0xae, 0xe0, 0x37, 0x13, 0xee, 0x09, 0x0d, 0x05, 0x07, 0x60,
- 0x3b, 0xf1, 0xc6, 0xd4, 0xea, 0x02, 0x83, 0xf9, 0xce, 0xea, 0x6f, 0xae,
- 0x60, 0x3d, 0x5d, 0xc4, 0x47, 0x67, 0xef, 0xe0, 0x3c, 0x17, 0x65, 0x66,
- 0x6f, 0xf3, 0xe0, 0x43, 0x2f, 0x61, 0xe9, 0xe0, 0x42, 0xad, 0x6d, 0xe5,
- 0x0d, 0x04, 0x22, 0x09, 0x0f, 0x06, 0x05, 0x09, 0x07, 0x53, 0xed, 0xc7,
- 0xe4, 0x75, 0x6e, 0xe9, 0xb2, 0xf3, 0x05, 0x06, 0xe0, 0x46, 0x8c, 0x6b,
- 0x6c, 0xe5, 0xe0, 0x35, 0xe5, 0xe5, 0x02, 0x84, 0xee, 0xe0, 0x36, 0x23,
- 0x63, 0x75, 0x72, 0x69, 0x74, 0xf9, 0x02, 0x83, 0xf0, 0xdf, 0xbd, 0x6d,
- 0xe1, 0xdf, 0xb9, 0x6f, 0x66, 0x66, 0x69, 0x63, 0xe5, 0xe0, 0x2e, 0x8a,
- 0x6c, 0x69, 0xee, 0x02, 0x85, 0x75, 0xf8, 0xe0, 0x33, 0x8d, 0x6b, 0xae,
- 0xe0, 0x28, 0x66, 0x67, 0x6f, 0xef, 0xe0, 0x2d, 0xd0, 0x66, 0x74, 0xf0,
- 0xd7, 0xcd, 0xe4, 0x03, 0xdc, 0x48, 0x65, 0xf0, 0xe0, 0x37, 0x5d, 0x62,
- 0x75, 0x69, 0xec, 0xe0, 0x42, 0x0c, 0xae, 0xca, 0x2d, 0xec, 0x0b, 0x0b,
- 0x05, 0x60, 0x33, 0x75, 0x4b, 0x16, 0x46, 0xa4, 0x81, 0xf4, 0x02, 0x85,
- 0x1f, 0x43, 0xe5, 0xc6, 0x80, 0xe1, 0xc6, 0x7d, 0x6d, 0xe5, 0xe0, 0x37,
- 0xd9, 0x64, 0x69, 0x6e, 0xe7, 0xe0, 0x44, 0x4d, 0xeb, 0x02, 0x90, 0xf5,
- 0x02, 0x88, 0x74, 0x6f, 0xae, 0x60, 0x29, 0xb5, 0xdb, 0x47, 0x72, 0xf9,
- 0xe0, 0x42, 0x26, 0xeb, 0x60, 0x30, 0x63, 0xd4, 0x92, 0xe6, 0x04, 0xe0,
- 0x45, 0x0b, 0xf5, 0xe0, 0x44, 0x81, 0x63, 0xeb, 0xe0, 0x3a, 0x0c, 0xe2,
- 0x03, 0x06, 0x84, 0x1f, 0x43, 0xf8, 0xe0, 0x3f, 0xba, 0xef, 0xe0, 0x3f,
- 0xb6, 0x62, 0x79, 0x2d, 0x73, 0x69, 0x74, 0xe5, 0xdc, 0xac, 0xee, 0x60,
- 0x33, 0x54, 0xd2, 0x8a, 0xed, 0x60, 0x44, 0xe4, 0xc0, 0xf4, 0xec, 0x04,
- 0xe0, 0x41, 0xeb, 0xf8, 0x02, 0x84, 0xb3, 0xe0, 0x25, 0xd2, 0xae, 0x04,
- 0xe0, 0x25, 0xcb, 0x6c, 0x69, 0xf6, 0xe0, 0x45, 0x82, 0xeb, 0x06, 0x60,
- 0x44, 0x91, 0xc1, 0x24, 0xae, 0x04, 0xe0, 0x43, 0x6e, 0xe3, 0x60, 0x44,
- 0x11, 0xc1, 0x98, 0xea, 0x02, 0x87, 0x65, 0x6c, 0x6d, 0xe5, 0xe0, 0x39,
- 0x7b, 0x61, 0x72, 0xf4, 0xe0, 0x3f, 0x66, 0xe9, 0x15, 0x06, 0x24, 0x40,
- 0x4b, 0x40, 0x4f, 0x07, 0x05, 0x14, 0x10, 0x18, 0x05, 0x04, 0x40, 0xd1,
- 0x18, 0x0a, 0xe0, 0x40, 0x6e, 0x7a, 0x65, 0xee, 0xe0, 0x3b, 0xd2, 0xf4,
- 0x04, 0xe0, 0x39, 0x56, 0xe1, 0x04, 0xe0, 0x34, 0x8e, 0x63, 0x68, 0xe9,
- 0x07, 0x09, 0x60, 0x3e, 0x92, 0xc6, 0xcf, 0xef, 0x02, 0x82, 0xf4, 0x88,
- 0x6d, 0x69, 0xf9, 0x84, 0x6e, 0x61, 0x6b, 0xe1, 0xe0, 0x3e, 0x8e, 0xf3,
- 0x02, 0xbc, 0x74, 0xef, 0x02, 0xb4, 0xf2, 0x02, 0x8d, 0xf9, 0x04, 0xe0,
- 0x43, 0x21, 0x6f, 0x66, 0x73, 0x63, 0xe9, 0xe0, 0x25, 0x67, 0xe9, 0x02,
- 0x88, 0x73, 0x63, 0xe8, 0x60, 0x3e, 0xa8, 0xc4, 0x6b, 0xe3, 0x02, 0x87,
- 0x68, 0x6f, 0x75, 0xf3, 0xe0, 0x3e, 0x9c, 0x61, 0xec, 0x04, 0xe0, 0x42,
- 0xfe, 0x73, 0x6f, 0x63, 0x69, 0x65, 0xf4, 0xe0, 0x3d, 0x50, 0xe9, 0xe0,
- 0x41, 0x76, 0xe1, 0x02, 0x85, 0x79, 0x61, 0xed, 0xc4, 0x3f, 0x6d, 0xe9,
- 0xcd, 0x1c, 0xf2, 0x02, 0x9f, 0xef, 0x08, 0x09, 0x08, 0x60, 0x3a, 0x08,
- 0xc1, 0x2f, 0xf3, 0x04, 0xe0, 0x40, 0x5c, 0x61, 0xeb, 0xc3, 0x0a, 0x6e,
- 0x6f, 0xae, 0x4d, 0x75, 0xe0, 0x32, 0xdb, 0x6b, 0x61, 0xf7, 0xc4, 0x19,
- 0xe1, 0x07, 0x04, 0x0b, 0x03, 0x04, 0x06, 0x86, 0xf9, 0xe0, 0x29, 0x88,
- 0xf4, 0x04, 0xe0, 0x3a, 0x4c, 0x73, 0x75, 0xeb, 0xe0, 0x40, 0xe3, 0xf2,
- 0xc9, 0xc3, 0x6e, 0xe1, 0xc2, 0xdf, 0x6b, 0x61, 0xf4, 0xe0, 0x29, 0x97,
- 0x69, 0x7a, 0x75, 0xed, 0xcd, 0x41, 0xe4, 0xc7, 0xad, 0xf0, 0x03, 0xd0,
- 0xce, 0xe8, 0xda, 0xad, 0x6f, 0xeb, 0xe0, 0x3e, 0x42, 0x6e, 0xef, 0x03,
- 0x04, 0x83, 0x68, 0xe1, 0xc3, 0x4d, 0xe4, 0xc0, 0xc7, 0x2e, 0x74, 0xef,
- 0x60, 0x28, 0xbc, 0xd9, 0x41, 0xed, 0x03, 0xcc, 0x28, 0xe5, 0x04, 0xe0,
- 0x37, 0xc3, 0x73, 0x68, 0x69, 0xed, 0xe0, 0x2e, 0x2f, 0xeb, 0x03, 0x06,
- 0x85, 0x6f, 0x6e, 0xe5, 0xe0, 0x3f, 0xfb, 0x69, 0xed, 0xe0, 0x3f, 0x96,
- 0xe1, 0x02, 0x83, 0xf7, 0xc3, 0x5f, 0xf2, 0xc3, 0xb8, 0x6a, 0xe9, 0xe0,
- 0x33, 0x90, 0xe8, 0xe0, 0x2c, 0x19, 0x67, 0x61, 0x73, 0x68, 0xe9, 0x0d,
- 0x21, 0x05, 0x08, 0x17, 0x0a, 0x0c, 0x18, 0x19, 0x0c, 0x08, 0x07, 0x8a,
- 0xf9, 0x02, 0x93, 0xef, 0x02, 0x88, 0x73, 0x68, 0x69, 0x6e, 0xef, 0xe0,
- 0x3d, 0x0d, 0x64, 0x6f, 0x67, 0x61, 0xf7, 0xe0, 0x29, 0x15, 0x61, 0x6d,
- 0xe1, 0x04, 0xe0, 0x37, 0x2b, 0xf4, 0xe0, 0x3f, 0x31, 0x75, 0xf2, 0xe0,
- 0x28, 0x66, 0x74, 0x73, 0x75, 0x6e, 0xef, 0xe0, 0x3f, 0x27, 0xf3, 0x02,
- 0x8b, 0x75, 0x6d, 0x69, 0x79, 0x6f, 0x73, 0x68, 0xe9, 0xe0, 0x42, 0xe9,
- 0x68, 0x69, 0x72, 0x61, 0x6b, 0x61, 0xf7, 0xd5, 0xb1, 0xef, 0x04, 0xe0,
- 0x28, 0xdb, 0x6d, 0xe9, 0xe0, 0x3f, 0x83, 0xee, 0x02, 0x83, 0xe5, 0xcb,
- 0x9c, 0x61, 0x72, 0x75, 0xf3, 0xc7, 0x7f, 0xed, 0x02, 0x88, 0x75, 0x72,
- 0x61, 0x79, 0xe1, 0xe0, 0x3e, 0xe5, 0x61, 0x74, 0x73, 0xf5, 0x03, 0xc1,
- 0xce, 0x73, 0x68, 0x69, 0xed, 0xcb, 0x28, 0xeb, 0x02, 0x88, 0x75, 0x72,
- 0x75, 0x6d, 0xe5, 0xe0, 0x41, 0x37, 0xe1, 0x04, 0xe0, 0x39, 0x56, 0xe7,
- 0x04, 0xe0, 0x36, 0xb7, 0xf5, 0xe0, 0x42, 0x8b, 0x69, 0x7a, 0xf5, 0x04,
- 0xe0, 0x3a, 0x6d, 0x6d, 0xef, 0xe0, 0x3e, 0xdb, 0x68, 0x69, 0x72, 0x6f,
- 0x73, 0xe8, 0xd3, 0xd6, 0x63, 0x68, 0x69, 0xe3, 0xe0, 0x28, 0x95, 0x61,
- 0x67, 0x61, 0x74, 0x73, 0x75, 0xed, 0xe0, 0x3b, 0xfe, 0xae, 0x04, 0xe0,
- 0x3f, 0xd9, 0x66, 0x75, 0x6b, 0xf5, 0x60, 0x3a, 0x4c, 0xc4, 0xb4, 0xe4,
- 0x02, 0x84, 0x6f, 0xf2, 0xcd, 0x2e, 0xe1, 0x04, 0xe0, 0x3b, 0xcc, 0x6b,
- 0x61, 0xae, 0x60, 0x39, 0xd9, 0x40, 0x52, 0x44, 0x5a, 0xc3, 0xf2, 0xe3,
- 0x04, 0xe0, 0x2a, 0xba, 0x68, 0x69, 0xf3, 0xc7, 0x0b, 0xae, 0x60, 0x3f,
- 0x45, 0xc0, 0x57, 0xe7, 0xd9, 0x74, 0xe5, 0x0d, 0x06, 0x40, 0x54, 0x08,
- 0x1a, 0x11, 0x08, 0x0d, 0x07, 0xe0, 0x30, 0x32, 0x74, 0x65, 0xed, 0xe0,
- 0x29, 0x4a, 0xf2, 0x0a, 0x0e, 0x21, 0x04, 0x06, 0x4f, 0x71, 0xe0, 0x2c,
- 0x12, 0x1f, 0x43, 0x78, 0x79, 0xae, 0x04, 0xe0, 0x2f, 0x1c, 0x6d, 0x1f,
- 0x43, 0xf8, 0x8c, 0xef, 0x02, 0x93, 0x79, 0xae, 0x04, 0xe0, 0x2f, 0x0e,
- 0x6d, 0x6f, 0x72, 0x65, 0x2d, 0x6f, 0x67, 0x2d, 0x72, 0x6f, 0xed, 0xca,
- 0x09, 0x6b, 0xf5, 0x04, 0xe0, 0x42, 0xcc, 0x73, 0xf3, 0xe0, 0x3d, 0x36,
- 0xed, 0xe0, 0x37, 0xca, 0x69, 0x74, 0xe1, 0xe0, 0x2b, 0x48, 0xe5, 0x04,
- 0xe0, 0x43, 0x16, 0x2d, 0x66, 0x6f, 0x72, 0x2d, 0x6d, 0x6f, 0xf2, 0xe0,
- 0x33, 0xf2, 0x70, 0x66, 0x6f, 0x72, 0xe7, 0xe0, 0x2e, 0xa8, 0xed, 0x03,
- 0x03, 0x87, 0xf3, 0xd9, 0x5e, 0x6e, 0xe5, 0x60, 0x3f, 0x76, 0xc2, 0x90,
- 0x62, 0x79, 0x67, 0x64, 0x73, 0x66, 0x6f, 0x72, 0x62, 0xf5, 0xd4, 0x1a,
- 0xec, 0x06, 0x5d, 0x99, 0xe0, 0x24, 0x4d, 0x73, 0x69, 0x6e, 0x6b, 0xe9,
- 0x60, 0x40, 0xb3, 0xc2, 0x2a, 0x6b, 0x69, 0x6e, 0x61, 0xee, 0xe0, 0x3e,
- 0x4e, 0x69, 0x6d, 0x61, 0x74, 0x75, 0x6e, 0x64, 0x75, 0x68, 0xf2, 0xe0,
- 0x2d, 0xf8, 0x67, 0x75, 0x72, 0xe9, 0xe0, 0x3b, 0x7a, 0xe1, 0x02, 0x84,
- 0xf6, 0xe0, 0x3b, 0x1f, 0x6c, 0x74, 0xe8, 0x06, 0x05, 0x07, 0xe0, 0x42,
- 0xa0, 0x63, 0xe1, 0xe0, 0x40, 0x70, 0xae, 0x60, 0x3c, 0xdb, 0x43, 0xa2,
- 0xa6, 0x2d, 0x63, 0x61, 0x72, 0x65, 0x72, 0x65, 0x66, 0x6f, 0xf2, 0xe0,
- 0x39, 0xea, 0x64, 0x66, 0xe3, 0x60, 0x21, 0x23, 0xe0, 0x21, 0x6d, 0xe2,
- 0x04, 0xe0, 0x41, 0x9b, 0x2e, 0xe3, 0x04, 0xe0, 0x40, 0xe1, 0x6c, 0x64,
- 0x6d, 0x61, 0x69, 0xec, 0xe0, 0x30, 0x93, 0xe1, 0x17, 0x04, 0x10, 0x06,
- 0x0a, 0x21, 0x3b, 0x23, 0x0d, 0x40, 0x47, 0x40, 0x58, 0x14, 0x29, 0x0a,
- 0x0f, 0x08, 0x0c, 0x21, 0xe0, 0x3c, 0xa1, 0xfa, 0xe0, 0x26, 0x91, 0x79,
- 0xe1, 0x02, 0x87, 0x73, 0x68, 0x69, 0xed, 0xe0, 0x3d, 0x5a, 0x6b, 0x61,
- 0xf7, 0xca, 0x54, 0x77, 0x61, 0xe9, 0xe0, 0x37, 0x19, 0xf5, 0x04, 0xe0,
- 0x40, 0x6c, 0x67, 0xe5, 0xe0, 0x2c, 0x97, 0xf4, 0x03, 0x07, 0x8a, 0x74,
- 0x66, 0x6a, 0x65, 0xec, 0xcd, 0xb8, 0x73, 0x75, 0x6b, 0x61, 0x69, 0x63,
- 0xe8, 0xe0, 0x37, 0x84, 0xef, 0x02, 0x85, 0x79, 0x61, 0xed, 0xca, 0x0b,
- 0x67, 0xe1, 0xca, 0x06, 0xf3, 0x05, 0x10, 0x1b, 0xc8, 0x35, 0xf5, 0x02,
- 0x8a, 0x72, 0xe1, 0x04, 0xe0, 0x41, 0x35, 0xad, 0xe0, 0x34, 0x20, 0xe4,
- 0xc9, 0xf1, 0xe8, 0x02, 0x91, 0xe9, 0x02, 0x88, 0xed, 0x03, 0xd3, 0x90,
- 0x6f, 0xf4, 0xc5, 0x91, 0x6b, 0x61, 0x6d, 0xe9, 0xc5, 0x41, 0x62, 0x61,
- 0x6e, 0xe7, 0xe0, 0x31, 0xab, 0x61, 0xed, 0x04, 0xe0, 0x2b, 0x7e, 0xe9,
- 0xe0, 0x26, 0x9a, 0xf2, 0x06, 0x0b, 0x06, 0x05, 0xce, 0x15, 0x76, 0x65,
- 0x73, 0x74, 0x63, 0x65, 0x6c, 0x65, 0xe2, 0xdf, 0x15, 0x69, 0x6d, 0xe1,
- 0xe0, 0x3d, 0xbe, 0x65, 0xe9, 0xe0, 0x3e, 0x69, 0xe1, 0x60, 0x36, 0xc6,
- 0xc8, 0x73, 0xf0, 0x02, 0x85, 0x70, 0x6f, 0xf5, 0xdc, 0x80, 0x6d, 0xe9,
- 0xe0, 0x36, 0xbb, 0xee, 0x05, 0x04, 0x09, 0x0d, 0x93, 0xf9, 0xe0, 0x26,
- 0x86, 0xee, 0x03, 0xc9, 0xd7, 0x61, 0xee, 0xe0, 0x40, 0x63, 0xe7, 0x02,
- 0x85, 0x6f, 0xf5, 0xe0, 0x40, 0x72, 0x67, 0x6c, 0xe9, 0xc5, 0xe8, 0xe4,
- 0x05, 0x04, 0xe0, 0x25, 0xb0, 0xf3, 0xe0, 0x37, 0x7b, 0x63, 0x72, 0x61,
- 0x66, 0x74, 0xe5, 0xe0, 0x2c, 0x79, 0xe1, 0x04, 0xe0, 0x36, 0xe5, 0xed,
- 0x02, 0x88, 0x69, 0x67, 0x61, 0x77, 0xe1, 0xe0, 0x40, 0x2a, 0x61, 0xeb,
- 0xc9, 0xe5, 0xed, 0x05, 0x05, 0x14, 0x09, 0xa5, 0x75, 0xf2, 0xe0, 0x3c,
- 0x4b, 0xed, 0x02, 0x88, 0x65, 0x72, 0x66, 0x65, 0xf3, 0xe0, 0x3a, 0x7a,
- 0x61, 0x72, 0x66, 0x65, 0x61, 0xf3, 0xe0, 0x3c, 0x19, 0x62, 0x75, 0x72,
- 0xe7, 0x60, 0x3f, 0x1a, 0xc2, 0x2a, 0xe1, 0x04, 0x0d, 0x06, 0x88, 0xf4,
- 0x02, 0x85, 0x6f, 0xee, 0xe0, 0x3d, 0x4a, 0x61, 0xed, 0xe0, 0x37, 0x6f,
- 0xf2, 0x60, 0x36, 0x2e, 0xca, 0x0a, 0x6d, 0x61, 0x74, 0x73, 0xf5, 0xe0,
- 0x37, 0xc5, 0x64, 0xe1, 0xe0, 0x3c, 0x30, 0x2d, 0x72, 0x61, 0x64, 0x69,
- 0x6f, 0x2d, 0xef, 0xe0, 0x35, 0x7d, 0xec, 0x05, 0x07, 0x03, 0xc7, 0x7d,
- 0x6c, 0x6f, 0x66, 0x66, 0xe1, 0xde, 0x21, 0xe6, 0xcb, 0x05, 0xe4, 0xe0,
- 0x32, 0xa8, 0xeb, 0x03, 0x12, 0x8d, 0xf5, 0x03, 0x06, 0x84, 0x73, 0x61,
- 0xee, 0xe0, 0x39, 0xdb, 0xe9, 0xe0, 0x39, 0xd7, 0xe2, 0xe0, 0x25, 0x8c,
- 0xef, 0x02, 0x84, 0xee, 0xe0, 0x33, 0xbf, 0x64, 0x61, 0xf4, 0xe0, 0x25,
- 0x04, 0x61, 0x74, 0xe1, 0xe0, 0x2a, 0x33, 0xe9, 0x04, 0xe0, 0x40, 0x16,
- 0x62, 0x61, 0xf2, 0xc9, 0x27, 0xe7, 0x05, 0x04, 0xe0, 0x35, 0xea, 0xe9,
- 0xe0, 0x3f, 0x44, 0x65, 0x62, 0xef, 0xcd, 0x0f, 0x65, 0x62, 0x61, 0x72,
- 0xf5, 0xe0, 0x3c, 0xe5, 0xe4, 0x02, 0x83, 0xf3, 0xc5, 0xb5, 0x61, 0x6e,
- 0xef, 0xe0, 0x3c, 0xb3, 0xe3, 0x02, 0x9a, 0x68, 0xe9, 0x04, 0x08, 0x05,
- 0x83, 0x72, 0x6f, 0x67, 0x61, 0x74, 0xe1, 0xdb, 0x61, 0x6f, 0xea, 0xe0,
- 0x3d, 0xe6, 0xee, 0xc3, 0xdb, 0xea, 0xe0, 0x3b, 0x7d, 0xe3, 0xe0, 0x31,
- 0xf5, 0xe2, 0x05, 0x04, 0xe0, 0x35, 0x8e, 0xed, 0xe0, 0x35, 0x86, 0x69,
- 0x6b, 0xe9, 0xe0, 0x3f, 0x3a, 0xe7, 0x27, 0x30, 0x04, 0x1b, 0x04, 0x14,
- 0x0b, 0x40, 0x5d, 0x40, 0x6f, 0x40, 0xba, 0x42, 0x44, 0x0d, 0x15, 0x40,
- 0x59, 0x1d, 0x40, 0xa7, 0x08, 0x09, 0x40, 0x9e, 0x1c, 0x04, 0x0a, 0x40,
- 0x8d, 0x05, 0x58, 0xae, 0x56, 0x95, 0x48, 0xff, 0xa8, 0x1f, 0xc3, 0x02,
- 0x9a, 0x7c, 0x6e, 0x73, 0x74, 0x69, 0xe7, 0x02, 0x88, 0x6c, 0x69, 0x65,
- 0x66, 0x65, 0xf2, 0xd5, 0xf9, 0x62, 0x65, 0x73, 0x74, 0x65, 0x6c, 0x6c,
- 0xe5, 0xd5, 0xef, 0xe1, 0x04, 0x06, 0xc6, 0xb4, 0x1f, 0x45, 0x4b, 0xe7,
- 0xc6, 0x63, 0x6c, 0x73, 0x1f, 0xc3, 0xe0, 0x3a, 0xee, 0xfa, 0xe0, 0x2d,
- 0x91, 0xf9, 0x05, 0x07, 0xe0, 0x40, 0x0b, 0x6f, 0x6b, 0x75, 0xf4, 0xe0,
- 0x36, 0x9b, 0x65, 0x6f, 0x6e, 0xe7, 0x05, 0x60, 0x23, 0xb0, 0x85, 0x67,
- 0xe9, 0xe0, 0x29, 0x81, 0xf8, 0xe0, 0x2d, 0x72, 0xf7, 0x05, 0x08, 0xe0,
- 0x3f, 0xeb, 0x69, 0x64, 0x64, 0x6c, 0xe5, 0xe0, 0x3d, 0x34, 0x61, 0x6e,
- 0x67, 0xea, 0xda, 0xd7, 0x76, 0xae, 0x04, 0xe0, 0x3f, 0xcf, 0xe1, 0x60,
- 0x3e, 0xba, 0xba, 0xf5, 0x13, 0x09, 0x04, 0x0b, 0x04, 0x03, 0x0b, 0x07,
- 0x04, 0x05, 0x4e, 0x45, 0x60, 0x29, 0x9a, 0x42, 0xcd, 0xc4, 0xe0, 0x73,
- 0x68, 0x69, 0x6b, 0x61, 0xed, 0xe0, 0x3b, 0xf0, 0xf2, 0xe0, 0x3f, 0x54,
- 0x6f, 0x76, 0x64, 0x61, 0x67, 0x65, 0x61, 0x69, 0xe4, 0xd9, 0x7e, 0xec,
- 0xe0, 0x31, 0x57, 0xea, 0xc3, 0x36, 0xe9, 0x02, 0x84, 0x74, 0xe1, 0xdd,
- 0x59, 0xe4, 0xe0, 0x3e, 0xc4, 0x65, 0x72, 0x6e, 0xf3, 0xe0, 0x2e, 0x9c,
- 0xe3, 0xe0, 0x34, 0x59, 0x62, 0xae, 0xe0, 0x2e, 0x6c, 0xe1, 0x02, 0x86,
- 0x72, 0x64, 0xe9, 0xe0, 0x3c, 0x16, 0x6d, 0x2e, 0xe7, 0xe0, 0x3f, 0x18,
- 0xf3, 0x07, 0x06, 0x60, 0x33, 0x93, 0xcb, 0xdc, 0x6a, 0x2e, 0xe2, 0xe0,
- 0x2c, 0x93, 0xae, 0x10, 0x05, 0x05, 0x0d, 0x04, 0x0e, 0x06, 0x04, 0x0a,
- 0x06, 0x04, 0x60, 0x37, 0xd9, 0xc3, 0x5f, 0xf6, 0x24, 0xe0, 0x3a, 0x08,
- 0xf4, 0x60, 0x34, 0x5e, 0x84, 0xf3, 0x05, 0x15, 0xe0, 0x38, 0x5f, 0x76,
- 0x61, 0x6c, 0xe2, 0xe0, 0x39, 0x8f, 0xf2, 0xe0, 0x39, 0x11, 0xef, 0x05,
- 0x04, 0xe0, 0x39, 0x04, 0xf3, 0xe0, 0x38, 0x05, 0xe6, 0xe0, 0x3e, 0x44,
- 0xee, 0x60, 0x38, 0x5a, 0xc0, 0xa5, 0xed, 0xe0, 0x34, 0x38, 0x6a, 0x61,
- 0x6e, 0x2d, 0x6d, 0x61, 0xf9, 0xe0, 0x30, 0xce, 0xe8, 0x60, 0x34, 0x26,
- 0xc4, 0xc5, 0xe6, 0xe0, 0x34, 0x20, 0xe1, 0x04, 0xe0, 0x39, 0xe1, 0xe8,
- 0xe0, 0x3e, 0x1d, 0xf2, 0x0b, 0x05, 0x04, 0x40, 0x40, 0x13, 0x11, 0x37,
- 0xe0, 0x3e, 0x5e, 0xf5, 0x60, 0x3e, 0x0c, 0xbc, 0xf0, 0xe0, 0x33, 0xc8,
- 0xef, 0x06, 0x06, 0x13, 0x06, 0x0b, 0x8c, 0x7a, 0x6e, 0xf9, 0xe0, 0x3b,
- 0xfc, 0xf5, 0x02, 0x86, 0xf0, 0x60, 0x3c, 0x98, 0xc2, 0x50, 0x6e, 0x64,
- 0x68, 0x61, 0x6e, 0x64, 0xec, 0xe0, 0x30, 0x7e, 0x73, 0x73, 0xe5, 0xe0,
- 0x3b, 0x24, 0xee, 0x04, 0xe0, 0x38, 0xc4, 0x64, 0x61, 0xf2, 0xe0, 0x3b,
- 0x27, 0x6b, 0x73, 0x2d, 0x74, 0xe8, 0x04, 0xe0, 0x2f, 0xa3, 0xe9, 0xd4,
- 0x3c, 0xe3, 0xd8, 0x41, 0xe9, 0x03, 0x08, 0x84, 0x77, 0x2e, 0x67, 0x6f,
- 0xf6, 0xe0, 0x3a, 0xc3, 0xf0, 0xe0, 0x3d, 0xd1, 0xed, 0xca, 0xfc, 0xe5,
- 0x03, 0x05, 0x84, 0x74, 0xe1, 0xe0, 0x3b, 0x75, 0xe5, 0xe0, 0x3c, 0xfe,
- 0xe1, 0xe0, 0x2c, 0x84, 0xe1, 0x09, 0x09, 0x0a, 0x15, 0x60, 0x2a, 0x11,
- 0xc6, 0x44, 0xf4, 0x04, 0xe0, 0x30, 0x2d, 0xe9, 0xe0, 0x3c, 0xb2, 0x70,
- 0xe8, 0x04, 0xe0, 0x37, 0x72, 0x6f, 0xf8, 0xc1, 0xdc, 0xee, 0x06, 0x05,
- 0x60, 0x3d, 0x77, 0x81, 0x76, 0xe9, 0xe0, 0x39, 0x31, 0x64, 0x72, 0x61,
- 0x70, 0x69, 0xe4, 0xe0, 0x37, 0xd2, 0x6a, 0x65, 0xf7, 0xc2, 0x59, 0xae,
- 0x60, 0x38, 0xf7, 0x43, 0xfd, 0x40, 0x5a, 0xc1, 0x08, 0xef, 0x10, 0x40,
- 0xfd, 0x1b, 0x2c, 0x13, 0x12, 0x06, 0x1a, 0x0e, 0x1a, 0x0a, 0x06, 0x0c,
- 0x0d, 0xba, 0xf6, 0x06, 0x03, 0x04, 0xe0, 0x3e, 0x35, 0xf4, 0xc4, 0x15,
- 0x65, 0xf2, 0xcf, 0x31, 0xae, 0x1f, 0x08, 0x06, 0x0c, 0x13, 0x0d, 0x0d,
- 0x15, 0x11, 0x0c, 0x0c, 0x04, 0x0d, 0x0a, 0x0e, 0x12, 0x50, 0xe6, 0x4b,
- 0x27, 0x04, 0x3a, 0x40, 0x48, 0x5a, 0xb1, 0x44, 0x09, 0x15, 0xc0, 0x48,
- 0xf6, 0x60, 0x32, 0xd0, 0x49, 0xa6, 0xc0, 0xc3, 0xf5, 0x60, 0x3c, 0x3c,
- 0xc0, 0xca, 0xf4, 0x60, 0x3c, 0x2c, 0x1f, 0x03, 0x0e, 0x0c, 0x40, 0x7a,
- 0x3a, 0xb3, 0xf3, 0x50, 0xfd, 0x60, 0x23, 0xa9, 0x42, 0x95, 0x44, 0xec,
- 0x1b, 0x03, 0x14, 0x40, 0x96, 0x05, 0x1c, 0xc0, 0xe9, 0xf0, 0x60, 0x3c,
- 0x14, 0x03, 0x18, 0x17, 0x03, 0x40, 0x7a, 0x19, 0xc0, 0x54, 0xee, 0x08,
- 0x60, 0x33, 0xed, 0x49, 0x2e, 0xc0, 0xb6, 0xe3, 0xe0, 0x2d, 0xa2, 0xed,
- 0x60, 0x3a, 0xbf, 0x41, 0x31, 0x03, 0x07, 0x03, 0x18, 0x17, 0x03, 0x40,
- 0x98, 0x1c, 0x0f, 0x24, 0x40, 0x4f, 0xc0, 0x67, 0xec, 0x60, 0x32, 0x74,
- 0x41, 0xf0, 0x46, 0x46, 0x41, 0x3b, 0x03, 0x2f, 0x40, 0x7d, 0x1e, 0xc0,
- 0x4f, 0xeb, 0x60, 0x3b, 0xca, 0x03, 0x1f, 0x1d, 0x1e, 0x40, 0x84, 0xc0,
- 0xfb, 0xe9, 0x5c, 0xe3, 0x5e, 0xe5, 0x1b, 0x1a, 0x40, 0x7a, 0x40, 0x49,
- 0xa4, 0xe8, 0xe0, 0x3b, 0xbf, 0xe7, 0x60, 0x3b, 0xd6, 0x14, 0x03, 0x1e,
- 0x40, 0x75, 0x30, 0x24, 0xc0, 0x4f, 0xe5, 0x60, 0x32, 0x3a, 0x4a, 0x20,
- 0x40, 0x49, 0xc0, 0xda, 0xe3, 0x60, 0x36, 0xb5, 0x45, 0x04, 0x03, 0x03,
- 0x14, 0x03, 0x40, 0xb4, 0xc0, 0x82, 0xe2, 0x60, 0x33, 0x43, 0x40, 0xcf,
- 0x47, 0x77, 0x0a, 0x18, 0x1a, 0x03, 0x40, 0x7a, 0x19, 0x05, 0xc0, 0x4f,
- 0xe1, 0x60, 0x32, 0x10, 0x41, 0x21, 0x48, 0x46, 0x0a, 0x1b, 0x40, 0xdd,
- 0x24, 0xc0, 0x4f, 0xf5, 0x02, 0x92, 0x76, 0xae, 0x0c, 0x60, 0x31, 0xfa,
- 0x03, 0x1a, 0x41, 0x35, 0x46, 0xbf, 0xc0, 0x58, 0xed, 0xe0, 0x3b, 0x7c,
- 0x70, 0x69, 0xec, 0xe0, 0x21, 0x82, 0xf4, 0x08, 0x05, 0x08, 0x04, 0x05,
- 0xe0, 0x3d, 0x0c, 0x73, 0xf5, 0xe0, 0x38, 0x33, 0x70, 0x61, 0x6e, 0x74,
- 0x68, 0xe5, 0xcd, 0x71, 0xef, 0xe0, 0x21, 0xcd, 0x65, 0x6d, 0xe2, 0xc5,
- 0x6b, 0x64, 0x6e, 0x73, 0xae, 0x04, 0xe0, 0x3a, 0xc2, 0xe3, 0x60, 0x3c,
- 0xc9, 0xb4, 0xf3, 0x04, 0x05, 0xcb, 0x97, 0x68, 0xe9, 0xe0, 0x36, 0x98,
- 0xe5, 0x04, 0xe0, 0x35, 0xab, 0xee, 0xe0, 0x39, 0x74, 0xf2, 0x05, 0x07,
- 0xe0, 0x25, 0x09, 0x6c, 0x69, 0x63, 0xe5, 0xe0, 0x38, 0xf5, 0x69, 0xfa,
- 0xe0, 0x37, 0x3a, 0xf0, 0x4b, 0x7b, 0xe0, 0x31, 0x5e, 0xef, 0x05, 0x0e,
- 0xe0, 0x3c, 0xc0, 0xe7, 0x04, 0xe0, 0x3c, 0xc9, 0x6c, 0xe5, 0x42, 0x55,
- 0x60, 0x24, 0x3f, 0xd6, 0x33, 0x64, 0x79, 0xe5, 0xe0, 0x2b, 0x8f, 0xee,
- 0x06, 0x4b, 0x55, 0xe0, 0x22, 0xcb, 0x6f, 0x68, 0x65, 0xae, 0xe0, 0x34,
- 0xaf, 0xec, 0x05, 0x0b, 0xe0, 0x3b, 0xa7, 0xe6, 0x04, 0xe0, 0x3c, 0xa1,
- 0x66, 0x61, 0x6e, 0xae, 0xde, 0x35, 0xe4, 0x04, 0xe0, 0x3c, 0x96, 0x70,
- 0xef, 0xcc, 0x5c, 0xeb, 0x03, 0xcb, 0x30, 0x61, 0x73, 0xe5, 0xe0, 0x2f,
- 0x7b, 0x6a, 0x6f, 0x6d, 0xe5, 0xd7, 0x4f, 0xe9, 0x02, 0x83, 0xf0, 0xd8,
- 0x23, 0x61, 0x6e, 0xe9, 0xe0, 0x34, 0x0a, 0xe4, 0x02, 0x84, 0xef, 0xe0,
- 0x34, 0xac, 0x61, 0x64, 0xe4, 0xe0, 0x3a, 0xcb, 0xe2, 0x02, 0x84, 0xef,
- 0xe0, 0x32, 0xae, 0xae, 0x11, 0x04, 0x07, 0x04, 0x04, 0x04, 0x06, 0x5f,
- 0x95, 0x4b, 0x70, 0x47, 0x07, 0x48, 0x08, 0xc0, 0xbe, 0xf3, 0xe0, 0x39,
- 0x41, 0xf0, 0x60, 0x3a, 0x7b, 0x40, 0xca, 0xab, 0xed, 0xe0, 0x35, 0x85,
- 0xe8, 0xe0, 0x3a, 0xa2, 0xe7, 0xe0, 0x3b, 0x18, 0xe5, 0x60, 0x30, 0xf4,
- 0xc9, 0x71, 0xe3, 0xe0, 0x3a, 0x7a, 0xae, 0x0f, 0x04, 0x06, 0x50, 0x0a,
- 0x5b, 0x4b, 0x4b, 0x54, 0x03, 0x43, 0x6d, 0x36, 0xc0, 0xb7, 0xf5, 0xe0,
- 0x3c, 0x1d, 0xe9, 0x60, 0x3a, 0x65, 0xc0, 0x91, 0x64, 0x79, 0xee, 0xd2,
- 0x0a, 0xee, 0x04, 0xe0, 0x3c, 0x0b, 0x69, 0x65, 0x7a, 0x6e, 0xef, 0xe0,
- 0x38, 0x19, 0xed, 0x0b, 0x05, 0x4d, 0xe2, 0x60, 0x27, 0x51, 0x45, 0xd5,
- 0xc0, 0xea, 0x69, 0xee, 0xe0, 0x2e, 0x31, 0xe2, 0xe0, 0x3a, 0xe6, 0xec,
- 0x08, 0x05, 0x27, 0x15, 0x08, 0xe0, 0x3b, 0x9c, 0x75, 0xe7, 0xe0, 0x38,
- 0x98, 0xef, 0x04, 0x05, 0x05, 0x84, 0x70, 0xf0, 0xe0, 0x2d, 0x83, 0x6f,
- 0xed, 0xe0, 0x34, 0x3b, 0xe7, 0xe0, 0x2f, 0xb4, 0xe2, 0x04, 0xe0, 0x3a,
- 0xde, 0x61, 0xec, 0x04, 0xe0, 0x3b, 0xc2, 0xae, 0x04, 0xe0, 0x3a, 0xee,
- 0xf3, 0xe0, 0x3a, 0xde, 0xe9, 0x03, 0x07, 0x86, 0x77, 0x69, 0x63, 0xe5,
- 0xe0, 0x2b, 0x08, 0x74, 0x63, 0xe8, 0xe0, 0x26, 0x5e, 0xe4, 0xe0, 0x2d,
- 0x49, 0xe5, 0x04, 0xe0, 0x3b, 0x9f, 0xe5, 0xc0, 0xab, 0x61, 0xf3, 0x60,
- 0x25, 0xc4, 0xd3, 0xac, 0xea, 0x03, 0x05, 0x83, 0x1f, 0x43, 0xf8, 0xc1,
- 0xdb, 0xef, 0xc1, 0xd8, 0xe5, 0x04, 0x08, 0xc2, 0x53, 0xf2, 0x03, 0xc7,
- 0xd4, 0xe4, 0xe0, 0x24, 0x6a, 0xed, 0xe0, 0x37, 0xf5, 0xe9, 0x0e, 0x06,
- 0x40, 0x43, 0x07, 0x16, 0x0f, 0x05, 0x0c, 0x48, 0xfa, 0xe0, 0x31, 0xe9,
- 0xf6, 0x60, 0x26, 0xc4, 0xc9, 0x4a, 0xf4, 0x05, 0x05, 0x06, 0x12, 0x88,
- 0x70, 0x61, 0x67, 0xe5, 0x9c, 0x6c, 0x61, 0xe2, 0xe0, 0x3a, 0xe4, 0x68,
- 0x75, 0xe2, 0x06, 0x60, 0x32, 0x3b, 0xc8, 0x9d, 0x70, 0x72, 0x65, 0x76,
- 0x69, 0x65, 0xf7, 0xd7, 0x3a, 0x61, 0x70, 0x70, 0x2e, 0xf3, 0xe0, 0x2b,
- 0xe9, 0xad, 0x02, 0x86, 0x72, 0x65, 0x70, 0xef, 0xd5, 0x5b, 0x70, 0x61,
- 0x67, 0x65, 0x73, 0x2e, 0x72, 0x69, 0x74, 0x2e, 0x65, 0xe4, 0xe0, 0x39,
- 0xd4, 0x72, 0xec, 0x60, 0x26, 0x14, 0xcd, 0x71, 0xee, 0x02, 0x8e, 0xef,
- 0x02, 0x85, 0x7a, 0xe1, 0xe0, 0x37, 0x42, 0x77, 0x61, 0xee, 0xe0, 0x37,
- 0x3c, 0x61, 0xee, 0xe0, 0x33, 0x40, 0x6c, 0x64, 0x65, 0x73, 0xeb, 0x04,
- 0xe0, 0x34, 0xc4, 0x1f, 0x43, 0xe5, 0xe0, 0x34, 0xc2, 0x69, 0xfa, 0xe0,
- 0x34, 0x14, 0xe6, 0x04, 0xe0, 0x38, 0x79, 0x75, 0xae, 0x60, 0x33, 0x23,
- 0xc6, 0xb6, 0xe5, 0x02, 0x85, 0x73, 0xf3, 0xe0, 0x26, 0x10, 0x68, 0x74,
- 0x61, 0x76, 0x75, 0x6f, 0xe1, 0xe0, 0x2c, 0xb7, 0xe8, 0x04, 0xe0, 0x3a,
- 0xcc, 0xef, 0xd6, 0xbc, 0xe7, 0x4c, 0x14, 0x55, 0xbc, 0x54, 0xe8, 0xc4,
- 0x10, 0xe5, 0x0e, 0x0a, 0x23, 0x34, 0x08, 0x07, 0x09, 0x60, 0x38, 0xce,
- 0x40, 0x64, 0xc1, 0x06, 0xf4, 0x02, 0x84, 0xf3, 0xe0, 0x25, 0x0f, 0xed,
- 0xc4, 0xb3, 0xef, 0x07, 0x0e, 0x60, 0x32, 0xbc, 0xc7, 0x1c, 0x72, 0xe7,
- 0x04, 0xe0, 0x39, 0xbe, 0x69, 0x61, 0xae, 0x60, 0x38, 0x6d, 0xc0, 0xd9,
- 0x6d, 0x65, 0x74, 0x72, 0x65, 0x2d, 0x65, 0x78, 0x70, 0xe5, 0xe0, 0x37,
- 0x5a, 0xee, 0x04, 0x13, 0x09, 0x84, 0xf4, 0x08, 0x60, 0x25, 0xd2, 0x48,
- 0x30, 0xcc, 0x74, 0x6c, 0x65, 0x6e, 0x74, 0x61, 0x70, 0xe9, 0xe0, 0x39,
- 0xec, 0xef, 0x04, 0xe0, 0x34, 0xc5, 0xf6, 0xe0, 0x34, 0xc4, 0x6b, 0xe1,
- 0xc2, 0x37, 0xae, 0x08, 0x60, 0x22, 0x94, 0x4b, 0x1f, 0xc5, 0x00, 0xee,
- 0x60, 0x38, 0x15, 0xc0, 0x63, 0x6d, 0x6f, 0x6c, 0x6f, 0xe7, 0xe0, 0x30,
- 0xc5, 0x69, 0x73, 0x65, 0xe9, 0xe0, 0x35, 0x39, 0x68, 0x69, 0x72, 0x6e,
- 0x2e, 0xee, 0xe0, 0x2c, 0x6e, 0xe5, 0x02, 0x86, 0x6c, 0x76, 0x69, 0xee,
- 0xdc, 0xa1, 0xeb, 0x02, 0x86, 0x67, 0x61, 0x6c, 0xe1, 0xdc, 0x44, 0xae,
- 0xe0, 0x34, 0x5a, 0xe4, 0x09, 0x07, 0x60, 0x27, 0x87, 0x50, 0xec, 0xc1,
- 0x9e, 0x79, 0x6e, 0x69, 0xe1, 0xe0, 0x29, 0x6b, 0xe1, 0x04, 0xe0, 0x29,
- 0x63, 0x6e, 0x73, 0xeb, 0xe0, 0x29, 0x60, 0xe3, 0xe0, 0x38, 0xfd, 0xe2,
- 0x05, 0x60, 0x39, 0xd8, 0xa4, 0xe9, 0xe0, 0x38, 0x1e, 0xe1, 0x10, 0x08,
- 0x10, 0x06, 0x0f, 0x32, 0x19, 0x60, 0x34, 0x8f, 0x43, 0x3f, 0x40, 0xa5,
- 0xc0, 0xfc, 0xf5, 0x03, 0xc0, 0xba, 0xec, 0xe0, 0x2b, 0xb6, 0x74, 0x65,
- 0x77, 0x61, 0x79, 0xae, 0x04, 0xe0, 0x37, 0xac, 0x64, 0x65, 0xf6, 0xe0,
- 0x39, 0xc0, 0x72, 0x64, 0xe5, 0xe0, 0x28, 0xbc, 0x6e, 0xe7, 0x02, 0x83,
- 0xf7, 0xd4, 0xb0, 0x61, 0x76, 0x69, 0x69, 0xeb, 0xe0, 0x34, 0x8c, 0xed,
- 0x06, 0x05, 0x1f, 0xe0, 0x35, 0x00, 0x76, 0xe9, 0xe0, 0x30, 0x12, 0xe5,
- 0x07, 0x06, 0x4d, 0xe1, 0xe0, 0x2b, 0xbf, 0xf3, 0x60, 0x39, 0x3b, 0xc0,
- 0x6a, 0xad, 0x02, 0x88, 0x73, 0x65, 0x72, 0x76, 0x65, 0xf2, 0xd5, 0xcf,
- 0x68, 0x6f, 0xf3, 0xe0, 0x30, 0xe0, 0x61, 0x67, 0x6f, 0xf2, 0xe0, 0x35,
- 0x05, 0xec, 0x05, 0x04, 0xe0, 0x39, 0x7e, 0xf3, 0xe0, 0x34, 0x50, 0xec,
- 0x05, 0x04, 0xe0, 0x38, 0x8a, 0xf5, 0xe0, 0x38, 0x7b, 0x65, 0xf2, 0xe0,
- 0x37, 0x44, 0xe9, 0xe0, 0x2b, 0x4e, 0x31, 0xb2, 0xe0, 0x38, 0xb0, 0xae,
- 0x06, 0x60, 0x28, 0xfa, 0xd0, 0x63, 0x76, 0x62, 0x72, 0x70, 0x6c, 0x73,
- 0xe2, 0xcd, 0x24, 0xe6, 0x1c, 0x08, 0x12, 0x01, 0x04, 0x41, 0xae, 0x0e,
- 0x03, 0x42, 0x01, 0x40, 0xfb, 0x18, 0x0c, 0x40, 0x95, 0x04, 0x11, 0x41,
- 0x07, 0x1e, 0x40, 0x6b, 0x04, 0x09, 0xc6, 0x9b, 0x1f, 0x43, 0x78, 0x72,
- 0xe4, 0xe0, 0x38, 0x3f, 0xf9, 0x05, 0x06, 0xe0, 0x37, 0xa5, 0x72, 0x65,
- 0xf3, 0xe0, 0x32, 0xf1, 0x6c, 0x6b, 0x65, 0xf3, 0xc4, 0xae, 0x76, 0xe7,
- 0xe0, 0x37, 0xb3, 0xf5, 0x0c, 0x31, 0x0b, 0x29, 0x0f, 0x34, 0x40, 0x59,
- 0x40, 0x6d, 0x19, 0x8b, 0xf4, 0x05, 0x17, 0x06, 0x04, 0x85, 0x75, 0x72,
- 0xe5, 0x04, 0x05, 0xc8, 0x4c, 0x6d, 0x61, 0x69, 0xec, 0x85, 0x68, 0x6f,
- 0x73, 0x74, 0x69, 0x6e, 0x67, 0xae, 0xe0, 0x38, 0xbb, 0x74, 0x73, 0xf5,
- 0xe0, 0x37, 0xa9, 0x73, 0xf5, 0xdd, 0xa1, 0x62, 0xef, 0xe0, 0x37, 0x2c,
- 0x61, 0xe2, 0xe0, 0x2e, 0x4f, 0xf3, 0x06, 0x60, 0x31, 0x37, 0xc2, 0x6d,
- 0xf3, 0xe0, 0x33, 0xc0, 0xf2, 0x03, 0x18, 0x88, 0xf5, 0x05, 0x0c, 0xe0,
- 0x33, 0xd7, 0x6b, 0x61, 0x77, 0x61, 0x2e, 0x6d, 0x69, 0x79, 0xe1, 0xe0,
- 0x2d, 0xf1, 0x64, 0x6f, 0xee, 0xe0, 0x34, 0x05, 0x6e, 0x69, 0x74, 0x75,
- 0xf2, 0xe0, 0x27, 0x13, 0x61, 0xee, 0xe0, 0x2d, 0xc1, 0xef, 0x02, 0x86,
- 0x73, 0x73, 0xeb, 0xe0, 0x31, 0x6c, 0x69, 0x73, 0xeb, 0xe0, 0x33, 0x1d,
- 0xee, 0x06, 0x0b, 0x0b, 0xe0, 0x38, 0x7c, 0xe4, 0x04, 0xe0, 0x38, 0x8d,
- 0x61, 0x63, 0xe9, 0xe0, 0x2e, 0x83, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73,
- 0x2e, 0x66, 0xee, 0xc4, 0x29, 0xe1, 0x03, 0x06, 0x88, 0x68, 0x61, 0x73,
- 0x68, 0xe9, 0xbb, 0x67, 0x61, 0x74, 0x61, 0xae, 0xe0, 0x2d, 0xf6, 0x62,
- 0x61, 0xf3, 0xdc, 0x52, 0xeb, 0x03, 0xc0, 0x49, 0xf5, 0x0a, 0x04, 0x10,
- 0x05, 0x09, 0x07, 0x07, 0xe0, 0x2e, 0xcf, 0x79, 0xe1, 0xc8, 0x6c, 0xf3,
- 0x04, 0xe0, 0x31, 0xec, 0x68, 0x69, 0x6d, 0x61, 0xae, 0x60, 0x33, 0xa1,
- 0x43, 0x8f, 0x89, 0x72, 0xef, 0xe0, 0x2e, 0xe2, 0x6d, 0x69, 0x74, 0x73,
- 0x75, 0xae, 0xe0, 0x2d, 0xc6, 0x69, 0xae, 0x4c, 0x63, 0xe0, 0x2a, 0xc0,
- 0x64, 0x6f, 0x6d, 0xe9, 0xe0, 0x2e, 0x7a, 0x63, 0x68, 0xe9, 0x04, 0xe0,
- 0x21, 0x7d, 0x79, 0x61, 0x6d, 0xe1, 0xe0, 0x2b, 0x06, 0xe1, 0x02, 0x85,
- 0x79, 0xe1, 0xe0, 0x2d, 0x86, 0xe7, 0xe0, 0x2d, 0x8a, 0x6a, 0xe9, 0x0c,
- 0x09, 0x05, 0x18, 0x04, 0x06, 0x0f, 0x14, 0x06, 0xe0, 0x2e, 0x48, 0x79,
- 0x6f, 0x73, 0x68, 0x69, 0x64, 0xe1, 0xce, 0x88, 0x74, 0xf3, 0xe0, 0x37,
- 0x8b, 0xf3, 0x02, 0x86, 0x68, 0x69, 0xf2, 0xe0, 0x29, 0x36, 0xe1, 0x02,
- 0x88, 0x77, 0x61, 0xae, 0x40, 0x5f, 0xe0, 0x33, 0x8e, 0x74, 0xef, 0xd2,
- 0xa5, 0xef, 0xe0, 0x30, 0x25, 0x6e, 0x6f, 0x6d, 0x69, 0xf9, 0xac, 0x6d,
- 0xe9, 0x02, 0x85, 0x6e, 0xef, 0xe0, 0x2d, 0x3b, 0xae, 0x60, 0x2e, 0x60,
- 0xc6, 0xde, 0x6b, 0x61, 0x77, 0xe1, 0x02, 0x89, 0x67, 0x75, 0x63, 0x68,
- 0x69, 0x6b, 0xef, 0xce, 0x43, 0xae, 0x5b, 0x50, 0xd3, 0x03, 0x69, 0x64,
- 0x65, 0xf2, 0xdc, 0x72, 0x65, 0x64, 0xe1, 0xe0, 0x2e, 0x45, 0xe5, 0x05,
- 0x0e, 0xe0, 0x33, 0x69, 0x74, 0x74, 0x65, 0x72, 0x74, 0x64, 0x61, 0x73,
- 0x6e, 0x65, 0x74, 0xfa, 0xd3, 0x32, 0x66, 0x75, 0xeb, 0xce, 0x18, 0x64,
- 0x61, 0x69, 0x2e, 0x69, 0x77, 0x61, 0xf4, 0xe0, 0x33, 0x82, 0x63, 0x68,
- 0x75, 0xae, 0x04, 0xe0, 0x2c, 0xd1, 0x74, 0xef, 0x60, 0x32, 0x7e, 0xc2,
- 0x4b, 0xf4, 0x04, 0xe0, 0x36, 0xb0, 0x70, 0x61, 0x63, 0x63, 0x65, 0x73,
- 0xf3, 0xd3, 0x98, 0xf3, 0xd8, 0x5b, 0xf2, 0x10, 0x0a, 0x08, 0x40, 0xe9,
- 0x24, 0x40, 0x6a, 0x3a, 0x60, 0x24, 0x1a, 0x4f, 0xb5, 0xc1, 0xb8, 0x1f,
- 0xc3, 0x02, 0x82, 0xf8, 0x97, 0xe6, 0xe0, 0x2d, 0x69, 0x75, 0x73, 0x6b,
- 0x79, 0x2e, 0xe4, 0xd9, 0x09, 0xef, 0x09, 0x04, 0x09, 0x0d, 0x40, 0xba,
- 0xe0, 0x2a, 0x32, 0xf9, 0xe0, 0x31, 0xfc, 0xf3, 0x04, 0xe0, 0x31, 0xf3,
- 0x69, 0xee, 0xd3, 0x9a, 0x6e, 0xf4, 0x02, 0x84, 0xe9, 0xe0, 0x24, 0x61,
- 0x64, 0xef, 0xe0, 0x34, 0x81, 0xed, 0x02, 0x94, 0xae, 0x05, 0x0a, 0xe0,
- 0x2c, 0xb5, 0x6d, 0x61, 0x72, 0x6b, 0x65, 0x74, 0xe9, 0xe0, 0x23, 0xda,
- 0xe8, 0xe0, 0x36, 0x49, 0xad, 0x15, 0x09, 0x06, 0x04, 0x06, 0x06, 0x04,
- 0x06, 0x07, 0x17, 0x12, 0x05, 0x09, 0x04, 0x02, 0x04, 0x05, 0x0a, 0xe0,
- 0x21, 0xb3, 0xf7, 0x40, 0x7b, 0x42, 0xa2, 0x55, 0xe6, 0xd5, 0x0b, 0xf6,
- 0x40, 0x72, 0xe0, 0x2d, 0x67, 0xf5, 0xe0, 0x2d, 0xd3, 0xf4, 0x47, 0x2e,
- 0xe0, 0x26, 0xb8, 0xf3, 0x50, 0x0e, 0xe0, 0x22, 0x29, 0xf2, 0xe0, 0x2d,
- 0xef, 0xf0, 0x40, 0x58, 0xe0, 0x2d, 0xa1, 0xef, 0x19, 0x60, 0x2d, 0xda,
- 0xc0, 0xa1, 0xee, 0x0e, 0x04, 0x42, 0xdb, 0x4d, 0x0a, 0x5e, 0x13, 0x41,
- 0xce, 0x42, 0x48, 0xc3, 0xd1, 0xea, 0xe0, 0x36, 0x9f, 0xe8, 0xe0, 0x36,
- 0x9b, 0xed, 0x0d, 0x27, 0x46, 0xc6, 0x5b, 0x47, 0x4b, 0x5a, 0x2c, 0x44,
- 0x42, 0xc4, 0x13, 0xef, 0xe0, 0x36, 0x89, 0xeb, 0x58, 0xaa, 0xdd, 0x60,
- 0xe9, 0x1d, 0x46, 0xc6, 0x60, 0x29, 0xb3, 0xc1, 0x5c, 0xe8, 0xe0, 0x2d,
- 0xa7, 0xe7, 0x90, 0xe6, 0xe0, 0x30, 0x87, 0xe4, 0x4f, 0xb6, 0xdf, 0xe1,
- 0xe3, 0x05, 0x60, 0x2d, 0x4d, 0x9a, 0xe1, 0xe0, 0x36, 0x62, 0xe1, 0x08,
- 0x60, 0x2d, 0x94, 0x40, 0xa1, 0xc2, 0x37, 0xfa, 0xe0, 0x36, 0x39, 0xe7,
- 0x06, 0x60, 0x31, 0x15, 0xc3, 0x14, 0xe1, 0xe0, 0x27, 0x5c, 0xe9, 0x02,
- 0x9d, 0x75, 0x6c, 0xe9, 0x02, 0x81, 0x2d, 0xf6, 0x03, 0x0b, 0x81, 0xe5,
- 0x03, 0x07, 0x81, 0x6e, 0x65, 0x7a, 0x69, 0xe1, 0x02, 0x81, 0x2d, 0x67,
- 0x69, 0x75, 0xec, 0xe0, 0x30, 0x8f, 0x62, 0xef, 0xda, 0x03, 0xe5, 0x06,
- 0x07, 0x08, 0x05, 0xc0, 0x4a, 0x73, 0x65, 0x6e, 0xe9, 0xe0, 0x31, 0xea,
- 0x6e, 0x63, 0x68, 0x6b, 0x69, 0xf3, 0xda, 0x7a, 0xe9, 0x59, 0xe9, 0xdb,
- 0x37, 0xe5, 0x09, 0x06, 0x07, 0x0d, 0x14, 0x0d, 0xe0, 0x35, 0xcb, 0x74,
- 0x6c, 0xf3, 0xe0, 0x35, 0x38, 0x73, 0x69, 0x74, 0x65, 0xae, 0xd7, 0xf5,
- 0xed, 0x02, 0x83, 0xf9, 0xcd, 0x5a, 0x61, 0x73, 0x6f, 0xee, 0xe0, 0x2b,
- 0xd4, 0xe4, 0x02, 0x89, 0x65, 0x73, 0x6b, 0x74, 0x6f, 0xf0, 0xe0, 0x33,
- 0xa0, 0x64, 0x6e, 0x73, 0xae, 0x57, 0x74, 0xdc, 0x26, 0x62, 0x6f, 0xf8,
- 0x02, 0x81, 0x2d, 0x6f, 0x73, 0xae, 0x5a, 0x25, 0xdb, 0xa3, 0x2e, 0xe8,
- 0xe0, 0x34, 0x00, 0x64, 0x72, 0xe9, 0xc2, 0x16, 0xe1, 0x03, 0x1d, 0x8d,
- 0xee, 0x06, 0x09, 0x06, 0xe0, 0x30, 0x76, 0x7a, 0x69, 0x73, 0x6b, 0x61,
- 0xee, 0xe0, 0x2f, 0x2c, 0x6b, 0x66, 0xf5, 0xe0, 0x2f, 0x0c, 0x63, 0x61,
- 0x69, 0xf3, 0xe0, 0x31, 0xfc, 0x6d, 0x65, 0xf2, 0x04, 0xe0, 0x34, 0xc4,
- 0x63, 0x61, 0x6e, 0xf6, 0xcc, 0x2b, 0x31, 0x2d, 0x64, 0x65, 0x2e, 0x63,
- 0x6c, 0x6f, 0x75, 0xe4, 0xd7, 0x3a, 0xad, 0x02, 0x95, 0x70, 0x61, 0x72,
- 0xad, 0x02, 0x82, 0xb2, 0x82, 0x31, 0x2e, 0x62, 0x61, 0x72, 0x65, 0x6d,
- 0x65, 0x74, 0x61, 0xec, 0xc1, 0x24, 0x31, 0x2e, 0x70, 0x61, 0x61, 0x73,
- 0x2e, 0x6d, 0x61, 0x73, 0x73, 0x69, 0x76, 0x65, 0x67, 0x72, 0x69, 0xe4,
- 0xe0, 0x35, 0x39, 0xef, 0x0f, 0x09, 0x04, 0x40, 0xaa, 0x16, 0x19, 0x55,
- 0x62, 0x4d, 0x89, 0x4a, 0xba, 0xc6, 0xbf, 0x75, 0x6e, 0x64, 0x61, 0x74,
- 0xe9, 0xe0, 0x20, 0x7f, 0xf3, 0xe0, 0x31, 0xba, 0xf2, 0x0d, 0x0c, 0x1f,
- 0x07, 0x13, 0x1c, 0x06, 0x14, 0x60, 0x22, 0xc0, 0xc3, 0x56, 0x75, 0xed,
- 0x06, 0x60, 0x34, 0xbd, 0xc0, 0x6a, 0xfa, 0xe0, 0x26, 0x0b, 0xf4, 0x04,
- 0x08, 0x0a, 0x83, 0x77, 0x6f, 0x72, 0x74, 0xe8, 0xe0, 0x32, 0xef, 0x6d,
- 0x69, 0x73, 0x73, 0x6f, 0x75, 0xec, 0xe0, 0x2f, 0x96, 0xe5, 0xc1, 0x52,
- 0x61, 0xec, 0xe0, 0x34, 0x4e, 0x73, 0xe1, 0x60, 0x2f, 0x57, 0xc4, 0x51,
- 0xec, 0x02, 0x85, 0x1f, 0x43, 0xec, 0x05, 0x81, 0xe9, 0x02, 0x81, 0x2d,
- 0x63, 0x65, 0x73, 0xe5, 0xe0, 0x2d, 0xc2, 0xe7, 0x02, 0x90, 0x6f, 0x74,
- 0x2e, 0xe8, 0x02, 0x83, 0x69, 0xf3, 0x83, 0x65, 0x72, 0x2e, 0x6e, 0xe1,
- 0xdf, 0x8d, 0x65, 0x62, 0x6c, 0x6f, 0x63, 0xeb, 0xe0, 0x34, 0x4e, 0xe4,
- 0x60, 0x33, 0xd8, 0xc0, 0xf5, 0xae, 0x04, 0x06, 0xd6, 0xc2, 0x73, 0x61,
- 0xec, 0xe0, 0x34, 0x83, 0xed, 0x04, 0xe0, 0x34, 0xb2, 0xe5, 0xe0, 0x21,
- 0x52, 0xad, 0x05, 0x03, 0x04, 0x06, 0x85, 0x74, 0xe8, 0x8e, 0x73, 0x6f,
- 0xed, 0x8a, 0x6f, 0x75, 0xf2, 0xe0, 0x25, 0x89, 0x6d, 0x6f, 0x72, 0xe5,
- 0x87, 0x62, 0x65, 0x74, 0x74, 0x65, 0x72, 0xae, 0xd6, 0x3d, 0xef, 0x07,
- 0x50, 0x11, 0x54, 0x89, 0xcf, 0xf1, 0xe4, 0x04, 0xe0, 0x34, 0x86, 0x6e,
- 0x65, 0x74, 0x77, 0x6f, 0xf2, 0xe0, 0x32, 0xb0, 0xec, 0x03, 0x04, 0x88,
- 0xec, 0xe0, 0x2e, 0x41, 0x6b, 0x65, 0x62, 0x69, 0xe2, 0xe0, 0x2e, 0x3b,
- 0x69, 0x6f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0xf2, 0x8d, 0x67, 0xe7, 0xe0,
- 0x2e, 0xbf, 0xee, 0x05, 0x04, 0xe0, 0x20, 0x57, 0x77, 0xeb, 0xdd, 0x94,
- 0x63, 0x2e, 0x66, 0x72, 0x2d, 0x70, 0x61, 0x72, 0x2e, 0x73, 0x63, 0xf7,
- 0xdc, 0xda, 0xed, 0x04, 0xe0, 0x34, 0x42, 0xae, 0x60, 0x32, 0xd8, 0x40,
- 0x76, 0xbb, 0xec, 0x0b, 0x0f, 0x0e, 0x28, 0x13, 0x0c, 0x06, 0x5c, 0xc1,
- 0xd2, 0x24, 0xf9, 0x06, 0x50, 0x20, 0xe0, 0x24, 0x08, 0x6e, 0x6e, 0x68,
- 0x6f, 0x73, 0xf4, 0xd6, 0xaa, 0x74, 0x2e, 0x63, 0x6c, 0x6f, 0x75, 0x64,
- 0x2e, 0x6d, 0x75, 0x6e, 0xe9, 0xcf, 0xd0, 0xef, 0x05, 0x04, 0x19, 0xd5,
- 0x01, 0x77, 0xe5, 0xd1, 0xc1, 0xf2, 0x08, 0x0a, 0x5f, 0xac, 0x4d, 0x13,
- 0xc2, 0x05, 0xe9, 0x05, 0x5b, 0xd2, 0xcb, 0xde, 0xf0, 0xe0, 0x2b, 0x89,
- 0x65, 0x6e, 0xe3, 0xe0, 0x2d, 0xcf, 0x70, 0xf0, 0xe0, 0x2c, 0x54, 0xe9,
- 0x07, 0x08, 0x60, 0x21, 0xc7, 0xd1, 0x5c, 0x67, 0x68, 0xf4, 0x60, 0x31,
- 0x8f, 0xc0, 0x7d, 0xe3, 0xdd, 0x5b, 0xe5, 0x02, 0x83, 0xf3, 0xc6, 0x2c,
- 0x6b, 0x6b, 0xe5, 0xe0, 0x25, 0x4e, 0x64, 0x72, 0xf6, 0xe0, 0x33, 0xc0,
- 0xe1, 0x07, 0x03, 0x06, 0x07, 0xe0, 0x32, 0xb9, 0xf4, 0xde, 0x5e, 0x70,
- 0x2e, 0xe9, 0xe0, 0x28, 0x16, 0x6e, 0x64, 0x65, 0xf2, 0xe0, 0x2d, 0x1c,
- 0x6b, 0x73, 0x74, 0xe1, 0xe0, 0x30, 0x4e, 0xeb, 0xe0, 0x23, 0x43, 0xea,
- 0x07, 0x04, 0x60, 0x21, 0x0c, 0xd2, 0x8a, 0xe5, 0xe0, 0x28, 0x98, 0x61,
- 0xec, 0xe0, 0x28, 0x9d, 0xe9, 0x0f, 0x0a, 0x06, 0x40, 0x50, 0x2e, 0x38,
- 0x05, 0x09, 0x09, 0x42, 0xac, 0xe0, 0x2f, 0xf8, 0xf4, 0x05, 0x5c, 0xf4,
- 0xd6, 0x87, 0xea, 0xe0, 0x25, 0x4e, 0x73, 0xe8, 0x5e, 0xd1, 0xd4, 0xa4,
- 0xf2, 0x02, 0x9c, 0xed, 0x02, 0x85, 0x64, 0xe1, 0xe0, 0x32, 0x0f, 0xae,
- 0x0d, 0x5b, 0x96, 0x45, 0xa0, 0x47, 0xfb, 0x40, 0xb8, 0x46, 0x9d, 0xc1,
- 0xe7, 0xee, 0x60, 0x29, 0x34, 0xc7, 0xe3, 0xe5, 0x07, 0x15, 0x0d, 0x5d,
- 0xbc, 0xd5, 0x6c, 0xf7, 0x02, 0x85, 0x65, 0xe2, 0xe0, 0x32, 0x70, 0x61,
- 0x6c, 0x6c, 0x2d, 0x67, 0x61, 0x74, 0x65, 0x77, 0x61, 0xf9, 0xde, 0x0d,
- 0xee, 0x02, 0x84, 0xfa, 0xe0, 0x2d, 0x0e, 0x65, 0x74, 0x2e, 0xe3, 0xdc,
- 0x50, 0x62, 0x61, 0x73, 0xe5, 0xe0, 0x32, 0xc6, 0xee, 0x06, 0x06, 0x09,
- 0x0e, 0xc4, 0x2a, 0xee, 0x60, 0x28, 0x1b, 0xc3, 0x71, 0x65, 0x61, 0x72,
- 0xf4, 0x60, 0x2c, 0x7c, 0xc4, 0x6a, 0xe1, 0x04, 0xe0, 0x31, 0x4e, 0x6e,
- 0xe3, 0x04, 0xe0, 0x32, 0x25, 0xe9, 0xcc, 0x12, 0xae, 0x05, 0x44, 0x17,
- 0xcc, 0xfd, 0xe3, 0xe0, 0x23, 0xa4, 0xec, 0x03, 0x0b, 0xa3, 0xed, 0x04,
- 0xe0, 0x32, 0xea, 0xae, 0x60, 0x30, 0xc0, 0xc1, 0xc0, 0x65, 0x67, 0x65,
- 0x61, 0xf2, 0x03, 0xdd, 0x8f, 0xad, 0x07, 0x04, 0x04, 0x04, 0x03, 0xcd,
- 0x68, 0x73, 0xe7, 0xdd, 0x85, 0x6a, 0xf0, 0xdd, 0x81, 0x67, 0xe2, 0xdd,
- 0x7d, 0xe4, 0xcd, 0x6b, 0x61, 0xf5, 0xdd, 0x76, 0x61, 0x74, 0xe5, 0xe0,
- 0x26, 0x69, 0x67, 0x75, 0xe5, 0xd9, 0xcd, 0xe5, 0x04, 0xe0, 0x2e, 0xa0,
- 0xec, 0xe0, 0x2e, 0x69, 0xe4, 0x04, 0xe0, 0x31, 0xbe, 0x65, 0xec, 0xd7,
- 0xf0, 0xae, 0x06, 0x60, 0x2d, 0x37, 0xc3, 0xfd, 0xe3, 0x04, 0xe0, 0x31,
- 0xe1, 0x6c, 0x6f, 0x75, 0x64, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72,
- 0xed, 0xcb, 0xe4, 0xe8, 0x06, 0x04, 0x09, 0xe0, 0x22, 0x10, 0xf6, 0xe0,
- 0x22, 0x1c, 0xf3, 0x04, 0xe0, 0x31, 0x86, 0xeb, 0xe0, 0x22, 0x13, 0x2d,
- 0x6d, 0x75, 0x65, 0x6e, 0x73, 0xf4, 0xe0, 0x22, 0x92, 0xe5, 0x0c, 0x05,
- 0x08, 0x11, 0x08, 0x05, 0x06, 0x44, 0x9f, 0xe0, 0x2c, 0x25, 0xf4, 0x5c,
- 0xb8, 0xd4, 0xb2, 0x73, 0x74, 0x65, 0x2d, 0xe9, 0xe0, 0x26, 0xc0, 0xf2,
- 0x04, 0xe0, 0x21, 0x61, 0xf2, 0x04, 0xe0, 0x2f, 0xfa, 0x61, 0xf2, 0x60,
- 0x2c, 0xa4, 0xc4, 0x22, 0x6e, 0x74, 0x69, 0x67, 0x65, 0xf2, 0xd9, 0xce,
- 0x69, 0xf2, 0xe0, 0x29, 0xc8, 0x65, 0x64, 0x62, 0xe1, 0xc4, 0x01, 0xe4,
- 0x06, 0x19, 0x04, 0xe0, 0x2d, 0x2a, 0x6f, 0x72, 0xe1, 0x02, 0x87, 0x70,
- 0x65, 0x6f, 0x70, 0xec, 0xdd, 0xbf, 0x69, 0x6e, 0x66, 0x72, 0x61, 0x63,
- 0x6c, 0x6f, 0x75, 0xe4, 0xe0, 0x2f, 0xce, 0xea, 0xe0, 0x31, 0x18, 0xe5,
- 0x04, 0xe0, 0x2b, 0x46, 0xf2, 0xe0, 0x2f, 0xaf, 0xe3, 0xe0, 0x30, 0x96,
- 0x62, 0xf8, 0x02, 0x81, 0x2d, 0x6f, 0xf3, 0xd6, 0x4b, 0xe1, 0x09, 0x06,
- 0x40, 0x43, 0x21, 0x13, 0x14, 0x06, 0x8c, 0x75, 0x73, 0xeb, 0xe0, 0x30,
- 0xf2, 0xf3, 0x02, 0xb1, 0xf4, 0x06, 0x0d, 0x08, 0xe0, 0x31, 0xc5, 0x76,
- 0x70, 0xf3, 0x02, 0x85, 0xae, 0x53, 0xce, 0xc7, 0x44, 0xad, 0xcc, 0x7c,
- 0x73, 0x74, 0x61, 0x63, 0xeb, 0xe0, 0x31, 0xa3, 0x6c, 0xf9, 0x02, 0x85,
- 0x6c, 0xe2, 0xe0, 0x31, 0x9b, 0x2d, 0x74, 0x65, 0x72, 0x72, 0x61, 0x72,
- 0x69, 0xf5, 0xe0, 0x29, 0x07, 0x68, 0x69, 0x6f, 0xee, 0x04, 0xe0, 0x31,
- 0xa8, 0x73, 0x74, 0x6f, 0xf2, 0xe0, 0x23, 0xd7, 0xf2, 0x05, 0x5b, 0xf5,
- 0xd4, 0xed, 0xed, 0x07, 0x06, 0x60, 0x2f, 0x63, 0xc2, 0x2a, 0x73, 0x74,
- 0xe5, 0xe0, 0x23, 0x79, 0xe5, 0x02, 0x83, 0xf2, 0xdb, 0xb3, 0x71, 0x75,
- 0x69, 0x70, 0xed, 0xcf, 0x29, 0xee, 0x06, 0x60, 0x2f, 0xa6, 0xc1, 0xd3,
- 0x74, 0x61, 0x73, 0x79, 0x6c, 0x65, 0x61, 0x67, 0x75, 0xe5, 0xcd, 0xa7,
- 0xed, 0x02, 0x8c, 0x69, 0x6c, 0xf9, 0x06, 0x60, 0x2f, 0x37, 0xc2, 0x2a,
- 0xe4, 0xde, 0x86, 0x2e, 0xf0, 0xe0, 0x2f, 0x8c, 0x6b, 0x65, 0x66, 0xf5,
- 0xdf, 0x43, 0xe9, 0x06, 0x60, 0x2f, 0x94, 0xc0, 0xac, 0x72, 0x77, 0xe9,
- 0xc9, 0x74, 0xe7, 0xe0, 0x30, 0x6b, 0xe5, 0x21, 0x11, 0x40, 0x68, 0x2b,
- 0x40, 0x6d, 0x2b, 0x40, 0x6c, 0x1d, 0x0d, 0x0f, 0x40, 0xe2, 0x40, 0x53,
- 0x32, 0x0a, 0x2e, 0x1a, 0x15, 0x41, 0x71, 0x39, 0x14, 0x40, 0x48, 0x03,
- 0x10, 0xe0, 0x27, 0x5a, 0x7a, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2e, 0x6b,
- 0x75, 0x6c, 0x65, 0x75, 0x76, 0x65, 0xee, 0xd3, 0x67, 0xf8, 0x07, 0x08,
- 0x25, 0x04, 0x07, 0x04, 0x8b, 0x74, 0x72, 0x61, 0x73, 0xf0, 0xe0, 0x2a,
- 0xfc, 0xf0, 0x03, 0x09, 0x84, 0x72, 0x65, 0x73, 0xf3, 0x60, 0x2e, 0xa8,
- 0xc2, 0x50, 0x6f, 0xf3, 0xd2, 0x46, 0x65, 0x72, 0xf4, 0x04, 0xe0, 0x30,
- 0xe8, 0x73, 0x2d, 0x63, 0x6f, 0x6d, 0x70, 0x74, 0x61, 0x62, 0xec, 0xe0,
- 0x24, 0x16, 0x6e, 0xe5, 0xd5, 0x4f, 0x68, 0x69, 0x62, 0xe9, 0xe0, 0x26,
- 0xc5, 0xe5, 0xe0, 0x26, 0xce, 0x63, 0x68, 0x61, 0x6e, 0x67, 0xe5, 0x60,
- 0x2e, 0x76, 0xc2, 0x50, 0xae, 0x02, 0x89, 0x6f, 0x72, 0x74, 0x73, 0x69,
- 0x6e, 0x66, 0xef, 0x8a, 0x66, 0x75, 0x74, 0x75, 0x72, 0x65, 0x63, 0x6d,
- 0x73, 0x2e, 0xe1, 0xcd, 0x0f, 0xf6, 0x02, 0x8d, 0x6a, 0x65, 0x2d, 0x6f,
- 0x67, 0x2d, 0x68, 0x6f, 0x72, 0xee, 0xe0, 0x2d, 0x15, 0x65, 0xee, 0x07,
- 0x0c, 0x60, 0x28, 0x0d, 0xc4, 0xf1, 0x1f, 0x43, 0x61, 0x1f, 0x45, 0x61,
- 0x1f, 0x45, 0xe1, 0xe0, 0x29, 0xb0, 0x61, 0x73, 0xf3, 0xe0, 0x29, 0xaa,
- 0xf5, 0x09, 0x0f, 0x06, 0x32, 0x60, 0x2e, 0x59, 0xc1, 0xd3, 0x72, 0xef,
- 0x02, 0x86, 0x76, 0x69, 0xf3, 0xe0, 0x24, 0x69, 0x64, 0x69, 0xf2, 0xde,
- 0x80, 0x6e, 0x2e, 0xe5, 0xe0, 0x30, 0x60, 0xae, 0x0a, 0x12, 0x09, 0x03,
- 0x5f, 0xc9, 0x4e, 0x2a, 0xc2, 0x3b, 0xf0, 0x04, 0xe0, 0x20, 0x07, 0x79,
- 0x74, 0x68, 0x6f, 0x6e, 0x61, 0x6e, 0x79, 0x77, 0x68, 0xe5, 0xd6, 0x19,
- 0x6d, 0x65, 0x74, 0x65, 0x6f, 0xf2, 0xe0, 0x2f, 0xdd, 0xe9, 0xc7, 0xea,
- 0x65, 0x6e, 0x63, 0x6f, 0x77, 0x61, 0xf9, 0xd8, 0xbf, 0xad, 0x07, 0x05,
- 0x02, 0x02, 0x02, 0xd8, 0xb2, 0x77, 0xe5, 0xe0, 0x27, 0xde, 0xb4, 0x86,
- 0xb3, 0x84, 0xb2, 0x82, 0x31, 0x2e, 0x65, 0x76, 0x65, 0x6e, 0xee, 0xd9,
- 0xe0, 0xf4, 0x08, 0x07, 0x0c, 0x05, 0x04, 0xe0, 0x2f, 0xeb, 0x6e, 0xe5,
- 0x60, 0x29, 0xce, 0xc5, 0x43, 0xe9, 0x04, 0xe0, 0x2f, 0x42, 0x73, 0x61,
- 0x6c, 0xe1, 0xe0, 0x2e, 0xd3, 0x68, 0xee, 0xe0, 0x28, 0x1c, 0xe3, 0xe0,
- 0x2f, 0x35, 0x61, 0x6a, 0x69, 0xed, 0xca, 0xa8, 0xf3, 0x0b, 0x38, 0x06,
- 0x04, 0x09, 0x09, 0x4e, 0xcd, 0xe0, 0x20, 0xb8, 0xf4, 0x03, 0x04, 0x85,
- 0x61, 0xf4, 0xde, 0x36, 0x2e, 0xf0, 0xe0, 0x2f, 0x18, 0xad, 0x03, 0x0c,
- 0x88, 0x6d, 0x6f, 0x6e, 0x2d, 0x62, 0x6c, 0x6f, 0x67, 0x75, 0xe5, 0xc5,
- 0x6e, 0x6c, 0x65, 0x2d, 0x70, 0x61, 0x74, 0xf2, 0x8f, 0x61, 0x2d, 0x6c,
- 0x61, 0x2d, 0x6d, 0xe1, 0x02, 0x83, 0x73, 0xe9, 0x83, 0x69, 0x73, 0x6f,
- 0xee, 0xe0, 0x2f, 0x9c, 0x73, 0x65, 0xf8, 0xe0, 0x2d, 0x74, 0xf0, 0xe0,
- 0x2e, 0xe1, 0xe1, 0x04, 0xe0, 0x2a, 0x66, 0xee, 0xe0, 0x2e, 0x78, 0xae,
- 0x59, 0x0f, 0x46, 0x12, 0x4b, 0x00, 0x03, 0x83, 0x2d, 0x31, 0x2e, 0x61,
- 0x78, 0x61, 0x72, 0x6e, 0x65, 0xf4, 0xd8, 0x0c, 0xf2, 0x06, 0x0c, 0x52,
- 0xc8, 0xcc, 0x3c, 0x6f, 0x74, 0xe9, 0x02, 0x82, 0xeb, 0x82, 0x63, 0xe1,
- 0xe0, 0x2e, 0xff, 0xe9, 0x02, 0x84, 0xed, 0xe0, 0x24, 0x78, 0x63, 0xf3,
- 0x91, 0x71, 0x75, 0x69, 0x70, 0x6d, 0x65, 0x6e, 0xf4, 0x60, 0x2d, 0x04,
- 0xc2, 0x50, 0xf0, 0x02, 0x84, 0xf3, 0xe0, 0x28, 0xd2, 0x69, 0x6c, 0x65,
- 0x70, 0xf3, 0xe0, 0x27, 0x71, 0xee, 0x10, 0x19, 0x28, 0x12, 0x04, 0x24,
- 0x04, 0x0e, 0x20, 0x19, 0x04, 0x60, 0x27, 0x3a, 0xc5, 0xc1, 0x76, 0x69,
- 0x72, 0x6f, 0x6e, 0x6d, 0x65, 0x6e, 0xf4, 0x04, 0xe0, 0x2c, 0xf8, 0x61,
- 0x6c, 0x63, 0x6f, 0x6e, 0x73, 0x65, 0x72, 0xf6, 0xe0, 0x25, 0x0a, 0xf4,
- 0x03, 0x05, 0x9c, 0x6f, 0xed, 0xe0, 0x27, 0x3a, 0x65, 0xf2, 0x02, 0x87,
- 0x74, 0x61, 0x69, 0xee, 0xe0, 0x28, 0x0f, 0x70, 0x72, 0x69, 0x73, 0xe5,
- 0x04, 0xe0, 0x2d, 0x26, 0x63, 0x6c, 0x6f, 0x75, 0x64, 0xae, 0xdf, 0xa8,
- 0xae, 0xde, 0xa8, 0xf3, 0x02, 0x8a, 0x63, 0x61, 0x6c, 0x65, 0x64, 0x2e,
- 0xf3, 0xe0, 0x2c, 0xa4, 0x2e, 0xf4, 0xe0, 0x2d, 0x41, 0xe9, 0xe0, 0x24,
- 0x57, 0xe7, 0x04, 0x06, 0x0f, 0x85, 0x6c, 0x61, 0xee, 0xe0, 0x2a, 0x89,
- 0x69, 0x6e, 0xe5, 0x04, 0xe0, 0x2c, 0x76, 0x65, 0xf2, 0x5a, 0x20, 0x52,
- 0x54, 0xc2, 0x50, 0x65, 0xf2, 0xe0, 0x28, 0x85, 0xae, 0x60, 0x2d, 0xcc,
- 0xb4, 0xe6, 0xe0, 0x2d, 0xfa, 0xe5, 0x02, 0x85, 0x72, 0xe7, 0xe0, 0x2d,
- 0x0a, 0x62, 0x61, 0xeb, 0xe0, 0x25, 0x06, 0x64, 0x6f, 0xe6, 0x02, 0x8d,
- 0x74, 0x68, 0x65, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0xe5, 0xe0, 0x25,
- 0xe1, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0xae, 0x60, 0x2c,
- 0x45, 0xc2, 0x1f, 0xe3, 0x03, 0x09, 0x84, 0x79, 0x63, 0x6c, 0x6f, 0x70,
- 0x65, 0xe4, 0xd4, 0x46, 0xf2, 0xe0, 0x2d, 0x9f, 0x6f, 0x72, 0x65, 0x61,
- 0xf0, 0xe0, 0x25, 0x94, 0xe1, 0xe0, 0x26, 0xa2, 0x2d, 0x72, 0x6f, 0x6f,
- 0xf4, 0xd2, 0xb5, 0xed, 0x06, 0x04, 0x0b, 0x0d, 0x10, 0x9b, 0xf2, 0xe0,
- 0x2c, 0xec, 0xf0, 0x04, 0xe0, 0x2d, 0x95, 0x72, 0x65, 0xf3, 0xe0, 0x2c,
- 0x2a, 0x69, 0x6c, 0x69, 0xe1, 0x02, 0x81, 0x2d, 0x72, 0x6f, 0x6d, 0xe1,
- 0xd9, 0xc0, 0x65, 0xf2, 0x02, 0x88, 0x67, 0x65, 0x6e, 0x63, 0xf9, 0xe0,
- 0x2b, 0xe2, 0xe3, 0xe0, 0x2c, 0x5e, 0xe2, 0x06, 0x07, 0x08, 0xe0, 0x2a,
- 0x2b, 0x72, 0x6f, 0x69, 0xe4, 0xe0, 0x24, 0x04, 0x61, 0x69, 0x78, 0x61,
- 0x64, 0xe1, 0xcb, 0xf9, 0x2e, 0xeb, 0xe0, 0x2c, 0x39, 0x61, 0xe9, 0xe0,
- 0x2c, 0x56, 0xec, 0x07, 0x0d, 0x0f, 0x5c, 0x5f, 0xc9, 0x52, 0x76, 0xe5,
- 0x03, 0xd6, 0xe9, 0x6e, 0x64, 0x72, 0x65, 0xec, 0xe0, 0x24, 0x76, 0x65,
- 0x6d, 0x65, 0x6e, 0x74, 0x6f, 0x72, 0x2e, 0xe3, 0x03, 0xd6, 0x7d, 0xef,
- 0xc7, 0xdb, 0xe2, 0x04, 0x06, 0xd1, 0xb2, 0x6c, 0x61, 0xe7, 0xe0, 0x29,
- 0xf2, 0xae, 0xcc, 0x1b, 0x6b, 0x6c, 0x6f, 0x67, 0x65, 0x73, 0xae, 0xe0,
- 0x2a, 0x40, 0xe9, 0x04, 0x09, 0x07, 0x82, 0x73, 0x65, 0x6e, 0x62, 0x61,
- 0xe8, 0xe0, 0x23, 0xce, 0x68, 0x65, 0x69, 0x6a, 0xe9, 0xc1, 0xec, 0xe7,
- 0xae, 0xe4, 0x05, 0x5f, 0x32, 0xcd, 0x8c, 0xf3, 0x05, 0x05, 0xe0, 0x22,
- 0xa4, 0x6b, 0xef, 0xe0, 0x27, 0xa0, 0x62, 0x65, 0xf2, 0xe0, 0x27, 0x9a,
- 0xe7, 0x06, 0x08, 0x07, 0xe0, 0x2d, 0x8b, 0x79, 0x70, 0x74, 0x69, 0xe1,
- 0xe0, 0x23, 0x9f, 0x6f, 0x69, 0x73, 0xed, 0xe0, 0x25, 0xf6, 0x65, 0xf2,
- 0xd7, 0xe3, 0xe5, 0x06, 0x60, 0x21, 0x80, 0xcc, 0x00, 0x72, 0xef, 0x02,
- 0x86, 0x2d, 0x73, 0x74, 0x61, 0x67, 0x65, 0x2e, 0xef, 0xde, 0x17, 0xe4,
- 0x07, 0x41, 0x2b, 0x07, 0x08, 0x19, 0x85, 0xf5, 0x06, 0x04, 0x12, 0xe0,
- 0x2d, 0x4d, 0x67, 0xe9, 0xdb, 0x59, 0x63, 0x61, 0xf4, 0x02, 0x83, 0xef,
- 0xd2, 0xa0, 0x69, 0x6f, 0xee, 0x60, 0x23, 0xd0, 0x47, 0x5a, 0xc2, 0x2a,
- 0xae, 0x1c, 0x06, 0x0a, 0x06, 0x0a, 0x1c, 0x06, 0x10, 0x08, 0x14, 0x10,
- 0x12, 0x08, 0x07, 0x10, 0x04, 0x0e, 0x07, 0x0c, 0x10, 0x4b, 0x04, 0x04,
- 0x3a, 0x40, 0x48, 0xda, 0xb1, 0xfa, 0x60, 0x2b, 0x74, 0xc0, 0xb5, 0xf6,
- 0x60, 0x21, 0xe5, 0x49, 0xa6, 0x40, 0xc3, 0xc0, 0x73, 0xf5, 0x60, 0x2b,
- 0x7e, 0xc0, 0x9b, 0xf4, 0x60, 0x2b, 0x3f, 0x1f, 0x11, 0x40, 0x86, 0x3a,
- 0xb3, 0xf3, 0x12, 0x60, 0x23, 0xa9, 0x46, 0x46, 0x41, 0x3b, 0x1b, 0x03,
- 0x14, 0x03, 0x40, 0x7a, 0x1e, 0x1c, 0xc0, 0xe9, 0xe3, 0x04, 0xe0, 0x2c,
- 0xf8, 0xef, 0xe0, 0x2c, 0xd6, 0xf2, 0x60, 0x2b, 0x20, 0xc0, 0x84, 0xf0,
- 0x60, 0x22, 0xca, 0x48, 0x50, 0x03, 0x18, 0x17, 0x03, 0x40, 0x7a, 0x19,
- 0x05, 0x2b, 0xa4, 0xee, 0x60, 0x2b, 0x5d, 0x40, 0xc9, 0xc0, 0xb6, 0xed,
- 0x60, 0x26, 0x16, 0x43, 0xb1, 0x41, 0x31, 0x03, 0x07, 0x03, 0x18, 0x17,
- 0x03, 0x40, 0x7a, 0x3a, 0x0f, 0xc0, 0xda, 0xec, 0x60, 0x21, 0x7d, 0x41,
- 0xf0, 0x46, 0x46, 0x41, 0x3b, 0x03, 0x2f, 0x40, 0x9b, 0xc0, 0x4f, 0xeb,
- 0x0d, 0x60, 0x2a, 0xc7, 0x03, 0x1f, 0x1a, 0x03, 0x1e, 0x40, 0x84, 0xc0,
- 0xfb, 0xf2, 0xe0, 0x21, 0x02, 0xe9, 0x4b, 0xe7, 0x5e, 0xe5, 0x35, 0xc0,
- 0x7a, 0xe8, 0x60, 0x2a, 0xc7, 0x32, 0xc0, 0x7a, 0xe7, 0x60, 0x2a, 0xd8,
- 0x03, 0x14, 0x03, 0x1e, 0x40, 0x5c, 0x19, 0x0f, 0x21, 0x24, 0xc0, 0x4f,
- 0xe6, 0xe0, 0x2a, 0xc5, 0xe5, 0x60, 0x21, 0x38, 0x45, 0xdf, 0x43, 0x92,
- 0x40, 0xaf, 0x40, 0x49, 0xc0, 0xda, 0xe4, 0x60, 0x2a, 0x91, 0x22, 0xc0,
- 0xd1, 0xe3, 0x60, 0x29, 0x59, 0x41, 0x34, 0x3c, 0x1e, 0x40, 0x96, 0xc0,
- 0x82, 0xe2, 0x60, 0x23, 0x07, 0x47, 0x77, 0x0a, 0x18, 0x1d, 0x1e, 0x40,
- 0x5c, 0x19, 0x05, 0x1c, 0xb3, 0xe1, 0x60, 0x21, 0x07, 0x41, 0x21, 0x48,
- 0x46, 0x25, 0x41, 0x01, 0xc0, 0x4f, 0x6f, 0x67, 0x61, 0xf7, 0xe0, 0x27,
- 0x27, 0x69, 0x74, 0x6f, 0x72, 0xf8, 0xe0, 0x2b, 0xc2, 0x67, 0xe5, 0x03,
- 0x07, 0x88, 0x73, 0x74, 0x61, 0x63, 0xeb, 0xd6, 0xd9, 0x63, 0x6f, 0x6d,
- 0x70, 0x75, 0xf4, 0xd7, 0x77, 0x61, 0xf0, 0xe0, 0x20, 0x85, 0x65, 0xeb,
- 0xe0, 0x2b, 0x0f, 0xae, 0x06, 0x06, 0x5b, 0x45, 0xcf, 0xb1, 0xe3, 0x60,
- 0x2a, 0x8a, 0xc0, 0xc9, 0xe1, 0xe0, 0x2b, 0x1a, 0xe3, 0x06, 0x21, 0x04,
- 0xe0, 0x2b, 0xd5, 0xef, 0x08, 0x06, 0x0c, 0x60, 0x2b, 0x26, 0xc0, 0xb9,
- 0x6e, 0x6f, 0xed, 0xe0, 0x29, 0xcc, 0x6d, 0x6d, 0x65, 0x72, 0x63, 0x65,
- 0x2d, 0x73, 0x68, 0xef, 0xdb, 0x37, 0x6c, 0x6f, 0xe7, 0xe0, 0x29, 0xba,
- 0xee, 0xe0, 0x2b, 0x1f, 0x68, 0x69, 0x7a, 0x65, 0x6e, 0x2e, 0x66, 0x75,
- 0x6b, 0xf5, 0xe0, 0x2a, 0x57, 0xe2, 0x04, 0xe0, 0x27, 0xd9, 0xe9, 0x02,
- 0x84, 0x7a, 0xae, 0xcd, 0x1a, 0xee, 0x04, 0xe0, 0x27, 0xc1, 0xef, 0xde,
- 0xa6, 0xe1, 0x05, 0x19, 0xe0, 0x2a, 0x88, 0xf4, 0x05, 0x05, 0xe0, 0x2b,
- 0xa3, 0x6f, 0xee, 0xe0, 0x24, 0x02, 0x69, 0x6e, 0x67, 0x2d, 0x6f, 0x72,
- 0x67, 0x61, 0x6e, 0x69, 0xe3, 0xe0, 0x2b, 0x74, 0x73, 0xf4, 0x04, 0x05,
- 0x04, 0x8f, 0x75, 0x73, 0xb2, 0xd1, 0x50, 0x63, 0xef, 0xd5, 0xf6, 0xe1,
- 0x02, 0x85, 0x73, 0x69, 0xe1, 0xd1, 0x44, 0x66, 0x72, 0x69, 0xe3, 0xe0,
- 0x26, 0x01, 0x2d, 0x6b, 0x61, 0x7a, 0x61, 0x6b, 0x68, 0x73, 0xf4, 0xde,
- 0x41, 0xb4, 0xc7, 0x27, 0xb1, 0x02, 0x88, 0x36, 0x34, 0x2e, 0x61, 0xf2,
- 0xe0, 0x2a, 0x04, 0x32, 0xae, 0xe0, 0x21, 0x35, 0xae, 0x5a, 0xf3, 0xd0,
- 0x63, 0xe4, 0x22, 0x07, 0x40, 0xec, 0x0a, 0x12, 0x21, 0x04, 0x1b, 0x40,
- 0x4f, 0x03, 0x40, 0x6a, 0x40, 0x57, 0x09, 0x05, 0x40, 0xf3, 0x11, 0x06,
- 0x06, 0x41, 0xa6, 0x38, 0x05, 0x40, 0xc6, 0x60, 0x23, 0x2b, 0x22, 0x91,
- 0x1f, 0x43, 0x78, 0xee, 0xe0, 0x21, 0x53, 0xf9, 0x05, 0x44, 0x7c, 0xd9,
- 0xf7, 0xee, 0x0b, 0x08, 0x04, 0x07, 0x40, 0x6a, 0x1c, 0x05, 0x1e, 0xc0,
- 0xb6, 0xf6, 0x03, 0xc0, 0xbe, 0xb6, 0xe0, 0x2a, 0xef, 0xf5, 0xe0, 0x2a,
- 0xeb, 0x73, 0x65, 0x72, 0xf6, 0xe0, 0x28, 0xc5, 0x64, 0x6e, 0xf3, 0x03,
- 0x03, 0x99, 0xb1, 0xc6, 0xa3, 0xae, 0x0b, 0x4c, 0x98, 0x40, 0x60, 0x4c,
- 0xae, 0x42, 0x30, 0xcc, 0xd8, 0xe4, 0x03, 0xc0, 0x85, 0x61, 0x70, 0x70,
- 0x6e, 0x6f, 0xe4, 0xe0, 0x22, 0x55, 0xad, 0x0e, 0x0a, 0x07, 0x06, 0x08,
- 0x06, 0x05, 0x09, 0x42, 0x06, 0x42, 0xb4, 0xc0, 0x94, 0xf7, 0x04, 0x2b,
- 0xc5, 0x13, 0x69, 0xeb, 0xe0, 0x21, 0xf5, 0x72, 0x65, 0x6d, 0xef, 0xe0,
- 0x23, 0xe7, 0x70, 0x69, 0xe3, 0xe0, 0x2a, 0x3d, 0x6f, 0x66, 0x66, 0x69,
- 0xe3, 0xe0, 0x23, 0xda, 0x6d, 0x61, 0xe9, 0xe0, 0x24, 0xc0, 0x62, 0x6c,
- 0xef, 0xde, 0x44, 0x61, 0x74, 0xad, 0x02, 0x84, 0x77, 0xef, 0xd4, 0x5c,
- 0x68, 0x6f, 0xed, 0xe0, 0x23, 0xc0, 0xe1, 0x04, 0x05, 0xc1, 0xaa, 0x74,
- 0x68, 0xef, 0xd5, 0xac, 0x6d, 0xe9, 0x02, 0x87, 0x73, 0x63, 0x68, 0x65,
- 0xf3, 0xc4, 0xaf, 0x63, 0x2d, 0x64, 0x6e, 0xf3, 0xdb, 0x66, 0x35, 0xb3,
- 0xe0, 0x2a, 0x0c, 0xae, 0x03, 0x0e, 0x86, 0x68, 0x6f, 0x6d, 0x65, 0x2d,
- 0x77, 0x65, 0x62, 0x73, 0x65, 0x72, 0xf6, 0xc6, 0x0d, 0x64, 0x64, 0x6e,
- 0xf3, 0xc4, 0x8e, 0x63, 0x6f, 0x73, 0xe9, 0xc4, 0x86, 0xad, 0x04, 0x03,
- 0x08, 0x86, 0x76, 0xf0, 0x94, 0x6f, 0x2d, 0x73, 0x61, 0xf5, 0xe0, 0x21,
- 0x85, 0x69, 0x70, 0x32, 0xb4, 0xc5, 0xed, 0x62, 0x65, 0x72, 0x6c, 0x69,
- 0xee, 0xc5, 0xe5, 0x77, 0x65, 0x62, 0x2e, 0x6c, 0x69, 0xee, 0xe0, 0x24,
- 0xa2, 0xf6, 0x02, 0x8b, 0xf2, 0x05, 0x23, 0xe0, 0x2a, 0x08, 0x63, 0x61,
- 0xed, 0xdb, 0x0d, 0xe1, 0xe0, 0x2a, 0x24, 0xf5, 0x05, 0x08, 0x04, 0x06,
- 0x86, 0xf2, 0x03, 0xcf, 0x3d, 0xe2, 0xe0, 0x26, 0xa7, 0x70, 0xef, 0xc1,
- 0xc5, 0x6e, 0x6c, 0xef, 0xe0, 0x29, 0x11, 0x63, 0x6b, 0x64, 0xee, 0xd6,
- 0x05, 0xe2, 0xce, 0x72, 0xf4, 0xe0, 0x26, 0xf2, 0xf3, 0x03, 0x04, 0x85,
- 0xf4, 0xe0, 0x22, 0x54, 0x6d, 0x79, 0xee, 0xc1, 0x0d, 0x63, 0x6c, 0x6f,
- 0x75, 0x64, 0xae, 0x03, 0xcb, 0x8f, 0xed, 0x5a, 0x92, 0xcf, 0x17, 0xf2,
- 0x08, 0x04, 0x07, 0x06, 0x06, 0x03, 0x0c, 0x9b, 0x1f, 0x43, 0xf8, 0x8f,
- 0x75, 0x64, 0xae, 0x4b, 0x6a, 0xdd, 0xf8, 0x72, 0x2e, 0xe1, 0xe0, 0x29,
- 0xbc, 0x6f, 0x62, 0xe1, 0xe0, 0x20, 0x26, 0xe9, 0xdf, 0x9a, 0x65, 0x61,
- 0x6d, 0x68, 0x6f, 0x73, 0x74, 0x65, 0xf2, 0xe0, 0x29, 0x33, 0xe1, 0x03,
- 0x0d, 0x86, 0xf9, 0x03, 0xc3, 0xd2, 0xe4, 0x03, 0xc3, 0xd0, 0x64, 0xee,
- 0xe0, 0x29, 0x22, 0x6e, 0x67, 0xe5, 0xe0, 0x23, 0x68, 0x6d, 0xed, 0xdb,
- 0x44, 0xae, 0x5c, 0xf4, 0xc9, 0x68, 0xf0, 0xce, 0xaf, 0xef, 0x10, 0x08,
- 0x05, 0x06, 0x06, 0x06, 0x17, 0x03, 0x05, 0x12, 0x60, 0x28, 0x0b, 0x41,
- 0x23, 0x81, 0x77, 0x6e, 0x6c, 0x6f, 0xe1, 0xe0, 0x27, 0xc5, 0x76, 0xf2,
- 0xe0, 0x28, 0x80, 0x73, 0x68, 0x69, 0xae, 0xcd, 0x0f, 0x70, 0x61, 0xe1,
- 0xe0, 0x28, 0xe6, 0x6f, 0x6d, 0x64, 0x6e, 0xf3, 0xac, 0xee, 0x05, 0x08,
- 0x05, 0xdf, 0x73, 0x74, 0x65, 0x78, 0x69, 0x73, 0xf4, 0xd6, 0x7b, 0x6f,
- 0x73, 0xf4, 0xdc, 0xfb, 0x65, 0xf4, 0xc0, 0x78, 0xed, 0xda, 0x50, 0x6c,
- 0xec, 0xe0, 0x22, 0xb0, 0x65, 0xf3, 0x03, 0xd3, 0x9e, 0x6e, 0x74, 0x65,
- 0x78, 0x69, 0x73, 0x74, 0xae, 0x60, 0x26, 0xf2, 0xc2, 0x3b, 0xe3, 0x04,
- 0xe0, 0x27, 0x57, 0xf4, 0xe0, 0x26, 0x93, 0xee, 0x07, 0x3c, 0x05, 0x4d,
- 0xfa, 0xd9, 0xe7, 0xf3, 0x07, 0x0e, 0x07, 0x08, 0x06, 0x05, 0x86, 0x75,
- 0xf0, 0x04, 0xe0, 0x28, 0xec, 0x64, 0x61, 0x74, 0xe5, 0x44, 0xb0, 0xd5,
- 0x41, 0x6b, 0x69, 0x6e, 0xe7, 0xe0, 0x23, 0x27, 0x69, 0x73, 0x6b, 0x69,
- 0x6e, 0xeb, 0xcb, 0x18, 0x68, 0x6f, 0x6d, 0xe5, 0xc4, 0x9a, 0x66, 0x6f,
- 0xf2, 0xd3, 0xa3, 0x64, 0x6f, 0x6a, 0xef, 0xd6, 0x11, 0x61, 0x6c, 0x69,
- 0xe1, 0xd6, 0x0a, 0xe9, 0x05, 0xe0, 0x23, 0xfc, 0x65, 0x70, 0x72, 0x6f,
- 0x70, 0x65, 0x74, 0x72, 0x6f, 0x76, 0x73, 0xeb, 0xcd, 0xee, 0x6c, 0x75,
- 0x67, 0x6f, 0x6c, 0x65, 0xeb, 0xdb, 0x04, 0xeb, 0x5c, 0xc5, 0xcc, 0x00,
- 0xe9, 0x0d, 0x11, 0x0b, 0x40, 0x5d, 0x1e, 0x08, 0x0f, 0x1a, 0x11, 0xe0,
- 0x26, 0x39, 0x76, 0xf4, 0x02, 0x85, 0x74, 0x61, 0xf3, 0xda, 0x8c, 0x61,
- 0x73, 0x76, 0x75, 0x6f, 0xe4, 0xde, 0xca, 0x74, 0x63, 0x68, 0x79, 0x6f,
- 0x75, 0x72, 0xe9, 0xe0, 0x28, 0x3e, 0xf3, 0x05, 0x21, 0xe0, 0x27, 0x65,
- 0xeb, 0x02, 0x90, 0x75, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x62, 0x65,
- 0x72, 0x65, 0x69, 0x63, 0xe8, 0xc4, 0x25, 0x73, 0x74, 0x61, 0x74, 0x69,
- 0x6f, 0x6e, 0xae, 0x53, 0x2a, 0x50, 0xfa, 0xc2, 0x10, 0x63, 0xef, 0x03,
- 0x08, 0x9a, 0x76, 0x65, 0xf2, 0x60, 0x20, 0x96, 0xc7, 0xd2, 0xf5, 0x02,
- 0x93, 0x72, 0x73, 0x65, 0xae, 0x02, 0x86, 0x74, 0x65, 0xe1, 0xe0, 0x28,
- 0x52, 0x67, 0x72, 0x6f, 0xf5, 0xe0, 0x27, 0x7e, 0xee, 0xe0, 0x27, 0x28,
- 0x72, 0x64, 0xf3, 0x02, 0x85, 0x65, 0xfa, 0xe0, 0x28, 0x3a, 0x61, 0xf9,
- 0xe0, 0x27, 0xba, 0x72, 0x65, 0x63, 0xf4, 0x05, 0x03, 0xe0, 0x28, 0x2e,
- 0xef, 0xc1, 0xb7, 0x2e, 0x71, 0x75, 0x69, 0x63, 0x6b, 0x63, 0x6f, 0x6e,
- 0x6e, 0x65, 0x63, 0x74, 0xae, 0x49, 0x60, 0xde, 0x1d, 0x6e, 0x6f, 0x73,
- 0x61, 0xf5, 0xe0, 0x21, 0x94, 0x68, 0x65, 0x72, 0x2e, 0x73, 0x6f, 0x6c,
- 0x75, 0x74, 0x69, 0x6f, 0xee, 0xe0, 0x25, 0x25, 0x67, 0xe9, 0x02, 0x93,
- 0x74, 0x61, 0xec, 0x04, 0xe0, 0x27, 0xfa, 0x6f, 0x63, 0x65, 0x61, 0x6e,
- 0x73, 0x70, 0x61, 0x63, 0xe5, 0xc6, 0x50, 0xe3, 0xda, 0x1a, 0xe5, 0x04,
- 0xe0, 0x26, 0xc2, 0x6c, 0x64, 0x64, 0x61, 0x6e, 0x75, 0x6f, 0x72, 0xf2,
- 0xe0, 0x21, 0x07, 0xe1, 0x02, 0x85, 0x6d, 0x6f, 0xee, 0xcf, 0x49, 0x64,
- 0xe5, 0xd0, 0x60, 0xe8, 0x04, 0xe0, 0x26, 0x11, 0x2e, 0x62, 0x79, 0x74,
- 0x65, 0x6d, 0x61, 0x72, 0xeb, 0xe0, 0x25, 0x06, 0x67, 0x63, 0xe1, 0xe0,
- 0x25, 0x6a, 0x66, 0xae, 0x60, 0x22, 0x49, 0x83, 0xe5, 0x12, 0x40, 0x6e,
- 0x13, 0x16, 0x10, 0x21, 0x32, 0x35, 0x05, 0x10, 0x0e, 0x11, 0x05, 0x0f,
- 0xe0, 0x26, 0x27, 0xf6, 0x08, 0x14, 0x13, 0x0d, 0x23, 0xe0, 0x27, 0x3e,
- 0x69, 0x63, 0x65, 0x73, 0x2e, 0x72, 0x65, 0x73, 0x69, 0x6e, 0x73, 0x74,
- 0x61, 0x67, 0x69, 0x6e, 0xe7, 0xe0, 0x27, 0x13, 0x65, 0x6c, 0x6f, 0xf0,
- 0x02, 0x89, 0x6d, 0x65, 0x6e, 0x74, 0x2e, 0x72, 0xf5, 0xd4, 0x0f, 0x65,
- 0xf2, 0xd6, 0xbb, 0x63, 0x64, 0x6e, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73,
- 0xef, 0xe0, 0x27, 0x4e, 0xae, 0x06, 0x0c, 0x54, 0x80, 0xd2, 0x16, 0x73,
- 0x74, 0x61, 0x74, 0x69, 0x63, 0x2e, 0x6c, 0x61, 0xee, 0xdb, 0xaf, 0x61,
- 0x64, 0x6f, 0x62, 0x65, 0x61, 0x65, 0x6d, 0x63, 0x6c, 0x6f, 0x75, 0xe4,
- 0xe0, 0x27, 0x2b, 0xad, 0x04, 0xe0, 0x21, 0x4b, 0x62, 0x75, 0x69, 0x6c,
- 0x64, 0x65, 0xf2, 0xd0, 0xfe, 0xf4, 0x05, 0x06, 0xe0, 0x26, 0x6b, 0x72,
- 0x6f, 0xe9, 0xe0, 0x20, 0x8a, 0x61, 0xae, 0x43, 0x1b, 0xe0, 0x23, 0x33,
- 0xf3, 0x05, 0x0c, 0xe0, 0x26, 0x52, 0xe9, 0x04, 0xe0, 0x27, 0x12, 0x67,
- 0xee, 0x60, 0x20, 0x88, 0xc6, 0x88, 0x61, 0xae, 0xdf, 0x85, 0xf0, 0x04,
- 0xe0, 0x26, 0x0e, 0xef, 0x04, 0xe0, 0x20, 0x60, 0x72, 0x74, 0xe5, 0xe0,
- 0x24, 0xda, 0xee, 0x03, 0x0a, 0x8e, 0xf4, 0x02, 0x83, 0xe9, 0xda, 0xa6,
- 0xe1, 0xe0, 0x25, 0x34, 0xef, 0x03, 0xc2, 0xdd, 0x2d, 0x73, 0x74, 0x61,
- 0x67, 0x69, 0x6e, 0xe7, 0xc2, 0xd5, 0x6d, 0x61, 0xf2, 0xdf, 0x22, 0x6d,
- 0xef, 0x04, 0x07, 0xca, 0x0f, 0x63, 0x72, 0xe1, 0x4a, 0x49, 0xdb, 0x60,
- 0xae, 0x02, 0x8b, 0x6a, 0x65, 0x6c, 0x61, 0x73, 0x74, 0x69, 0xe3, 0xe0,
- 0x26, 0xb6, 0x64, 0x61, 0x74, 0xe1, 0x02, 0x87, 0x64, 0x65, 0x74, 0x65,
- 0xe3, 0xdd, 0xac, 0x63, 0x65, 0x6e, 0x74, 0x65, 0x72, 0x2e, 0xe6, 0xd7,
- 0x54, 0xec, 0x07, 0x06, 0x08, 0x12, 0x07, 0xda, 0xbd, 0x6f, 0x69, 0xf4,
- 0xe0, 0x20, 0xc7, 0x6d, 0x65, 0x6e, 0x68, 0x6f, 0xf2, 0xd0, 0xff, 0xec,
- 0x05, 0x01, 0xe0, 0x26, 0x87, 0x2d, 0x6f, 0x67, 0x6c, 0x69, 0x61, 0x73,
- 0x74, 0xf2, 0xe0, 0x20, 0xdd, 0x69, 0x76, 0x65, 0xf2, 0xe0, 0x24, 0xd7,
- 0x61, 0x77, 0xe1, 0xe0, 0x22, 0xca, 0x67, 0xf2, 0xe0, 0x22, 0x5d, 0xe6,
- 0x04, 0xe0, 0x25, 0xac, 0x69, 0x6e, 0x69, 0x6d, 0x61, 0xae, 0x60, 0x25,
- 0xef, 0xc0, 0x4d, 0xe4, 0x02, 0x85, 0x79, 0xee, 0xe0, 0x25, 0xe4, 0x69,
- 0x62, 0x6f, 0xf8, 0xca, 0xa3, 0xe3, 0x04, 0x09, 0xd7, 0xac, 0x6f, 0x72,
- 0x61, 0x74, 0x69, 0x76, 0xe5, 0xd5, 0xb4, 0xe9, 0xde, 0xa5, 0x62, 0x69,
- 0xe1, 0xd3, 0x93, 0xe1, 0x02, 0x85, 0x74, 0xee, 0xe0, 0x20, 0xb2, 0xec,
- 0x53, 0x72, 0x50, 0xe8, 0xc1, 0xd3, 0xae, 0x0b, 0x03, 0x04, 0x53, 0x4c,
- 0x47, 0x26, 0x46, 0x3c, 0xc1, 0x30, 0xf4, 0xcb, 0x94, 0xec, 0xe0, 0x25,
- 0xcf, 0x63, 0xef, 0x04, 0xe0, 0x26, 0x09, 0xef, 0xdc, 0x28, 0xe4, 0x06,
- 0x2b, 0x5f, 0x56, 0xc4, 0xb0, 0x6e, 0xf3, 0x07, 0x07, 0x05, 0x03, 0x05,
- 0x05, 0x84, 0x73, 0xae, 0x60, 0x23, 0xb7, 0xc2, 0x04, 0x6c, 0x69, 0xf6,
- 0xdf, 0x13, 0xeb, 0xd9, 0x84, 0x67, 0x65, 0xe5, 0xdd, 0xc0, 0x66, 0x72,
- 0xe5, 0xdf, 0x06, 0xb5, 0xe0, 0x25, 0xd9, 0xae, 0x50, 0x91, 0xd5, 0x29,
- 0x2d, 0x64, 0x6e, 0xf3, 0xc1, 0x7a, 0xe3, 0x5a, 0x9f, 0xc6, 0x53, 0xe1,
- 0x10, 0x07, 0x18, 0x3c, 0x09, 0x04, 0x08, 0x08, 0x17, 0x09, 0x18, 0x56,
- 0x7b, 0x4c, 0xdd, 0x94, 0x7a, 0x61, 0x69, 0x66, 0xf5, 0xcf, 0x16, 0x76,
- 0x76, 0xe5, 0x02, 0x87, 0x73, 0x69, 0x69, 0xe4, 0xe0, 0x20, 0x7c, 0x6e,
- 0xea, 0x02, 0x82, 0x1f, 0x43, 0x61, 0x72, 0xe7, 0xe0, 0x20, 0x70, 0xf4,
- 0x06, 0x17, 0x05, 0x0b, 0xd0, 0xcc, 0x74, 0xef, 0x03, 0x04, 0x86, 0x77,
- 0xe5, 0xdd, 0x0a, 0x72, 0x65, 0x6c, 0xe1, 0xc7, 0xa7, 0x6c, 0x6f, 0x63,
- 0x61, 0xec, 0xe0, 0x22, 0xbf, 0x73, 0xf5, 0xe0, 0x23, 0xe0, 0xe5, 0x04,
- 0xe0, 0x25, 0x76, 0xae, 0x60, 0x20, 0xce, 0xc3, 0x8f, 0xe1, 0x04, 0xe0,
- 0x25, 0x6b, 0x62, 0x61, 0x73, 0x65, 0xae, 0x4f, 0x39, 0xd4, 0x04, 0xf0,
- 0x03, 0xce, 0x7d, 0x6c, 0x69, 0xe5, 0xd0, 0x0e, 0xee, 0xe0, 0x21, 0x17,
- 0x6d, 0x6e, 0x73, 0x65, 0x72, 0xf6, 0xdc, 0x84, 0xec, 0x03, 0xda, 0x1b,
- 0x6c, 0xe1, 0xde, 0xb3, 0xe9, 0x04, 0x04, 0x04, 0x87, 0x77, 0xe1, 0xda,
- 0x9b, 0xf4, 0xe0, 0x23, 0xff, 0x73, 0x65, 0x6e, 0xae, 0xe0, 0x20, 0x17,
- 0xe7, 0xd6, 0x7c, 0x67, 0x65, 0x73, 0x74, 0x61, 0xee, 0xe0, 0x22, 0x35,
- 0xe5, 0x03, 0x0b, 0x85, 0x6d, 0x6f, 0x6e, 0x2e, 0x70, 0x61, 0x6e, 0x65,
- 0xec, 0xc0, 0x7b, 0x6a, 0x65, 0xef, 0xce, 0x93, 0x67, 0xf5, 0xce, 0x90,
- 0x62, 0xf5, 0xe0, 0x24, 0x54, 0xae, 0x05, 0x54, 0x9d, 0xd0, 0x63, 0x67,
- 0xf6, 0xe0, 0x24, 0xef, 0xe3, 0x27, 0x13, 0x2a, 0x08, 0x05, 0x40, 0xb9,
- 0x06, 0x17, 0x40, 0x7a, 0x03, 0x04, 0x44, 0x29, 0x40, 0x4e, 0x41, 0x77,
- 0x05, 0x40, 0xc3, 0x42, 0x05, 0x07, 0x40, 0x4b, 0x2d, 0x40, 0xb0, 0x0d,
- 0x41, 0xdf, 0x04, 0x55, 0x80, 0x1f, 0xc1, 0xba, 0xfa, 0x05, 0x53, 0x62,
- 0xd1, 0x6d, 0xe5, 0x02, 0x85, 0x73, 0xf4, 0xe0, 0x20, 0xdd, 0x6c, 0x61,
- 0xe4, 0xce, 0x0b, 0xf9, 0x08, 0x10, 0x08, 0x03, 0x58, 0x9e, 0xcc, 0x00,
- 0xef, 0x04, 0xe0, 0x24, 0x4c, 0x6e, 0xae, 0x03, 0xcd, 0xed, 0x6c, 0x69,
- 0xee, 0xe0, 0x21, 0xf7, 0x6d, 0x72, 0xf5, 0x60, 0x22, 0x7c, 0xc2, 0x2a,
- 0xe2, 0xde, 0x1c, 0x61, 0x2e, 0xe7, 0xe0, 0x22, 0x5b, 0xf8, 0x04, 0xe0,
- 0x24, 0x93, 0xae, 0xd2, 0x49, 0xf6, 0x49, 0xac, 0xda, 0xe3, 0xf5, 0x0b,
- 0x08, 0x40, 0x6b, 0x0d, 0x0a, 0x05, 0x0e, 0xe0, 0x23, 0xe2, 0x74, 0x65,
- 0x67, 0x69, 0x72, 0xec, 0xdc, 0xe1, 0x73, 0xf4, 0x02, 0xb3, 0x6f, 0xed,
- 0x02, 0x9f, 0x65, 0xf2, 0x02, 0x94, 0xae, 0x03, 0xcb, 0xfb, 0x73, 0x70,
- 0x65, 0x65, 0x64, 0x70, 0x61, 0x72, 0x74, 0x6e, 0x65, 0x72, 0xae, 0xe0,
- 0x24, 0x1c, 0x2d, 0x6f, 0x63, 0xe9, 0xe0, 0x24, 0x3d, 0x2e, 0x6d, 0x65,
- 0x74, 0x61, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x6d, 0x2e, 0xe3, 0xd1,
- 0x63, 0xae, 0x04, 0x08, 0x0c, 0x85, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e,
- 0xe7, 0x9d, 0x72, 0x65, 0x74, 0x72, 0x6f, 0x73, 0x6e, 0x75, 0xe2, 0xe0,
- 0x21, 0x70, 0x70, 0x72, 0x6f, 0xe4, 0x8c, 0xe4, 0x02, 0x86, 0x69, 0x73,
- 0x72, 0x65, 0xe3, 0x83, 0x65, 0x76, 0x2e, 0x74, 0x68, 0x69, 0x6e, 0x67,
- 0x64, 0x75, 0xf3, 0xd2, 0x06, 0xf2, 0x02, 0x86, 0x76, 0x2e, 0x64, 0xe5,
- 0xd2, 0xbc, 0x69, 0x74, 0xe9, 0xab, 0x70, 0x63, 0x61, 0x6b, 0x65, 0x2e,
- 0xe9, 0xe0, 0x23, 0xb0, 0x6e, 0xe5, 0xe0, 0x22, 0x88, 0x6c, 0x74, 0x75,
- 0xf2, 0x04, 0xe0, 0x20, 0x41, 0x61, 0xec, 0x59, 0xe2, 0xc7, 0xda, 0xe9,
- 0x02, 0x89, 0x73, 0x69, 0x6e, 0x65, 0x6c, 0xec, 0xe0, 0x22, 0xd3, 0x61,
- 0xe2, 0xdb, 0x65, 0x74, 0xae, 0x5f, 0x9a, 0xc2, 0xcd, 0xf3, 0x02, 0x86,
- 0x78, 0x2e, 0xe3, 0xe0, 0x23, 0xb7, 0xae, 0x04, 0xe0, 0x22, 0x55, 0x6b,
- 0x65, 0x6c, 0x69, 0x77, 0x65, 0xe2, 0xcc, 0x48, 0xf2, 0x0d, 0x10, 0x09,
- 0x09, 0x12, 0x19, 0x1a, 0x4a, 0x96, 0x56, 0xd7, 0xc1, 0xd3, 0x79, 0x70,
- 0x74, 0x6f, 0x6e, 0x6f, 0x6d, 0x69, 0x63, 0x2e, 0x6e, 0x65, 0xf4, 0xe0,
- 0x23, 0x88, 0x75, 0x69, 0x73, 0xe5, 0x60, 0x21, 0xc0, 0xc1, 0xd3, 0xef,
- 0x03, 0xcb, 0x41, 0x74, 0x6f, 0xee, 0xdd, 0x64, 0xe9, 0x03, 0x05, 0x84,
- 0x6d, 0x65, 0xe1, 0xc8, 0x9b, 0x63, 0xeb, 0xd1, 0x55, 0xae, 0x5d, 0xac,
- 0xc5, 0x13, 0xe5, 0x05, 0x04, 0x03, 0xd9, 0x57, 0xf7, 0xe0, 0x21, 0x1c,
- 0xed, 0xdc, 0x3c, 0x64, 0x69, 0xf4, 0x05, 0x54, 0x7d, 0xce, 0xe1, 0x75,
- 0xee, 0xd7, 0x59, 0xe1, 0x02, 0x8c, 0xee, 0x02, 0x83, 0xeb, 0xdb, 0xba,
- 0x62, 0x72, 0x6f, 0xef, 0xdb, 0x97, 0x66, 0xf4, 0x03, 0xdc, 0xb2, 0x69,
- 0x6e, 0x67, 0xae, 0xd0, 0x61, 0xae, 0x48, 0x5d, 0xd9, 0x79, 0xf1, 0xd0,
- 0xb0, 0xf0, 0xe0, 0x22, 0x46, 0xef, 0x16, 0x35, 0x0d, 0x25, 0x0d, 0x40,
- 0x48, 0x40, 0x65, 0x41, 0xa5, 0x40, 0x44, 0x03, 0x04, 0x1a, 0x06, 0x11,
- 0x4e, 0x62, 0xd1, 0x79, 0xf5, 0x04, 0x04, 0x08, 0x96, 0x72, 0xf3, 0xd7,
- 0xbc, 0x70, 0x6f, 0xee, 0x60, 0x21, 0x3e, 0xc1, 0xd3, 0xee, 0x02, 0x8f,
- 0xf4, 0x03, 0xdb, 0x33, 0x72, 0xf9, 0x04, 0xe0, 0x22, 0xff, 0x65, 0x73,
- 0xf4, 0xc6, 0xf9, 0x63, 0xe9, 0xde, 0xd6, 0x63, 0x68, 0x70, 0x6f, 0x74,
- 0x61, 0x74, 0x6f, 0x66, 0x72, 0x69, 0xe5, 0xce, 0xe9, 0xf3, 0x02, 0x85,
- 0x74, 0x75, 0xed, 0xdf, 0x3b, 0x65, 0x6e, 0xfa, 0xdd, 0x3b, 0xf2, 0x04,
- 0x05, 0x05, 0x91, 0x76, 0x65, 0xf4, 0xc6, 0xd1, 0x73, 0xe9, 0xe0, 0x21,
- 0xc8, 0x72, 0x65, 0x69, 0x6f, 0x73, 0x2d, 0x65, 0x2d, 0x74, 0x65, 0x6c,
- 0x65, 0x63, 0x6f, 0xed, 0xc0, 0xcd, 0x70, 0x6f, 0xf2, 0xd8, 0xa9, 0xf0,
- 0x02, 0x83, 0xf2, 0xdf, 0xfa, 0x65, 0x6e, 0x68, 0x61, 0xe7, 0xcd, 0xdd,
- 0xef, 0x03, 0x2a, 0x8b, 0xf0, 0x05, 0x09, 0xe0, 0x22, 0x96, 0x65, 0x72,
- 0x61, 0x74, 0x69, 0xf6, 0xe0, 0x20, 0x78, 0xae, 0x0d, 0x04, 0x04, 0x51,
- 0x52, 0x46, 0x10, 0x48, 0x4c, 0x41, 0xad, 0xc0, 0x6d, 0xf2, 0xe0, 0x20,
- 0xad, 0xf0, 0xe0, 0x20, 0xe2, 0xed, 0x5f, 0x71, 0xc1, 0x34, 0xec, 0x04,
- 0xe0, 0x22, 0x76, 0x62, 0x6c, 0x6f, 0xe7, 0xda, 0xda, 0x6b, 0x69, 0x6e,
- 0xe7, 0x04, 0xe0, 0x22, 0x68, 0x63, 0x68, 0x61, 0x6e, 0xee, 0xdd, 0xf8,
- 0xee, 0x06, 0x05, 0x26, 0x19, 0x03, 0x92, 0x76, 0x65, 0xee, 0xdb, 0xba,
- 0xf4, 0x03, 0x0c, 0x8c, 0xf2, 0x03, 0xde, 0x2a, 0x61, 0x63, 0x74, 0x6f,
- 0xf2, 0xe0, 0x20, 0x75, 0x65, 0x6d, 0x70, 0x6f, 0x72, 0x61, 0x72, 0xf9,
- 0x57, 0x30, 0xc8, 0xe3, 0xe1, 0x02, 0x83, 0xe7, 0xd1, 0x6f, 0xe3, 0xe0,
- 0x21, 0x0e, 0xf3, 0x02, 0x91, 0x75, 0xec, 0x02, 0x87, 0xf4, 0x03, 0xc9,
- 0x1b, 0xe1, 0xdb, 0x2f, 0x61, 0x64, 0x6f, 0xae, 0xd5, 0xd4, 0x74, 0x72,
- 0xf5, 0xd6, 0x13, 0xee, 0xdf, 0x5e, 0xe6, 0x02, 0x85, 0x65, 0x72, 0xe5,
- 0xdd, 0xf7, 0xae, 0x05, 0x56, 0xf2, 0xc8, 0x87, 0xf3, 0xe0, 0x21, 0xc7,
- 0x64, 0xef, 0xe0, 0x20, 0x2c, 0xed, 0x0b, 0x0c, 0x04, 0x40, 0x4b, 0x2d,
- 0x04, 0x5f, 0xf9, 0xc1, 0x6b, 0x75, 0x6e, 0x69, 0x63, 0x61, 0x1f, 0x43,
- 0x67, 0x1f, 0xc3, 0xdb, 0x50, 0x73, 0xe5, 0xd6, 0x9e, 0xf0, 0x03, 0xc0,
- 0x40, 0x75, 0x74, 0xe5, 0x03, 0x0d, 0x9e, 0xf2, 0x05, 0x5f, 0xa6, 0xc2,
- 0x2a, 0x68, 0x69, 0x73, 0x74, 0xef, 0xd7, 0xb0, 0xae, 0x02, 0x86, 0x65,
- 0x73, 0x74, 0xe1, 0xc3, 0x90, 0x61, 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x61,
- 0x77, 0x73, 0x2e, 0x63, 0x6f, 0xed, 0x04, 0xe0, 0x21, 0x9d, 0x2e, 0xe3,
- 0xcb, 0x82, 0x2d, 0x31, 0x2e, 0x61, 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x61,
- 0x77, 0xf3, 0xe0, 0x21, 0x89, 0xe1, 0x03, 0xdf, 0x5e, 0xee, 0xdf, 0xf6,
- 0xed, 0x02, 0xa5, 0x75, 0xee, 0x02, 0x9d, 0xe9, 0x02, 0x90, 0x74, 0xf9,
- 0x05, 0x5f, 0x5a, 0xc2, 0x2a, 0x2d, 0x70, 0x72, 0x6f, 0xae, 0x60, 0x21,
- 0x41, 0x9b, 0x63, 0x61, 0x74, 0x69, 0x6f, 0xee, 0x5a, 0xe1, 0xc4, 0x6a,
- 0x65, 0xae, 0xda, 0xaa, 0x62, 0x61, 0xee, 0xdf, 0x9a, 0x63, 0xe1, 0xd5,
- 0x1d, 0xae, 0x1b, 0x04, 0x03, 0x0a, 0x08, 0x0a, 0x15, 0x06, 0x04, 0x0e,
- 0x0b, 0x12, 0x0e, 0x0b, 0x03, 0x0b, 0x08, 0x0e, 0x06, 0x0b, 0x08, 0x0d,
- 0x10, 0x4e, 0x18, 0xcc, 0x3f, 0xf9, 0xe0, 0x20, 0x6c, 0xf7, 0xdf, 0x70,
- 0xf6, 0x55, 0xfc, 0x49, 0xa6, 0x1e, 0x40, 0xa5, 0xc0, 0x73, 0xf5, 0x5f,
- 0x59, 0x3c, 0x40, 0x9b, 0xc1, 0x05, 0xf4, 0x5f, 0x54, 0x1f, 0x11, 0x0c,
- 0x40, 0x7a, 0x3a, 0xb3, 0xf3, 0x55, 0xe0, 0x41, 0xf0, 0x46, 0x46, 0x41,
- 0x3b, 0x1b, 0x03, 0x14, 0x03, 0x40, 0x7a, 0x19, 0x05, 0x1c, 0x40, 0xad,
- 0xbc, 0xf2, 0x5f, 0xc0, 0x40, 0x65, 0x8f, 0xf1, 0xe0, 0x20, 0x03, 0xf0,
- 0x56, 0xe2, 0x48, 0x50, 0x03, 0x18, 0x17, 0x40, 0x7d, 0x19, 0x05, 0x2b,
- 0xa4, 0xee, 0x56, 0xd4, 0x48, 0xa3, 0x40, 0x7a, 0x40, 0x4f, 0xc0, 0xb6,
- 0xed, 0x5a, 0x2d, 0x43, 0xb1, 0x41, 0x34, 0x07, 0x03, 0x18, 0x17, 0x40,
- 0x7d, 0x3a, 0x40, 0x82, 0xc0, 0x67, 0xec, 0x55, 0x96, 0x41, 0xf0, 0x46,
- 0x46, 0x41, 0x3e, 0x2f, 0x40, 0x9b, 0xc0, 0x4f, 0xeb, 0x5e, 0xef, 0x03,
- 0x1f, 0x1a, 0x21, 0x40, 0x84, 0xc0, 0xfb, 0xea, 0xdf, 0xd7, 0xe9, 0x06,
- 0x5e, 0xe5, 0x18, 0xc0, 0xd1, 0xf1, 0xe0, 0x20, 0xb7, 0xe8, 0x5e, 0xe3,
- 0x32, 0x40, 0x7a, 0xc0, 0x6d, 0xe7, 0x5e, 0xf3, 0x17, 0x03, 0x1e, 0x40,
- 0x5c, 0x19, 0x0f, 0x21, 0x24, 0xc0, 0x4f, 0xe6, 0x5e, 0xe2, 0x11, 0xc0,
- 0xf3, 0xe5, 0x55, 0x53, 0x49, 0x71, 0x40, 0xaf, 0x40, 0x49, 0xc0, 0xda,
- 0xe4, 0x5e, 0xaf, 0x22, 0x40, 0xd1, 0xc0, 0xad, 0xe3, 0x5d, 0x76, 0x41,
- 0x34, 0x1f, 0x1a, 0x03, 0x1e, 0x40, 0x96, 0xc0, 0x82, 0xe2, 0x57, 0x23,
- 0x47, 0x77, 0x0a, 0x18, 0x1a, 0x03, 0x1e, 0x40, 0x5c, 0x19, 0x05, 0x1c,
- 0xb3, 0xe1, 0x55, 0x23, 0x41, 0x21, 0x48, 0x46, 0x03, 0x1f, 0x03, 0x38,
- 0x40, 0xc9, 0x40, 0x4f, 0xc0, 0x67, 0xec, 0x05, 0x07, 0x1f, 0x12, 0x83,
- 0x75, 0x6d, 0xe2, 0x43, 0xa9, 0xd0, 0x51, 0xef, 0x03, 0x0b, 0x8d, 0x72,
- 0x61, 0x64, 0x6f, 0x70, 0x6c, 0x61, 0x74, 0xe5, 0xcf, 0x4c, 0x6e, 0x69,
- 0x61, 0x6c, 0x77, 0x69, 0x6c, 0x6c, 0x69, 0x61, 0xed, 0xc4, 0x04, 0xe7,
- 0xca, 0xc4, 0x6c, 0xe5, 0x02, 0x8b, 0x67, 0xe5, 0x04, 0xe0, 0x20, 0x21,
- 0x66, 0x61, 0xee, 0xdd, 0xdb, 0xe3, 0xd6, 0x0d, 0xe4, 0xc2, 0xed, 0xae,
- 0xcc, 0xeb, 0xe7, 0xd8, 0x6e, 0x66, 0xe6, 0xdb, 0xfe, 0xe4, 0x03, 0xd8,
- 0x36, 0xe5, 0x04, 0x07, 0xc9, 0xce, 0xf3, 0x03, 0xdf, 0xff, 0xf0, 0xd6,
- 0xe9, 0x62, 0x65, 0x72, 0x67, 0x2e, 0x70, 0x61, 0xe7, 0xdf, 0xb7, 0x63,
- 0x6f, 0x74, 0xf4, 0xd2, 0x21, 0xe1, 0x05, 0x4b, 0x27, 0xcb, 0x3c, 0x73,
- 0x74, 0x61, 0x6c, 0x64, 0x65, 0x66, 0x65, 0xee, 0xcd, 0xe9, 0xae, 0x1a,
- 0x05, 0x09, 0x12, 0x05, 0x06, 0x0d, 0x0d, 0x0b, 0x05, 0x09, 0x04, 0x0a,
- 0x06, 0x09, 0x12, 0x0d, 0x12, 0x4c, 0xf2, 0x4c, 0x11, 0x44, 0x09, 0xc1,
- 0x9f, 0xf6, 0x5e, 0x3f, 0xc0, 0xa5, 0xf5, 0x50, 0x84, 0x4d, 0x59, 0x0a,
- 0x03, 0xc1, 0xcf, 0xf4, 0x08, 0x5d, 0xcc, 0x22, 0x11, 0x40, 0x86, 0x99,
- 0x65, 0x63, 0x68, 0x6e, 0x6f, 0x6c, 0xef, 0xcf, 0x32, 0xf3, 0x5d, 0xc2,
- 0xc0, 0xb9, 0xf2, 0x5d, 0xc0, 0x07, 0xc1, 0x64, 0xf0, 0x04, 0x5d, 0xb6,
- 0xbc, 0xec, 0x03, 0xdf, 0x88, 0x61, 0xe3, 0xdf, 0x4d, 0xee, 0x55, 0x39,
- 0x40, 0x69, 0x48, 0x08, 0x40, 0x5d, 0x40, 0x7a, 0xc0, 0x97, 0xed, 0x5d,
- 0x9d, 0x03, 0x40, 0xd4, 0x2b, 0x40, 0x73, 0xc0, 0x67, 0xec, 0x54, 0x2b,
- 0xc9, 0x71, 0xeb, 0x03, 0xde, 0x8c, 0xf2, 0x53, 0xc5, 0xcb, 0xa1, 0xea,
- 0x5e, 0x65, 0xa1, 0xe9, 0x5d, 0xa2, 0x03, 0x03, 0x17, 0x40, 0x7a, 0xc0,
- 0x6d, 0xe7, 0x5d, 0x9b, 0x17, 0xc1, 0xa0, 0x66, 0x69, 0x6e, 0x61, 0x6e,
- 0x63, 0xe9, 0xc1, 0x35, 0xe5, 0x02, 0x86, 0x76, 0x65, 0x6e, 0xf4, 0xde,
- 0xf6, 0x64, 0x75, 0x63, 0x61, 0x74, 0x69, 0xef, 0xcb, 0xd0, 0xe3, 0x4c,
- 0x53, 0x43, 0xa9, 0x4d, 0x7b, 0x03, 0x38, 0x40, 0xc9, 0xc0, 0xb1, 0xe2,
- 0x09, 0x4b, 0xb7, 0x4a, 0x11, 0x47, 0x7a, 0xc0, 0x5a, 0x75, 0x73, 0x69,
- 0x6e, 0x65, 0xf3, 0xde, 0xce, 0xe1, 0x5d, 0x58, 0x40, 0x97, 0x3a, 0x0f,
- 0xc0, 0xda, 0xee, 0x08, 0x03, 0x0a, 0x07, 0x03, 0x0b, 0xde, 0xe0, 0xf4,
- 0xde, 0x48, 0x73, 0x2e, 0x6a, 0x6f, 0x79, 0x65, 0x6e, 0xf4, 0xde, 0xe1,
- 0x70, 0x79, 0x2e, 0x67, 0xe4, 0xcb, 0x8b, 0xe7, 0xde, 0x34, 0xae, 0x44,
- 0x08, 0x48, 0x14, 0x4d, 0x68, 0x43, 0xfd, 0xc1, 0x62, 0x2d, 0x6e, 0x6f,
- 0x72, 0x74, 0xe8, 0x02, 0x84, 0x77, 0x65, 0x73, 0x74, 0x2d, 0x31, 0x2e,
- 0x65, 0x62, 0x2e, 0x61, 0x6d, 0x61, 0x7a, 0x6f, 0x6e, 0x61, 0x77, 0x73,
- 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0xe3, 0xcb, 0x5a, 0xec, 0x0a, 0x11, 0x41,
- 0x14, 0x1e, 0x1c, 0x5b, 0xe9, 0xc1, 0x6a, 0x75, 0xe2, 0x04, 0x04, 0xde,
- 0xa8, 0x6d, 0xe5, 0xdc, 0xf5, 0xae, 0x03, 0xdc, 0x55, 0xf4, 0xdc, 0xc9,
- 0xef, 0x04, 0x41, 0x08, 0x84, 0x75, 0xe4, 0x0e, 0x09, 0x0e, 0x26, 0x06,
- 0x11, 0x0a, 0x0d, 0x20, 0x0c, 0x40, 0x53, 0xdd, 0xa2, 0x79, 0x63, 0x6c,
- 0x75, 0x73, 0x74, 0xe5, 0xc3, 0xdf, 0x73, 0x69, 0x74, 0x65, 0x2e, 0x62,
- 0x75, 0x69, 0x6c, 0x64, 0x65, 0xf2, 0xde, 0x2f, 0x6e, 0x73, 0xae, 0x0a,
- 0x03, 0x05, 0x03, 0x05, 0x04, 0x5a, 0x02, 0xc2, 0x10, 0xf5, 0xde, 0x1f,
- 0xf0, 0x4a, 0xc5, 0xc4, 0x3b, 0xe9, 0xcf, 0x1b, 0xe3, 0x4f, 0x21, 0xcf,
- 0x2c, 0x62, 0xe9, 0xcb, 0x77, 0x61, 0x73, 0xe9, 0xcf, 0x1b, 0x6a, 0x69,
- 0x66, 0xe6, 0xdd, 0x86, 0xe6, 0x02, 0x89, 0x75, 0x6e, 0x63, 0x74, 0x69,
- 0x6f, 0xee, 0xde, 0x1a, 0x72, 0x6f, 0xee, 0xd2, 0x7a, 0x65, 0x72, 0x61,
- 0x2e, 0x73, 0x69, 0x74, 0xe5, 0xde, 0x1e, 0x63, 0x6f, 0x6e, 0x74, 0x72,
- 0x6f, 0xec, 0x03, 0xdd, 0xc6, 0xec, 0xd5, 0x26, 0xe1, 0x02, 0x8f, 0x70,
- 0xf0, 0x03, 0xdd, 0xf5, 0x73, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x74, 0xe1,
- 0xd4, 0x2c, 0x63, 0x63, 0x65, 0x73, 0x73, 0xae, 0x03, 0xdd, 0xe3, 0x68,
- 0x6f, 0xf3, 0xdd, 0xe2, 0x36, 0x36, 0xae, 0x02, 0x84, 0x7a, 0xef, 0xce,
- 0xa0, 0xf7, 0xdd, 0xaf, 0xae, 0x07, 0x0d, 0x10, 0x13, 0x16, 0xd4, 0xe9,
- 0x6e, 0x6f, 0x73, 0x70, 0x61, 0x6d, 0x70, 0x72, 0x6f, 0x78, 0xf9, 0xdd,
- 0xda, 0x6d, 0x65, 0x74, 0x61, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x75, 0x6d,
- 0x2e, 0x63, 0xfa, 0xdd, 0xc0, 0x6a, 0x65, 0x6c, 0x61, 0x73, 0x74, 0x69,
- 0x63, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x2e, 0x74, 0x69, 0xed, 0xdd, 0x69,
- 0x69, 0x6e, 0x74, 0x65, 0x72, 0x68, 0x6f, 0x73, 0x74, 0x73, 0x6f, 0x6c,
- 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0xe2, 0xdd, 0x6c, 0x67, 0x6f,
- 0xef, 0xdb, 0x64, 0x2d, 0x66, 0x72, 0x31, 0x2e, 0x75, 0x6e, 0x69, 0x73,
- 0x70, 0x61, 0xe3, 0xd5, 0x05, 0x74, 0xe8, 0xc8, 0xee, 0xe3, 0xd5, 0xd9,
- 0xe9, 0x02, 0x89, 0xee, 0x03, 0xc5, 0x43, 0xe9, 0x48, 0x94, 0xc9, 0xad,
- 0x63, 0xeb, 0x04, 0x08, 0xdd, 0x73, 0x72, 0x69, 0x73, 0x69, 0x6e, 0xe7,
- 0xdd, 0x51, 0x65, 0xf4, 0xd8, 0xd7, 0xe5, 0x03, 0x09, 0x8b, 0x76, 0x65,
- 0x72, 0x61, 0x70, 0x70, 0xf3, 0xdc, 0xf3, 0x72, 0xeb, 0x03, 0xdc, 0x88,
- 0x73, 0x74, 0x61, 0xe7, 0xc8, 0xb2, 0x61, 0xee, 0xc8, 0xb1, 0xe1, 0x02,
- 0x86, 0x6e, 0x2e, 0x72, 0xe9, 0xdc, 0x7a, 0x69, 0xed, 0xdb, 0x75, 0xeb,
- 0x42, 0x62, 0xca, 0x81, 0xe9, 0x0f, 0x18, 0x40, 0x59, 0x16, 0x08, 0x07,
- 0x0e, 0x0c, 0x59, 0xc1, 0x41, 0x56, 0xc1, 0x6a, 0x76, 0x69, 0xec, 0x03,
- 0x04, 0x89, 0x77, 0xe1, 0xd6, 0xa6, 0xe9, 0x02, 0x83, 0xfa, 0xd3, 0x14,
- 0xf3, 0xd3, 0x11, 0x61, 0x76, 0xe9, 0xda, 0xc6, 0xf4, 0x04, 0x40, 0x4b,
- 0x85, 0xf9, 0x04, 0x04, 0xdd, 0x0b, 0x65, 0xe1, 0xd4, 0x97, 0xae, 0x06,
- 0x08, 0x0f, 0x07, 0xdc, 0x7d, 0x79, 0x6f, 0x6b, 0x6f, 0x68, 0x61, 0xed,
- 0x95, 0xf3, 0x02, 0x85, 0x65, 0x6e, 0x64, 0xe1, 0xa7, 0x61, 0x70, 0x70,
- 0x6f, 0x72, 0xef, 0xa1, 0x6e, 0x61, 0x67, 0x6f, 0x79, 0xe1, 0x9a, 0xeb,
- 0x03, 0x04, 0x8a, 0x6f, 0x62, 0xe5, 0x92, 0x69, 0x74, 0x61, 0x6b, 0x79,
- 0x75, 0x73, 0x68, 0xf5, 0x88, 0x61, 0x77, 0x61, 0x73, 0x61, 0x6b, 0x69,
- 0x2e, 0x6a, 0x70, 0x85, 0xe9, 0x51, 0x84, 0xcb, 0x44, 0x61, 0xe4, 0xd8,
- 0x57, 0xf3, 0x02, 0x88, 0x74, 0x72, 0x6f, 0x6e, 0x2e, 0xee, 0xd2, 0xd2,
- 0x63, 0xef, 0x03, 0xdc, 0xb0, 0x66, 0x72, 0x65, 0xe1, 0xd4, 0x83, 0x72,
- 0xe3, 0x03, 0xdb, 0x4b, 0xf5, 0xd6, 0x10, 0x70, 0x72, 0x69, 0x61, 0xee,
- 0xdb, 0x1d, 0xee, 0x02, 0x83, 0xe5, 0xd7, 0x1d, 0x63, 0x69, 0x6e, 0x6e,
- 0x61, 0xf4, 0xd1, 0x61, 0xe5, 0x02, 0x85, 0x73, 0x7a, 0xf9, 0xcb, 0xef,
- 0x6e, 0xe3, 0xda, 0x61, 0xe1, 0xc4, 0x27, 0xe8, 0x0d, 0x28, 0x0a, 0x18,
- 0x27, 0x40, 0xeb, 0x40, 0x43, 0x40, 0x41, 0xda, 0x90, 0xf5, 0x06, 0x13,
- 0x44, 0x2b, 0xd0, 0x93, 0x6f, 0xae, 0x08, 0x51, 0x83, 0x48, 0x32, 0x41,
- 0x67, 0x89, 0x79, 0x61, 0x6d, 0x61, 0x6e, 0x61, 0xf3, 0xda, 0xe3, 0x6e,
- 0xe7, 0x02, 0x85, 0x6e, 0x61, 0xed, 0xc5, 0xd0, 0x62, 0x75, 0xeb, 0xc5,
- 0xcb, 0x74, 0x72, 0x2e, 0x6b, 0x31, 0x32, 0x2e, 0xed, 0xc2, 0xde, 0xf2,
- 0x02, 0x84, 0x6f, 0xed, 0xdb, 0x5e, 0x69, 0x73, 0xf4, 0x02, 0x83, 0xed,
- 0xc8, 0x1a, 0x69, 0x61, 0x6e, 0x73, 0x62, 0x75, 0xf2, 0xc5, 0xec, 0xef,
- 0x06, 0x03, 0x04, 0x09, 0x05, 0x84, 0xf9, 0xd2, 0xab, 0x77, 0xe4, 0xca,
- 0x07, 0xf3, 0x02, 0x82, 0xe8, 0x82, 0x65, 0xe9, 0xda, 0xcd, 0x6e, 0x61,
- 0xee, 0xda, 0xc8, 0x66, 0xf5, 0xd9, 0x5d, 0x63, 0x6f, 0x6c, 0x61, 0xf4,
- 0xd8, 0x5a, 0xe9, 0x10, 0x09, 0x09, 0x0b, 0x31, 0x0a, 0x08, 0x07, 0x14,
- 0x28, 0x0c, 0x0d, 0x07, 0x04, 0xd9, 0xe7, 0x7a, 0x75, 0x2e, 0x74, 0x6f,
- 0x74, 0xf4, 0xd3, 0xea, 0x79, 0x6f, 0x64, 0x61, 0xae, 0x54, 0x31, 0xc5,
- 0x05, 0xf4, 0x02, 0x85, 0x6f, 0x73, 0xe5, 0xda, 0xbd, 0xe1, 0xd7, 0x4e,
- 0xf2, 0x03, 0x04, 0xa2, 0x79, 0xf5, 0xd7, 0x46, 0x75, 0x72, 0x67, 0x69,
- 0x65, 0x6e, 0x73, 0x2d, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x73, 0x74, 0x65,
- 0xf3, 0x03, 0xd8, 0x8a, 0x2d, 0x65, 0x6e, 0x2d, 0x66, 0x72, 0x61, 0x6e,
- 0x63, 0x65, 0x2e, 0xe6, 0xd9, 0xe0, 0x6f, 0x70, 0x72, 0x61, 0xe3, 0xc1,
- 0x70, 0xf0, 0x02, 0x83, 0xf3, 0xd4, 0x04, 0x70, 0xf5, 0xd7, 0xad, 0xee,
- 0x03, 0xce, 0x55, 0x74, 0xe1, 0xda, 0x10, 0x6d, 0x6b, 0x65, 0x6e, 0xf4,
- 0xda, 0x38, 0xec, 0x02, 0x85, 0x6c, 0x6f, 0xf5, 0xc4, 0xf0, 0x64, 0x72,
- 0x65, 0xee, 0x03, 0xd9, 0x4e, 0xf3, 0x46, 0xa5, 0xd2, 0xa8, 0x6b, 0xf5,
- 0x06, 0x03, 0x0b, 0x04, 0x02, 0x88, 0xfa, 0xc4, 0xc6, 0xf3, 0x02, 0x84,
- 0x68, 0x69, 0xee, 0x94, 0x65, 0xe9, 0xd4, 0x90, 0x6d, 0xe1, 0xd8, 0xd4,
- 0xea, 0x8a, 0x68, 0xef, 0x03, 0xc4, 0xb0, 0xeb, 0xd7, 0xc6, 0x67, 0xef,
- 0xc4, 0xab, 0x6a, 0x69, 0x77, 0x61, 0x2e, 0x6e, 0x61, 0x67, 0x61, 0xf3,
- 0xd4, 0x77, 0x68, 0x61, 0x79, 0x61, 0x61, 0x6b, 0x61, 0x73, 0x61, 0x6b,
- 0xe1, 0xd9, 0xf9, 0x67, 0x61, 0x73, 0x61, 0xeb, 0xcf, 0x4b, 0x65, 0xf4,
- 0xd3, 0xdb, 0xe3, 0x02, 0x86, 0x68, 0x69, 0x62, 0xf5, 0xd0, 0x93, 0xe1,
- 0x02, 0x84, 0x70, 0xf0, 0xcc, 0x87, 0xe7, 0xd1, 0x0f, 0xe5, 0x04, 0x0b,
- 0x25, 0x88, 0x73, 0x61, 0x70, 0x65, 0x61, 0x6b, 0x65, 0x62, 0xe1, 0xd3,
- 0x34, 0xf2, 0x02, 0x97, 0xee, 0x02, 0x84, 0x6f, 0x76, 0xf4, 0x96, 0xe9,
- 0x03, 0x05, 0x83, 0x76, 0x74, 0x73, 0xe9, 0x8f, 0x68, 0xe9, 0x83, 0x67,
- 0x6f, 0xf6, 0x88, 0x6b, 0x61, 0xf3, 0x02, 0x81, 0x73, 0x79, 0x2e, 0xf5,
- 0xd9, 0xdb, 0x6c, 0x74, 0x65, 0x6e, 0x68, 0xe1, 0xd1, 0xa2, 0x61, 0xf0,
- 0x53, 0x3d, 0xc7, 0x98, 0xe1, 0x06, 0x0c, 0x0b, 0x10, 0xca, 0x3e, 0xf4,
- 0x03, 0xda, 0xc6, 0x74, 0x61, 0x6e, 0x6f, 0x6f, 0xe7, 0xd5, 0x47, 0xf2,
- 0x02, 0x85, 0x74, 0x65, 0xf2, 0xd8, 0x68, 0xe9, 0xca, 0x2d, 0xee, 0x03,
- 0xd6, 0x44, 0x6e, 0x65, 0xec, 0x03, 0xda, 0xa9, 0x73, 0x64, 0x76, 0xf2,
- 0xda, 0x81, 0xed, 0x02, 0x8a, 0x70, 0x69, 0x6f, 0x6e, 0x73, 0x68, 0x69,
- 0xf0, 0xd8, 0x48, 0x62, 0x61, 0x67, 0xf2, 0xc0, 0x9d, 0xae, 0x05, 0x55,
- 0x24, 0xc3, 0xfd, 0xf4, 0x03, 0xda, 0x77, 0x72, 0x65, 0x6e, 0x64, 0x68,
- 0x6f, 0x73, 0x74, 0x69, 0x6e, 0xe7, 0xc3, 0x0c, 0xe6, 0x58, 0xc3, 0x40,
- 0xaf, 0xc1, 0x06, 0xe5, 0x09, 0x12, 0x11, 0x12, 0x06, 0x4c, 0x6c, 0xcc,
- 0xd7, 0x73, 0x65, 0x6e, 0xe1, 0x02, 0x81, 0x2d, 0x66, 0x6f, 0x72, 0xec,
- 0x03, 0xd3, 0x0c, 0x1f, 0xc3, 0xcb, 0xf1, 0xf2, 0x03, 0xd8, 0xb4, 0xf4,
- 0x02, 0x84, 0x6d, 0xe7, 0xc2, 0x70, 0x69, 0x66, 0x69, 0xe3, 0xd7, 0xf2,
- 0x6e, 0xf4, 0x02, 0x88, 0x72, 0x61, 0x6c, 0x75, 0x73, 0xae, 0xda, 0x08,
- 0x65, 0xf2, 0x58, 0x0d, 0xc2, 0x2a, 0x6c, 0x74, 0x69, 0xe3, 0xd8, 0x05,
- 0x63, 0x68, 0x69, 0xf2, 0xd3, 0x4b, 0xe4, 0x05, 0x4e, 0x21, 0xcc, 0x00,
- 0xee, 0x03, 0x08, 0x95, 0x37, 0x37, 0x2d, 0x73, 0x73, 0xec, 0xd9, 0xf3,
- 0x2e, 0x70, 0x72, 0x6f, 0x64, 0x2e, 0x61, 0x74, 0x6c, 0x61, 0x73, 0x73,
- 0x69, 0x61, 0x6e, 0x2d, 0x64, 0x65, 0xf6, 0xd9, 0xde, 0x2d, 0x65, 0x64,
- 0xe7, 0xcc, 0xa2, 0xe3, 0x04, 0x03, 0xd9, 0xf2, 0xe9, 0xd6, 0xc9, 0xae,
- 0x13, 0x06, 0x06, 0x05, 0x07, 0x04, 0x03, 0x04, 0x05, 0x17, 0x0a, 0x02,
- 0x06, 0x0b, 0x05, 0x06, 0x02, 0x09, 0x8d, 0xf7, 0x32, 0x1b, 0x2e, 0xd1,
- 0xc0, 0xf6, 0x40, 0x6f, 0x06, 0xd1, 0xc0, 0xf5, 0x40, 0x69, 0xca, 0x32,
- 0xf4, 0x03, 0xc0, 0x41, 0xf8, 0xd4, 0xe8, 0xf3, 0x40, 0x40, 0x96, 0xf2,
- 0xd2, 0x1f, 0xf0, 0x40, 0x5c, 0x8f, 0xef, 0x18, 0x40, 0x4f, 0x86, 0xee,
- 0x0a, 0x03, 0x03, 0x03, 0x12, 0x0b, 0x13, 0x03, 0xd8, 0x69, 0xf6, 0xd4,
- 0xca, 0xed, 0xd4, 0xc7, 0xea, 0xd4, 0xc4, 0xe8, 0xd4, 0xc1, 0xed, 0x16,
- 0x03, 0x13, 0x0a, 0x03, 0x03, 0x0c, 0xd1, 0xb4, 0xec, 0xb2, 0xeb, 0x02,
- 0xba, 0xf9, 0xd4, 0xaf, 0xe9, 0x04, 0x03, 0x23, 0x92, 0xee, 0xd4, 0xa7,
- 0xe4, 0xd4, 0xa4, 0xe8, 0x46, 0x1c, 0xcb, 0xc3, 0xe7, 0x02, 0x98, 0xf5,
- 0xd4, 0x99, 0xe6, 0xa6, 0xe4, 0x02, 0x83, 0xe5, 0xd4, 0x91, 0xe3, 0xd4,
- 0x8e, 0xe3, 0x03, 0x03, 0x83, 0xf4, 0xd4, 0x87, 0xef, 0xd4, 0x84, 0xe1,
- 0xd4, 0x81, 0xe1, 0x05, 0x03, 0x03, 0x03, 0x83, 0xfa, 0xd4, 0x78, 0xf3,
- 0xd4, 0x75, 0xf2, 0xd4, 0x72, 0xec, 0xd4, 0x6f, 0xeb, 0xd4, 0x6c, 0xe2,
- 0x09, 0x57, 0x06, 0x40, 0x67, 0x35, 0x34, 0xc0, 0x64, 0xe7, 0xc7, 0x59,
- 0xe1, 0x14, 0x05, 0x2e, 0x32, 0x40, 0x65, 0x1e, 0x1c, 0x40, 0x51, 0x27,
- 0x05, 0x07, 0x07, 0x04, 0x0d, 0x08, 0x18, 0xd7, 0x68, 0x78, 0x69, 0xe1,
- 0xc1, 0x1e, 0xf4, 0x07, 0x0b, 0x05, 0x09, 0x0a, 0xd8, 0xf8, 0x68, 0x6f,
- 0x6c, 0x69, 0xe3, 0x03, 0xd9, 0x13, 0xae, 0xd6, 0x82, 0x66, 0x6f, 0xef,
- 0xc4, 0x08, 0x65, 0x72, 0x69, 0x6e, 0xe7, 0x56, 0xb6, 0xc2, 0x50, 0x61,
- 0xee, 0x03, 0xd3, 0x5a, 0x7a, 0x61, 0xf2, 0xd7, 0x8f, 0xae, 0xc8, 0x8b,
- 0xf3, 0x06, 0x09, 0x07, 0x07, 0xd7, 0xcc, 0xf4, 0x02, 0x83, 0xf2, 0xd2,
- 0x55, 0xec, 0xd5, 0x40, 0x69, 0x6e, 0xef, 0x58, 0x78, 0xc0, 0x6a, 0xe5,
- 0x03, 0xd8, 0xda, 0xf2, 0xd1, 0x7b, 0xe1, 0x04, 0x0a, 0xd8, 0xc8, 0x64,
- 0x65, 0x6c, 0x61, 0x6d, 0x6f, 0x6e, 0xe5, 0xc0, 0xa3, 0x63, 0x61, 0xed,
- 0xd8, 0xa1, 0xf2, 0x0b, 0x06, 0x18, 0x04, 0x0a, 0x03, 0x12, 0x56, 0xa3,
- 0xc1, 0xd3, 0x74, 0x6f, 0x6f, 0xee, 0xcd, 0xa6, 0xf2, 0x03, 0x03, 0x85,
- 0xe9, 0xd2, 0x28, 0x64, 0x2e, 0xe3, 0xd8, 0x38, 0x61, 0x72, 0xe1, 0x02,
- 0x81, 0x2d, 0x6d, 0x61, 0x73, 0xf3, 0xd2, 0xf9, 0x67, 0xef, 0xd6, 0x47,
- 0xe5, 0x03, 0xd8, 0x91, 0x65, 0xf2, 0x56, 0xbc, 0xc1, 0xd3, 0xe4, 0xd6,
- 0xb7, 0x62, 0x6f, 0x6e, 0x69, 0xe1, 0x02, 0x81, 0x2d, 0x69, 0x67, 0x6c,
- 0x65, 0x73, 0x69, 0x61, 0xf3, 0xd7, 0x0e, 0xe1, 0x02, 0x83, 0xf6, 0xd5,
- 0x02, 0x63, 0x61, 0x6c, 0x2e, 0x6d, 0x79, 0x74, 0x68, 0x69, 0x63, 0x2d,
- 0x62, 0x65, 0x61, 0x73, 0xf4, 0xd7, 0xdd, 0xf0, 0x03, 0x04, 0x88, 0x6f,
- 0xef, 0xd0, 0xc0, 0x69, 0x74, 0x61, 0xec, 0x42, 0xe8, 0xd5, 0x6a, 0xe5,
- 0x02, 0x85, 0x74, 0x6f, 0xf7, 0xd6, 0xaa, 0x62, 0x72, 0x65, 0xf4, 0xce,
- 0x36, 0xee, 0x07, 0x05, 0x0b, 0x51, 0xaf, 0xc4, 0x4f, 0x64, 0x79, 0xf0,
- 0xca, 0x53, 0x63, 0x65, 0x72, 0x72, 0x65, 0x73, 0x65, 0x61, 0xf2, 0xc3,
- 0x6b, 0x61, 0xe4, 0xd2, 0xad, 0xed, 0x08, 0x38, 0x04, 0x05, 0x56, 0x70,
- 0xc1, 0x6a, 0xf0, 0x05, 0x07, 0x1f, 0xd7, 0xef, 0x6f, 0x62, 0x61, 0x73,
- 0xf3, 0xd6, 0xa5, 0xe9, 0x02, 0x8f, 0x6e, 0xe1, 0x02, 0x83, 0xf3, 0xd7,
- 0x4d, 0x67, 0x72, 0x61, 0x6e, 0x64, 0xe5, 0xd7, 0x45, 0x64, 0x61, 0x6e,
- 0xef, 0x02, 0x81, 0x2d, 0x6d, 0x65, 0x64, 0xe9, 0xd6, 0x86, 0xe1, 0x02,
- 0x83, 0xee, 0xd2, 0x48, 0x69, 0x67, 0x6e, 0xae, 0xcf, 0x62, 0x65, 0xf2,
- 0xd6, 0xdb, 0x64, 0x76, 0xf2, 0xd5, 0x99, 0x62, 0x72, 0x69, 0x64, 0xe7,
- 0xd4, 0x2e, 0xec, 0x0a, 0x09, 0x08, 0x07, 0x55, 0xf8, 0x40, 0x4e, 0xc1,
- 0x6a, 0x76, 0x69, 0x6e, 0x6b, 0x6c, 0x65, 0xe9, 0xd6, 0x23, 0x74, 0x61,
- 0x6e, 0x69, 0x73, 0xf3, 0xd0, 0x59, 0x69, 0x66, 0x6f, 0x72, 0xee, 0xcb,
- 0x5e, 0x61, 0xe2, 0xd2, 0x0a, 0x68, 0x63, 0xe5, 0xd0, 0x6f, 0x67, 0x6c,
- 0x69, 0x61, 0xf2, 0xd0, 0x54, 0xe6, 0x03, 0xd6, 0xc1, 0xea, 0xd7, 0x18,
- 0x64, 0xe1, 0xd1, 0x00, 0xe2, 0x03, 0xd7, 0x91, 0x6c, 0x65, 0x2d, 0x6d,
- 0x6f, 0x64, 0xe5, 0xd5, 0x46, 0x61, 0xae, 0x03, 0xd5, 0x34, 0xec, 0xc8,
- 0x30, 0xae, 0x09, 0x52, 0x10, 0x41, 0x30, 0x40, 0xfb, 0xc1, 0xd2, 0x72,
- 0x65, 0x63, 0x6c, 0x61, 0x69, 0x6d, 0x2e, 0x63, 0x6c, 0x6f, 0xf5, 0xcb,
- 0xc9, 0x2d, 0x63, 0x65, 0x6e, 0x74, 0x72, 0x61, 0xec, 0xcf, 0x13, 0x36,
- 0xb6, 0xc2, 0x0f, 0xae, 0x07, 0x43, 0x32, 0x43, 0xbb, 0xd0, 0x63, 0x63,
- 0x64, 0x6e, 0x37, 0xb7, 0xd5, 0x0a, 0xe2, 0x24, 0x38, 0x17, 0x21, 0x40,
- 0x98, 0x05, 0x13, 0x40, 0xd6, 0x0b, 0x40, 0xfc, 0x15, 0x19, 0x41, 0x59,
- 0x1b, 0x41, 0x09, 0x0d, 0x09, 0x40, 0xff, 0x08, 0x25, 0x10, 0x42, 0x71,
- 0x09, 0x43, 0x8f, 0x47, 0x15, 0xc1, 0x34, 0x1f, 0xc3, 0x04, 0x0a, 0x05,
- 0x91, 0xf8, 0x02, 0x83, 0xed, 0xcf, 0xe5, 0xae, 0x42, 0xe4, 0x86, 0x66,
- 0x72, 0xf5, 0xcc, 0x19, 0xe5, 0x02, 0x83, 0xf4, 0xc7, 0x0a, 0x64, 0x1f,
- 0x43, 0x65, 0x64, 0x64, 0x6a, 0x1f, 0xc3, 0xd6, 0x0d, 0xe1, 0x05, 0x03,
- 0x06, 0xc8, 0xbc, 0xec, 0xc5, 0x43, 0x69, 0x64, 0x1f, 0xc3, 0xc8, 0xc7,
- 0xe8, 0xc8, 0xcb, 0xfa, 0x07, 0x55, 0x7c, 0x40, 0x5f, 0xc1, 0x0b, 0x7a,
- 0x2e, 0x64, 0x61, 0x70, 0x70, 0x73, 0x2e, 0x65, 0x61, 0x72, 0x74, 0xe8,
- 0xd6, 0xc7, 0xf9, 0x07, 0x04, 0x03, 0x03, 0x06, 0xd6, 0xbf, 0x74, 0xef,
- 0xca, 0xf1, 0xeb, 0xc8, 0x33, 0xe7, 0xca, 0x9f, 0x65, 0x6e, 0x2e, 0xf3,
- 0xc7, 0x52, 0x64, 0x67, 0x6f, 0x73, 0x7a, 0x63, 0xfa, 0xd2, 0xcb, 0xf5,
- 0x0d, 0x09, 0x0e, 0x03, 0x13, 0x07, 0x18, 0x04, 0x06, 0x2a, 0x05, 0xd5,
- 0x2f, 0xfa, 0x03, 0xd4, 0xc7, 0x65, 0x6e, 0xae, 0xcb, 0xc5, 0xf9, 0x03,
- 0xd6, 0x9b, 0x73, 0x68, 0xef, 0x03, 0xc8, 0xb4, 0x75, 0xf3, 0xc9, 0x39,
- 0xf4, 0xce, 0xf8, 0xf3, 0x05, 0x04, 0x03, 0xd4, 0x57, 0x69, 0xee, 0xcf,
- 0xdf, 0xe8, 0xc5, 0x87, 0x61, 0x6e, 0x2e, 0xeb, 0xd5, 0xc6, 0x72, 0x67,
- 0x68, 0x6f, 0xe6, 0xd4, 0x4c, 0xee, 0x02, 0x84, 0x6b, 0xf9, 0xd1, 0x61,
- 0x67, 0xef, 0x02, 0x88, 0x74, 0x61, 0x6b, 0x61, 0x64, 0xe1, 0xc5, 0x7d,
- 0x6f, 0x6e, 0xef, 0xc5, 0x78, 0x6c, 0xf3, 0xc7, 0xdd, 0x6b, 0x68, 0x61,
- 0xf2, 0xd5, 0x02, 0x69, 0xec, 0x02, 0x8a, 0x74, 0x77, 0x69, 0x74, 0x68,
- 0x64, 0x61, 0xf2, 0xce, 0x1d, 0xe4, 0x05, 0x05, 0x0b, 0xd6, 0x2e, 0x69,
- 0x6e, 0xe7, 0xd4, 0x11, 0x65, 0xf2, 0x03, 0xd4, 0x61, 0x2e, 0x63, 0x6f,
- 0xe4, 0xcf, 0x52, 0x2e, 0x72, 0x75, 0xee, 0xd6, 0x18, 0x67, 0x61, 0xf4,
- 0xc4, 0xb1, 0x64, 0x65, 0xea, 0xd0, 0xa0, 0xf4, 0x54, 0xb3, 0xc1, 0x6a,
- 0xf3, 0x06, 0x09, 0x54, 0x9f, 0xc1, 0x6a, 0x73, 0x2e, 0x64, 0x65, 0x73,
- 0x69, 0xe7, 0xc2, 0xa6, 0xe2, 0xd5, 0x4f, 0xf2, 0x0a, 0x0a, 0x09, 0x21,
- 0x39, 0x28, 0x0d, 0x24, 0xd5, 0x35, 0x1f, 0x43, 0x78, 0x6e, 0x6e, 0x1f,
- 0x43, 0xf8, 0xc0, 0x49, 0xf9, 0x02, 0x83, 0xee, 0xd4, 0xf8, 0xe1, 0xd0,
- 0xa2, 0xf5, 0x04, 0x06, 0x0c, 0x84, 0x78, 0x65, 0x6c, 0xec, 0xcf, 0x4a,
- 0x73, 0x73, 0x65, 0xec, 0x03, 0xd3, 0xac, 0xf3, 0x53, 0xab, 0xc2, 0x2a,
- 0x6e, 0xe5, 0xcc, 0x4c, 0x6d, 0x75, 0x6e, 0xe4, 0xcf, 0x92, 0xef, 0x05,
- 0x10, 0x04, 0x0a, 0x89, 0x77, 0x73, 0x65, 0x72, 0x73, 0x61, 0x66, 0x65,
- 0x74, 0x79, 0x6d, 0x61, 0x72, 0xeb, 0xd5, 0x42, 0x74, 0xe8, 0xc2, 0xf4,
- 0x6e, 0x6e, 0x6f, 0xf9, 0x03, 0xd4, 0xb2, 0xf3, 0xcf, 0xf9, 0x6b, 0xe5,
- 0x03, 0xc9, 0x1f, 0x2d, 0xe9, 0xc9, 0xdc, 0x61, 0xe4, 0x02, 0x83, 0xf7,
- 0xcf, 0xd1, 0x63, 0x61, 0xf3, 0xce, 0xf3, 0xe9, 0x04, 0x0f, 0x05, 0x86,
- 0x74, 0x69, 0x73, 0xe8, 0x03, 0xd3, 0x58, 0x63, 0x6f, 0x6c, 0x75, 0x6d,
- 0xe2, 0xc9, 0x28, 0x73, 0x74, 0xef, 0xcb, 0xf4, 0x6e, 0x64, 0x69, 0xf3,
- 0xce, 0x23, 0x64, 0x67, 0x65, 0x73, 0x74, 0x6f, 0xee, 0xd4, 0x8d, 0xe5,
- 0x02, 0x84, 0x73, 0xe3, 0xcf, 0xbe, 0x6d, 0x61, 0x6e, 0xe7, 0xca, 0x64,
- 0xe1, 0x03, 0x0c, 0x8f, 0x73, 0x69, 0xec, 0x03, 0xd3, 0x25, 0x69, 0x61,
- 0x2e, 0xed, 0xd5, 0x0e, 0x6e, 0xe4, 0x03, 0xc4, 0xde, 0x79, 0x77, 0x69,
- 0x6e, 0x65, 0x76, 0x61, 0xec, 0xc4, 0x3f, 0x64, 0x65, 0xf3, 0xcb, 0xc8,
- 0xae, 0x53, 0xcb, 0xc1, 0x62, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x64, 0xae,
- 0x54, 0xeb, 0x1b, 0x9c, 0xef, 0x12, 0x04, 0x0c, 0x09, 0x15, 0x19, 0x0f,
- 0x1c, 0x05, 0x05, 0x3b, 0x03, 0x03, 0x08, 0x09, 0x0a, 0xd4, 0x3a, 0x7a,
- 0xe5, 0xc6, 0x94, 0xf9, 0x03, 0xcd, 0x72, 0x66, 0x72, 0x69, 0x65, 0x6e,
- 0xe4, 0xcd, 0x6c, 0xf8, 0x03, 0xd4, 0xfe, 0x66, 0x75, 0xf3, 0xcc, 0x6a,
- 0xf5, 0x02, 0x89, 0x74, 0xe9, 0x03, 0xcc, 0x27, 0x71, 0xf5, 0xd4, 0x14,
- 0xee, 0x03, 0xce, 0xf1, 0x63, 0x65, 0xed, 0xc7, 0xa3, 0xf4, 0x03, 0xd4,
- 0xe0, 0x61, 0xee, 0x03, 0xcd, 0x09, 0x69, 0xe3, 0x02, 0x85, 0x61, 0xec,
- 0x03, 0xd2, 0xa8, 0x67, 0x61, 0x72, 0x64, 0xe5, 0xca, 0xd7, 0xf3, 0x02,
- 0x89, 0xf4, 0x02, 0x83, 0xef, 0xc3, 0xb4, 0xe9, 0xd2, 0xf1, 0xe3, 0xd3,
- 0xb3, 0xef, 0x06, 0x04, 0x4d, 0x19, 0xc7, 0x98, 0x6d, 0xec, 0xce, 0x78,
- 0xeb, 0x04, 0x08, 0xd4, 0xa4, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0xe5, 0xd3,
- 0xd1, 0x69, 0xee, 0xd4, 0xa1, 0xee, 0x4a, 0xa9, 0xc8, 0x41, 0xed, 0x4d,
- 0x63, 0xc7, 0x37, 0xec, 0x06, 0x0e, 0x03, 0x04, 0x04, 0x8a, 0x7a, 0x61,
- 0x6e, 0xef, 0x03, 0xd3, 0x1e, 0x2d, 0x61, 0x6c, 0x74, 0xef, 0xce, 0x5c,
- 0xf4, 0xd4, 0x16, 0x6f, 0xe7, 0xcd, 0x51, 0x69, 0xf6, 0xd2, 0x56, 0x65,
- 0x73, 0x6c, 0x61, 0x77, 0x69, 0x65, 0xe3, 0xd0, 0x81, 0x64, 0x6c, 0x79,
- 0x67, 0x6f, 0x69, 0x6e, 0x67, 0x6e, 0x6f, 0x77, 0x68, 0x65, 0x72, 0xe5,
- 0xd2, 0x1a, 0xeb, 0xcf, 0x1c, 0xe6, 0xd3, 0x51, 0x65, 0x68, 0x72, 0x69,
- 0x6e, 0xe7, 0xc1, 0x94, 0xe4, 0x03, 0xcd, 0x13, 0x1f, 0x43, 0xf8, 0xd3,
- 0x52, 0xe1, 0x03, 0xcb, 0xca, 0x76, 0x69, 0x73, 0xf4, 0xcb, 0xcd, 0xae,
- 0x04, 0x06, 0xd2, 0xc6, 0x74, 0x65, 0x6c, 0xe5, 0xca, 0x8f, 0x6e, 0x6f,
- 0x72, 0xe4, 0xc8, 0x03, 0xee, 0x06, 0x05, 0x52, 0xb3, 0xc1, 0x6a, 0x72,
- 0x2e, 0xec, 0xc4, 0xe9, 0x70, 0x70, 0x61, 0x72, 0x69, 0x62, 0xe1, 0xd2,
- 0x43, 0xed, 0x07, 0x0e, 0x52, 0x24, 0x07, 0xc1, 0xd3, 0x6f, 0x61, 0x74,
- 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0xf3, 0xd1, 0xbd, 0xe4,
- 0xd3, 0x44, 0xec, 0x06, 0x10, 0x41, 0x2c, 0xd1, 0x4e, 0xf5, 0x02, 0x84,
- 0x73, 0xe8, 0xcc, 0x57, 0xe5, 0x03, 0xd3, 0xe9, 0x62, 0x69, 0xf4, 0xcb,
- 0x55, 0xef, 0x05, 0x05, 0x07, 0xc1, 0x11, 0x78, 0x63, 0xed, 0xd3, 0x58,
- 0x6f, 0x6d, 0x62, 0x65, 0xf2, 0xd3, 0xd3, 0xe7, 0x06, 0x40, 0xef, 0x09,
- 0xd2, 0xd3, 0xf3, 0x04, 0x03, 0xc0, 0xdb, 0xf9, 0xcc, 0xe6, 0x70, 0x6f,
- 0x74, 0xae, 0x16, 0x03, 0x03, 0x07, 0x0a, 0x07, 0x03, 0x04, 0x05, 0x0a,
- 0x07, 0x03, 0x09, 0x07, 0x03, 0x05, 0x05, 0x40, 0x55, 0x0b, 0xcb, 0x57,
- 0xf6, 0xc0, 0x43, 0xf5, 0xd1, 0x65, 0xf4, 0x03, 0xc7, 0xfe, 0xf7, 0xd3,
- 0x9a, 0xf3, 0x36, 0x44, 0x13, 0x4c, 0x9d, 0x40, 0x75, 0xc2, 0x03, 0xf2,
- 0x52, 0x42, 0x40, 0xe0, 0x27, 0x8b, 0xf1, 0xc4, 0x54, 0xf0, 0x53, 0x4a,
- 0x9c, 0xee, 0x49, 0x9e, 0xc9, 0x76, 0xed, 0x43, 0x0f, 0x44, 0xbc, 0x12,
- 0x48, 0xec, 0xc0, 0xec, 0xec, 0x44, 0x22, 0x4e, 0x03, 0xc1, 0x2e, 0xeb,
- 0xd1, 0xa4, 0xe9, 0x05, 0x53, 0x1d, 0x0b, 0x9c, 0xee, 0xd3, 0x60, 0xe8,
- 0x50, 0xac, 0x40, 0xec, 0xc0, 0x7a, 0xe7, 0xd1, 0x91, 0xe6, 0x44, 0x05,
- 0xcd, 0x89, 0xe4, 0x50, 0x9d, 0xc2, 0x78, 0xe3, 0x0d, 0x40, 0x61, 0x40,
- 0x93, 0x41, 0x03, 0x42, 0x13, 0x45, 0x51, 0xc9, 0xab, 0xef, 0x02, 0xae,
- 0xed, 0x03, 0xd3, 0x35, 0xae, 0x08, 0x03, 0x03, 0x03, 0x03, 0x06, 0x05,
- 0x85, 0xf5, 0xc2, 0xc0, 0xf4, 0xd1, 0x63, 0xee, 0xd0, 0xe9, 0xed, 0xd3,
- 0x05, 0xe5, 0x50, 0xe3, 0x41, 0xf8, 0x8b, 0xe3, 0x42, 0xae, 0xd0, 0x00,
- 0xe2, 0x42, 0xa9, 0xce, 0xa6, 0xe1, 0x51, 0x4a, 0xc0, 0x7a, 0xae, 0x08,
- 0x03, 0x02, 0x03, 0x50, 0x48, 0xc2, 0x7e, 0xfa, 0xc3, 0xcf, 0xee, 0xa3,
- 0xeb, 0xd2, 0xc3, 0xe9, 0x47, 0x5c, 0xc1, 0xbc, 0xe2, 0x07, 0x43, 0xbb,
- 0x4c, 0xf6, 0xc2, 0x03, 0xea, 0xd2, 0xec, 0xe1, 0x49, 0x08, 0x49, 0xa8,
- 0xb8, 0x69, 0x74, 0x65, 0xae, 0x03, 0xd0, 0x9f, 0x78, 0x79, 0xfa, 0xd2,
- 0xda, 0x64, 0x6e, 0x73, 0xae, 0x50, 0x96, 0x42, 0x1f, 0x9c, 0xae, 0x04,
- 0x03, 0x03, 0x83, 0xf6, 0xd1, 0x7e, 0xeb, 0xd0, 0x8a, 0xe7, 0xd2, 0xa6,
- 0xe2, 0x51, 0xda, 0xb3, 0x63, 0x6b, 0x62, 0x75, 0x73, 0x74, 0xe5, 0xd2,
- 0x03, 0x61, 0x63, 0xeb, 0x04, 0x06, 0xd2, 0xab, 0x66, 0x72, 0x69, 0xe4,
- 0xcc, 0xe9, 0x62, 0x61, 0x75, 0x64, 0x63, 0x64, 0xee, 0xd2, 0x80, 0xea,
- 0x06, 0x04, 0x06, 0x07, 0xd2, 0x8a, 0x75, 0xe7, 0xcd, 0x5b, 0x65, 0x72,
- 0x6b, 0xf2, 0xc6, 0x3a, 0x61, 0x72, 0xeb, 0x47, 0x90, 0xc3, 0x71, 0xae,
- 0xce, 0xaa, 0xe9, 0x14, 0x40, 0x5b, 0x18, 0x1c, 0x03, 0x05, 0x09, 0x08,
- 0x03, 0x03, 0x05, 0x1d, 0x12, 0x4f, 0xdb, 0x40, 0x4b, 0xc1, 0x6a, 0xfa,
- 0x04, 0x04, 0xd2, 0x69, 0x65, 0xee, 0xcd, 0x74, 0xae, 0x18, 0x03, 0x04,
- 0x03, 0x07, 0x06, 0x05, 0x06, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x4a,
- 0x98, 0x40, 0xde, 0x43, 0x14, 0x41, 0x8f, 0xc0, 0x58, 0xfa, 0xd0, 0x94,
- 0x77, 0xe6, 0xd2, 0x47, 0xf5, 0xc3, 0x12, 0xf4, 0x50, 0x9b, 0x40, 0x86,
- 0xc0, 0x6d, 0xf0, 0x50, 0x6e, 0x18, 0xc1, 0x01, 0xee, 0x50, 0xb8, 0xc0,
- 0xc9, 0xed, 0x4f, 0x25, 0x41, 0x34, 0xb9, 0xec, 0xd0, 0x5a, 0xeb, 0xd0,
- 0xaa, 0xe7, 0xc8, 0x42, 0xe5, 0xd1, 0x00, 0xe4, 0xcf, 0x6c, 0xe2, 0xc8,
- 0xca, 0xe1, 0x50, 0x3e, 0xc1, 0xbc, 0xf4, 0x02, 0x85, 0x74, 0x65, 0xf2,
- 0xca, 0x79, 0xe2, 0x02, 0x87, 0x75, 0x63, 0x6b, 0x65, 0xf4, 0xd1, 0x96,
- 0x72, 0x69, 0x64, 0xe7, 0xc4, 0xbe, 0xf2, 0x05, 0x08, 0x03, 0x03, 0x85,
- 0x74, 0x68, 0x70, 0x6c, 0x61, 0xe3, 0xce, 0x4c, 0xeb, 0xce, 0x69, 0xe4,
- 0xc6, 0xe0, 0x61, 0x74, 0xef, 0xd0, 0xcd, 0xae, 0xce, 0xf6, 0xf0, 0xc1,
- 0xa2, 0xef, 0x51, 0x26, 0xc0, 0xb9, 0xee, 0x03, 0xcb, 0xa0, 0xe7, 0x50,
- 0xec, 0xc0, 0xea, 0xec, 0x03, 0xc8, 0x4b, 0x62, 0xe1, 0xc7, 0xc5, 0xeb,
- 0xd0, 0xee, 0xe8, 0xc6, 0xda, 0x66, 0x75, 0xeb, 0xd0, 0x79, 0xe5, 0x05,
- 0x08, 0x07, 0xd0, 0x90, 0xf6, 0x02, 0x82, 0x1f, 0x43, 0xe1, 0xca, 0xd9,
- 0x73, 0x7a, 0x63, 0x7a, 0xe1, 0xc0, 0x8e, 0xec, 0x03, 0xc8, 0x66, 0x61,
- 0xf7, 0xc3, 0xe0, 0xe2, 0x03, 0x06, 0x83, 0x6c, 0xe5, 0x4f, 0x72, 0xc2,
- 0x2a, 0xe1, 0xd0, 0x7d, 0xae, 0x47, 0x6d, 0xc9, 0x6f, 0x61, 0xec, 0x02,
- 0x87, 0x79, 0x73, 0x74, 0x6f, 0xeb, 0xcd, 0x9a, 0x6f, 0x77, 0x69, 0x65,
- 0xfa, 0xc3, 0xbc, 0xe8, 0x04, 0x03, 0xd1, 0x76, 0xfa, 0xd0, 0xbf, 0x61,
- 0x72, 0xf4, 0xcf, 0xf3, 0xe7, 0x03, 0xd1, 0x6d, 0xae, 0x4c, 0x06, 0xc3,
- 0xfd, 0xe5, 0x0e, 0x2e, 0x11, 0x31, 0x07, 0x0b, 0x1a, 0x05, 0x09, 0x14,
- 0x06, 0x20, 0xd0, 0x75, 0xf4, 0x05, 0x0d, 0x17, 0xd1, 0x2f, 0x74, 0x65,
- 0x72, 0x2d, 0x74, 0x68, 0x61, 0x6e, 0x2e, 0x74, 0xf6, 0xd1, 0x44, 0xe1,
- 0x02, 0x87, 0x69, 0x6e, 0x61, 0x62, 0xef, 0xc8, 0x51, 0xae, 0x03, 0xcb,
- 0x3c, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x63, 0xe1, 0xc3, 0xed, 0x2e, 0xe1,
- 0xd0, 0x76, 0xf3, 0x02, 0x88, 0xf4, 0x03, 0xd1, 0x24, 0x62, 0xf5, 0xcf,
- 0x81, 0x6b, 0x69, 0x64, 0xf9, 0xcd, 0x2f, 0xf2, 0x05, 0x15, 0x06, 0xc7,
- 0x03, 0xec, 0x02, 0x86, 0x69, 0xee, 0x4e, 0xe5, 0xc2, 0x2a, 0x65, 0xf6,
- 0x02, 0x85, 0x1f, 0x43, 0xe5, 0xca, 0xfa, 0xe1, 0xca, 0xf7, 0x6b, 0x65,
- 0x6c, 0xe5, 0xc9, 0x29, 0xe7, 0x06, 0x05, 0x42, 0x97, 0xcd, 0x62, 0x62,
- 0x61, 0xf5, 0xce, 0xc5, 0x61, 0xed, 0xcf, 0x80, 0x70, 0x70, 0x75, 0x2e,
- 0xef, 0xcb, 0xc9, 0xee, 0x02, 0x84, 0x74, 0xec, 0xc4, 0xf0, 0x65, 0xf6,
- 0xcd, 0x25, 0xec, 0x05, 0x0b, 0x03, 0xc0, 0x94, 0xec, 0x02, 0x83, 0xf5,
- 0xc6, 0x1c, 0x65, 0x76, 0xf5, 0xcd, 0x21, 0xe5, 0xce, 0x05, 0x61, 0x75,
- 0x2e, 0xf0, 0xce, 0xe5, 0x69, 0x61, 0xf2, 0xcb, 0x7c, 0x67, 0x65, 0x74,
- 0x2e, 0x61, 0x70, 0xf0, 0xd0, 0x9f, 0xe5, 0x04, 0x04, 0xcf, 0xef, 0x70,
- 0xae, 0xc6, 0xc2, 0x6c, 0x64, 0x65, 0x6e, 0x67, 0x65, 0x6c, 0x75, 0xe9,
- 0xcc, 0x56, 0x64, 0x7a, 0x69, 0xee, 0xcc, 0xaa, 0xe1, 0x04, 0x0b, 0xc8,
- 0x0f, 0xf5, 0x02, 0x85, 0x78, 0x61, 0xf2, 0xc6, 0x57, 0xf4, 0xce, 0xe6,
- 0xf2, 0x03, 0xc1, 0x94, 0x61, 0x6c, 0xf6, 0x02, 0x82, 0x1f, 0x43, 0x61,
- 0x68, 0xeb, 0xc9, 0xa1, 0xae, 0x04, 0x04, 0xcb, 0x06, 0x67, 0xf9, 0xd0,
- 0x6a, 0xe1, 0xc4, 0xb8, 0xe4, 0x02, 0x84, 0x2e, 0xf3, 0xcf, 0x89, 0x86,
- 0xe3, 0x06, 0x0e, 0x4e, 0xae, 0xc1, 0x9d, 0x69, 0x2e, 0x64, 0x6e, 0x73,
- 0x74, 0x72, 0x61, 0x63, 0x65, 0x2e, 0xf0, 0xc0, 0xea, 0xae, 0x03, 0xcf,
- 0x41, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x2e, 0xf3, 0xd0,
- 0x04, 0xe2, 0x08, 0x03, 0x44, 0xec, 0x4a, 0x20, 0xc1, 0x24, 0xf6, 0xcf,
- 0x2c, 0x73, 0xae, 0xc3, 0x8b, 0xe1, 0x14, 0x05, 0x0b, 0x0d, 0x3d, 0x40,
- 0xf3, 0x2f, 0x0c, 0x40, 0x5a, 0x04, 0x08, 0x0f, 0x06, 0x0e, 0x28, 0x1d,
- 0xcd, 0xc1, 0x79, 0x65, 0xf2, 0xce, 0x76, 0xf5, 0x02, 0x84, 0x68, 0xe1,
- 0xcb, 0xd7, 0x65, 0xf2, 0xc6, 0x13, 0xf4, 0x03, 0x03, 0x83, 0xf3, 0xc1,
- 0x82, 0xef, 0xc5, 0x2c, 0xe8, 0xc9, 0x68, 0xf3, 0x06, 0x08, 0x11, 0x07,
- 0xce, 0x69, 0x6b, 0x65, 0x74, 0x62, 0x61, 0xec, 0xce, 0x35, 0xe9, 0x02,
- 0x86, 0x6c, 0x69, 0x63, 0xe1, 0xc8, 0x86, 0x63, 0x73, 0x65, 0x72, 0x76,
- 0xe5, 0xc6, 0xe8, 0x68, 0x6b, 0x69, 0x72, 0xe9, 0xcc, 0xe1, 0xe5, 0x04,
- 0x08, 0xc6, 0x43, 0x62, 0x61, 0x6c, 0xec, 0x4d, 0xa0, 0xc2, 0x2a, 0xae,
- 0x02, 0x83, 0xf3, 0xc0, 0x57, 0xe5, 0xcf, 0xb0, 0xf2, 0x12, 0x08, 0x40,
- 0x72, 0x20, 0x17, 0x06, 0x06, 0x03, 0x17, 0x03, 0x03, 0x47, 0x7f, 0x46,
- 0x61, 0xc0, 0xed, 0xf5, 0x03, 0xc4, 0xac, 0x65, 0xf2, 0xc8, 0x83, 0x73,
- 0xf9, 0x03, 0x0d, 0x86, 0x6f, 0x6e, 0x6c, 0x69, 0x6e, 0x65, 0x2e, 0x63,
- 0xef, 0x4c, 0xdd, 0xc2, 0xb2, 0x63, 0x65, 0x6e, 0xf4, 0xc6, 0xc1, 0xae,
- 0x0f, 0x13, 0x03, 0x03, 0x0a, 0x0f, 0x08, 0x0d, 0x4a, 0xe1, 0x41, 0x9c,
- 0x42, 0x78, 0x9b, 0xf3, 0x03, 0x07, 0x84, 0x75, 0x70, 0x70, 0x6f, 0xf2,
- 0xcf, 0x50, 0x69, 0xf4, 0xcf, 0x30, 0x68, 0xef, 0xce, 0x97, 0xf0, 0x02,
- 0xaa, 0xf2, 0xce, 0xf4, 0xef, 0x03, 0xcd, 0x1c, 0x6e, 0x6c, 0x69, 0xee,
- 0xcf, 0x1c, 0xed, 0x02, 0x85, 0x6f, 0x62, 0xe9, 0xcf, 0x4d, 0xe5, 0x03,
- 0xcf, 0x47, 0xee, 0xcd, 0xfb, 0xe9, 0x03, 0xce, 0xd5, 0xee, 0x2b, 0xcf,
- 0x14, 0xe3, 0x04, 0x05, 0xcc, 0x7e, 0x6c, 0x75, 0xe2, 0xcf, 0x34, 0xe1,
- 0xcf, 0x31, 0xe2, 0xcc, 0xf2, 0x72, 0xe5, 0x02, 0x97, 0xec, 0x02, 0x81,
- 0x6c, 0x2d, 0x6f, 0x66, 0x2d, 0x6b, 0x6e, 0x6f, 0x77, 0x6c, 0x65, 0x64,
- 0x67, 0x65, 0x2e, 0x69, 0x6e, 0xe6, 0xce, 0xa8, 0x61, 0x75, 0xae, 0xc3,
- 0xda, 0x6c, 0x65, 0x74, 0x74, 0xe1, 0x02, 0x87, 0x74, 0x72, 0x61, 0x6e,
- 0xe9, 0xc9, 0x5d, 0x2d, 0x74, 0x72, 0x61, 0x6e, 0x69, 0xad, 0xc9, 0x54,
- 0x67, 0x61, 0x69, 0xee, 0xcd, 0x22, 0x65, 0x66, 0x6f, 0xef, 0xcd, 0xcb,
- 0xe4, 0xc9, 0x6d, 0xe3, 0x02, 0x8b, 0x6c, 0x61, 0xf9, 0x03, 0xcd, 0x0e,
- 0x63, 0x61, 0xf2, 0xcd, 0x29, 0x65, 0x6c, 0x6f, 0x6e, 0xe1, 0x4c, 0xad,
- 0xc2, 0x2a, 0xb2, 0xce, 0xae, 0xb1, 0xce, 0xab, 0xb0, 0xce, 0xa8, 0xee,
- 0x05, 0x0b, 0x0c, 0xcc, 0xdd, 0x7a, 0x61, 0x69, 0x2e, 0x63, 0x6c, 0x6f,
- 0x75, 0xe4, 0xce, 0xa9, 0xe4, 0x04, 0x03, 0xce, 0xb1, 0xef, 0xc7, 0xe4,
- 0x61, 0xe9, 0xca, 0x07, 0xe1, 0x02, 0x8b, 0x6e, 0x61, 0x72, 0x65, 0x70,
- 0x75, 0x62, 0x6c, 0xe9, 0xc3, 0x5d, 0x6d, 0xe5, 0xc7, 0xde, 0x6d, 0xe2,
- 0x02, 0x83, 0xec, 0xcd, 0xa1, 0x69, 0x6e, 0xe1, 0xc6, 0xf9, 0xec, 0x05,
- 0x06, 0x1f, 0x11, 0x94, 0x74, 0x69, 0x6d, 0xef, 0xca, 0xdd, 0xf3, 0x02,
- 0x83, 0xe6, 0xcb, 0x21, 0x61, 0xee, 0x03, 0xcd, 0x0e, 0x2d, 0xf3, 0x02,
- 0x84, 0x1f, 0x43, 0xfc, 0x85, 0xf5, 0x02, 0x81, 0x65, 0x64, 0x74, 0x69,
- 0x72, 0x6f, 0xec, 0xcc, 0xfc, 0xec, 0x02, 0x88, 0x6f, 0x6f, 0x6e, 0x69,
- 0x6e, 0xe7, 0xcc, 0x0b, 0x61, 0x6e, 0x67, 0xe5, 0xc9, 0x17, 0xe5, 0x04,
- 0x05, 0xcc, 0x1f, 0x73, 0x74, 0xf2, 0xc2, 0x24, 0x6e, 0x61, 0x2d, 0x64,
- 0x65, 0x76, 0x69, 0xe3, 0xcd, 0xbd, 0xe1, 0x03, 0xc7, 0x61, 0x73, 0x68,
- 0x6f, 0xf6, 0xcc, 0xe6, 0x6a, 0x64, 0xe4, 0x86, 0x69, 0xe4, 0x03, 0xcd,
- 0xc4, 0xe1, 0xc3, 0x35, 0xe8, 0x03, 0xc4, 0x2f, 0xe3, 0x02, 0x81, 0x63,
- 0x61, 0x76, 0x75, 0x6f, 0xf4, 0xc4, 0x42, 0x67, 0x68, 0x64, 0xe1, 0xc9,
- 0xcf, 0x64, 0xe1, 0x02, 0x85, 0x6a, 0x6f, 0xfa, 0xcb, 0xe3, 0x64, 0x64,
- 0xea, 0xc8, 0xd7, 0x63, 0xeb, 0x03, 0x13, 0x8a, 0x79, 0x61, 0x72, 0x64,
- 0x73, 0x2e, 0x62, 0x61, 0x6e, 0x7a, 0x61, 0x69, 0x63, 0x6c, 0x6f, 0x75,
- 0xe4, 0xc0, 0xa2, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x61, 0x70, 0xf0, 0xcd,
- 0x75, 0x64, 0x72, 0x6f, 0xf0, 0xc6, 0x48, 0xe2, 0x02, 0x91, 0xf9, 0x04,
- 0x06, 0xcd, 0xd0, 0x6d, 0x69, 0x6c, 0xeb, 0xc6, 0x3a, 0x62, 0x6c, 0x75,
- 0xe5, 0xc6, 0x34, 0x69, 0x61, 0x2d, 0x67, 0x6f, 0x72, 0xe1, 0xc9, 0xd6,
- 0xae, 0x48, 0x54, 0x03, 0xc4, 0x00, 0xae, 0x03, 0xcc, 0xd6, 0xe2, 0x4c,
- 0xff, 0xc0, 0xb6, 0x2d, 0x64, 0x61, 0x74, 0xe1, 0xcd, 0x3c, 0xe1, 0x2b,
- 0x0c, 0x40, 0x4a, 0x21, 0x09, 0x2e, 0x31, 0x40, 0xa3, 0x40, 0x6b, 0x41,
- 0x2c, 0x41, 0x1c, 0x1a, 0x41, 0x0a, 0x36, 0x40, 0xea, 0x40, 0xb4, 0x40,
- 0xf0, 0x40, 0x85, 0x03, 0x40, 0xf6, 0x08, 0x40, 0x6f, 0x28, 0x3f, 0x40,
- 0x62, 0x41, 0x11, 0x40, 0x90, 0x9c, 0x1f, 0x43, 0x69, 0x72, 0x6f, 0x70,
- 0x6f, 0x72, 0x74, 0xae, 0xc2, 0x3a, 0xfa, 0x07, 0x31, 0x07, 0x48, 0x53,
- 0xc4, 0xe0, 0xf5, 0x02, 0xa8, 0x72, 0xe5, 0x06, 0x09, 0x0e, 0x4d, 0x1b,
- 0xae, 0x77, 0x65, 0x62, 0x73, 0x69, 0x74, 0xe5, 0xcd, 0x34, 0x63, 0x6f,
- 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x65, 0x72, 0x2e, 0x69, 0xef, 0xcd, 0x39,
- 0x2d, 0x6d, 0x6f, 0x62, 0x69, 0x6c, 0xe5, 0xcd, 0x1e, 0x6d, 0x69, 0x6e,
- 0xef, 0xca, 0xb5, 0x69, 0x6d, 0x75, 0x74, 0xe8, 0xc2, 0xe5, 0x65, 0x72,
- 0x62, 0x61, 0x69, 0x6a, 0x61, 0xee, 0xcb, 0xda, 0x79, 0xe1, 0x04, 0x04,
- 0x08, 0x87, 0x73, 0xe5, 0xc9, 0x2c, 0x67, 0x61, 0x77, 0x61, 0x2e, 0xeb,
- 0xc9, 0x28, 0x62, 0x65, 0x2e, 0x6b, 0xf9, 0xc6, 0xaa, 0x2e, 0x6d, 0x69,
- 0x79, 0x61, 0xfa, 0xc6, 0x40, 0xf8, 0x05, 0x4b, 0xfc, 0xc1, 0x06, 0xe9,
- 0xc6, 0x6d, 0xf7, 0x05, 0x1c, 0x08, 0xcc, 0xd5, 0xf3, 0x04, 0x05, 0xcc,
- 0xef, 0x6d, 0x70, 0xf0, 0xc6, 0xff, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c,
- 0x61, 0x63, 0x63, 0x65, 0x6c, 0x65, 0x72, 0x61, 0x74, 0xef, 0xc4, 0x15,
- 0x64, 0x65, 0x76, 0x2e, 0x63, 0xe1, 0xcc, 0xc5, 0x61, 0xea, 0xc8, 0xc7,
- 0xf6, 0x04, 0x10, 0x08, 0x8d, 0xef, 0x02, 0x85, 0x75, 0x65, 0xf3, 0xc9,
- 0x9b, 0x63, 0x61, 0x74, 0xae, 0x49, 0x96, 0xc2, 0x3e, 0x69, 0xe1, 0x03,
- 0xc2, 0xa9, 0xee, 0xcb, 0xaf, 0xe5, 0x02, 0x85, 0xf2, 0x41, 0xb2, 0xc3,
- 0x71, 0x6c, 0x6c, 0xe9, 0xc1, 0xf8, 0xae, 0x03, 0xcb, 0x3a, 0xf4, 0xcb,
- 0xeb, 0xf5, 0x0a, 0x30, 0x2b, 0x16, 0x04, 0x08, 0x13, 0x05, 0xcc, 0x00,
- 0xf4, 0x02, 0x8f, 0xef, 0x07, 0x48, 0x9d, 0x42, 0x1a, 0xc1, 0xd3, 0x6d,
- 0x6f, 0x74, 0x69, 0xf6, 0xc8, 0xde, 0xe8, 0x02, 0x86, 0x6f, 0xf2, 0x4a,
- 0x2e, 0xc2, 0x50, 0x67, 0x65, 0x61, 0xf2, 0x02, 0x85, 0x61, 0x70, 0xf0,
- 0xcb, 0xee, 0x2d, 0x73, 0x74, 0x61, 0x67, 0x69, 0x6e, 0xe7, 0xcc, 0x5f,
- 0xf3, 0x03, 0x16, 0x85, 0xf4, 0x03, 0x0c, 0x83, 0xf2, 0x02, 0x84, 0x68,
- 0xe5, 0xc1, 0x5d, 0x61, 0x6c, 0xe9, 0xc6, 0xda, 0xe9, 0xc2, 0x5a, 0xe5,
- 0xc1, 0x4a, 0x70, 0x6f, 0xf3, 0xcb, 0x24, 0x2e, 0x62, 0x61, 0x73, 0x6b,
- 0x65, 0x74, 0x62, 0x61, 0xec, 0xc2, 0x57, 0xf2, 0x04, 0x0d, 0xcb, 0x33,
- 0x73, 0x6b, 0x6f, 0x67, 0x2d, 0xe8, 0x02, 0x84, 0x1f, 0x43, 0xf8, 0x82,
- 0x6f, 0x6c, 0xe1, 0xc6, 0x7b, 0x6b, 0xf2, 0xc6, 0xf1, 0x67, 0x75, 0x73,
- 0x74, 0x6f, 0xf7, 0xc8, 0x2d, 0xe4, 0x02, 0x87, 0x6e, 0x65, 0x64, 0x61,
- 0xec, 0xc6, 0xd2, 0xe9, 0x05, 0x4b, 0x1e, 0xc0, 0xea, 0xe2, 0xca, 0xad,
- 0x63, 0x74, 0xe9, 0xc5, 0x89, 0xae, 0xc6, 0x99, 0xf4, 0x0b, 0x07, 0x0b,
- 0x03, 0x03, 0x1c, 0x0d, 0x04, 0x0e, 0xcb, 0x9e, 0x74, 0x6f, 0x72, 0x6e,
- 0xe5, 0xca, 0x4b, 0x73, 0xf5, 0x02, 0x83, 0xed, 0xca, 0x9d, 0x67, 0xe9,
- 0xc7, 0xec, 0xef, 0xcb, 0x25, 0xed, 0xc7, 0xee, 0xec, 0x02, 0x85, 0x61,
- 0x6e, 0xf4, 0xc6, 0x5a, 0x2e, 0x6a, 0x65, 0x6c, 0x61, 0x73, 0x74, 0x69,
- 0x63, 0x2e, 0x76, 0x70, 0x73, 0x2d, 0x68, 0x6f, 0x73, 0xf4, 0xcb, 0x9b,
- 0xe8, 0x02, 0x85, 0x6c, 0x65, 0xf4, 0xca, 0xb1, 0x2e, 0x63, 0xf8, 0xcb,
- 0xae, 0x61, 0xed, 0xc2, 0x50, 0xae, 0x06, 0x03, 0x46, 0x3c, 0xc3, 0xfd,
- 0xf6, 0xc9, 0x64, 0x6d, 0xe4, 0xcb, 0x9c, 0x2d, 0x62, 0x61, 0x6e, 0x64,
- 0x2d, 0x63, 0x61, 0x6d, 0xf0, 0xcb, 0x70, 0xf3, 0x13, 0x05, 0x0b, 0x40,
- 0x53, 0x0b, 0x06, 0x17, 0x03, 0x2d, 0x04, 0x03, 0x0e, 0x41, 0x3b, 0x44,
- 0x93, 0xc4, 0xe0, 0x75, 0x6b, 0xe5, 0xc6, 0xf6, 0xf4, 0x03, 0xc4, 0x27,
- 0x72, 0x6f, 0x6e, 0x6f, 0xed, 0xc3, 0x9e, 0xf3, 0x05, 0x33, 0x05, 0x05,
- 0x8c, 0xef, 0x02, 0x8e, 0x63, 0x69, 0x61, 0xf4, 0x02, 0x85, 0x69, 0x6f,
- 0xee, 0xc4, 0xd4, 0xe5, 0xc9, 0x86, 0xae, 0x0f, 0x02, 0x04, 0x03, 0x03,
- 0x03, 0x1a, 0x45, 0xb8, 0x42, 0x3c, 0x40, 0x58, 0xc0, 0x98, 0xee, 0x82,
- 0x6d, 0xe3, 0xcb, 0x43, 0xe7, 0xca, 0x44, 0xe4, 0xc9, 0x60, 0xe3, 0xc9,
- 0xba, 0xe2, 0xc9, 0x8d, 0x6e, 0x2e, 0xec, 0xc9, 0x62, 0x69, 0x73, 0xe9,
- 0xc9, 0x03, 0xe1, 0x02, 0x86, 0x73, 0x73, 0x69, 0xee, 0xc1, 0x15, 0xe2,
- 0xc7, 0x3a, 0x2e, 0xeb, 0xc9, 0x62, 0xee, 0x03, 0xc7, 0x92, 0xae, 0x03,
- 0xc8, 0x87, 0xec, 0xc8, 0x04, 0x6d, 0x61, 0x74, 0xe1, 0xc4, 0x6e, 0xeb,
- 0x06, 0x05, 0x03, 0x04, 0xc3, 0x6a, 0x76, 0x6f, 0xec, 0xc4, 0xcb, 0xef,
- 0xc3, 0xb5, 0x69, 0xed, 0xca, 0x05, 0x65, 0xf2, 0xca, 0x01, 0xe9, 0xc9,
- 0xec, 0xe8, 0x03, 0x05, 0x9d, 0x6f, 0x72, 0xef, 0xc9, 0xd0, 0xe9, 0x04,
- 0x0b, 0xc6, 0xec, 0x79, 0x61, 0xae, 0x03, 0xc6, 0xd2, 0x66, 0x75, 0xeb,
- 0xc1, 0x81, 0x6b, 0x61, 0x67, 0x61, 0x2e, 0x74, 0x6f, 0x63, 0x68, 0x69,
- 0xe7, 0xc9, 0x58, 0x67, 0x61, 0x62, 0x61, 0xe4, 0xc9, 0x74, 0x65, 0xf2,
- 0xc4, 0x8b, 0xe4, 0xc9, 0xb8, 0x63, 0x6f, 0x6c, 0xe9, 0x02, 0x81, 0x2d,
- 0x70, 0x69, 0x63, 0x65, 0xee, 0xc9, 0x45, 0xe1, 0x04, 0x0c, 0x13, 0xa1,
- 0x6d, 0x69, 0x6e, 0x61, 0x6d, 0x69, 0x2e, 0x68, 0x69, 0xf2, 0xc5, 0x4f,
- 0xeb, 0x02, 0x86, 0x75, 0x63, 0x68, 0xe9, 0xc5, 0x9f, 0xe1, 0x02, 0x84,
- 0x77, 0xe1, 0xc5, 0xe7, 0xae, 0xc1, 0x28, 0x68, 0xe9, 0x02, 0x85, 0x6b,
- 0x61, 0xf7, 0xc9, 0x3b, 0xae, 0x09, 0x06, 0x04, 0x43, 0x9f, 0x44, 0x48,
- 0xc1, 0x43, 0x79, 0x61, 0x6d, 0xe1, 0xc6, 0xfc, 0x74, 0xef, 0xc5, 0x7c,
- 0x6d, 0xe9, 0xc6, 0x6f, 0xe7, 0xc5, 0x2d, 0xf2, 0x13, 0x0b, 0x40, 0x6a,
- 0x03, 0x03, 0x08, 0x09, 0x1c, 0x0e, 0x1c, 0x07, 0x28, 0x43, 0x1a, 0x44,
- 0xda, 0xc1, 0x5d, 0x76, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0xf2,
- 0xc7, 0x94, 0xf4, 0x09, 0x1d, 0x08, 0x0e, 0x06, 0x06, 0x0b, 0xc9, 0xf3,
- 0xf3, 0x02, 0x8a, 0x61, 0x6e, 0x64, 0x63, 0x72, 0x61, 0x66, 0xf4, 0xc3,
- 0x9e, 0xae, 0x08, 0x03, 0x40, 0xb5, 0x47, 0x46, 0xc1, 0x3e, 0xf6, 0xc9,
- 0x4b, 0x6e, 0xe6, 0xca, 0x22, 0x67, 0x61, 0x6c, 0x6c, 0x65, 0xf2, 0xc2,
- 0x48, 0xe5, 0x05, 0x47, 0xf2, 0xc2, 0x20, 0x64, 0x75, 0x63, 0x61, 0x74,
- 0x69, 0xef, 0x95, 0x64, 0x65, 0x63, 0xef, 0xc7, 0xdc, 0x63, 0x65, 0x6e,
- 0xf4, 0xc3, 0x7c, 0x61, 0x6e, 0x64, 0x64, 0x65, 0x73, 0x69, 0x67, 0xee,
- 0xc7, 0xcb, 0xae, 0x09, 0x03, 0x04, 0x47, 0x10, 0x40, 0xa9, 0xc1, 0x71,
- 0xf3, 0xc8, 0x4a, 0x70, 0xec, 0xc9, 0xe0, 0xe4, 0x48, 0x04, 0xc0, 0xf3,
- 0xf1, 0xc9, 0x23, 0xee, 0xc4, 0xa8, 0xed, 0x03, 0xc8, 0x32, 0x65, 0xee,
- 0xc8, 0x7e, 0x6b, 0x68, 0x61, 0x6e, 0x67, 0x65, 0xec, 0xc4, 0x81, 0xe9,
- 0x03, 0x03, 0x8d, 0x74, 0xe1, 0x91, 0x64, 0xe1, 0x02, 0x84, 0x67, 0x61,
- 0x77, 0x61, 0x2e, 0x77, 0xe1, 0xc4, 0xbf, 0x61, 0x6b, 0x65, 0x2e, 0x73,
- 0xe1, 0xc5, 0x22, 0xe5, 0x04, 0x03, 0xc8, 0x35, 0xee, 0xc3, 0x6d, 0x6d,
- 0x61, 0x72, 0xeb, 0xc8, 0xaa, 0x63, 0xe8, 0x02, 0x89, 0xe9, 0x03, 0xc9,
- 0x94, 0x74, 0x65, 0xe3, 0xc5, 0xe7, 0x61, 0x65, 0x6f, 0x6c, 0x6f, 0xe7,
- 0x03, 0xc1, 0xb4, 0x69, 0x63, 0x61, 0xec, 0xc7, 0x58, 0x62, 0x6f, 0x72,
- 0x65, 0xf4, 0xc0, 0x43, 0xe1, 0x05, 0x03, 0x04, 0x0e, 0x8a, 0xef, 0xc3,
- 0x04, 0x6d, 0xe3, 0xc8, 0x84, 0x6b, 0x61, 0x77, 0x61, 0xae, 0x03, 0xc6,
- 0xba, 0x73, 0x61, 0x69, 0xf4, 0xc4, 0x6e, 0x69, 0x2e, 0x73, 0x68, 0x69,
- 0x7a, 0x75, 0xef, 0xc8, 0x1f, 0xe2, 0xc9, 0x53, 0xae, 0x45, 0x1a, 0x42,
- 0xcd, 0xc1, 0x62, 0xf1, 0x05, 0x47, 0xda, 0xc1, 0x6a, 0xf5, 0x02, 0x84,
- 0x69, 0xec, 0xc3, 0x9d, 0x61, 0xf2, 0x02, 0x85, 0x69, 0x75, 0xed, 0xc7,
- 0x0c, 0x65, 0xec, 0xc7, 0xd8, 0xf0, 0x06, 0x40, 0x93, 0x18, 0x12, 0x8e,
- 0xf0, 0x08, 0x05, 0x31, 0x07, 0x07, 0x07, 0xc8, 0xd5, 0x75, 0x64, 0xef,
- 0xc8, 0xf9, 0xf3, 0x02, 0x9e, 0xf0, 0x02, 0x82, 0xef, 0x90, 0x61, 0x63,
- 0xe5, 0x02, 0x8d, 0x75, 0x73, 0x65, 0x72, 0x63, 0x6f, 0x6e, 0x74, 0x65,
- 0x6e, 0xf4, 0xc8, 0xfb, 0x68, 0x6f, 0x73, 0x74, 0xe5, 0xc4, 0x66, 0xae,
- 0x02, 0x86, 0x6c, 0x61, 0x69, 0xf2, 0xc8, 0x82, 0x66, 0x62, 0x73, 0x62,
- 0xf8, 0xc8, 0xe4, 0xec, 0x03, 0xc8, 0x0b, 0x69, 0xee, 0x8b, 0x65, 0x6e,
- 0x67, 0x69, 0xee, 0xc2, 0xfb, 0x63, 0x68, 0x69, 0x7a, 0xe9, 0xc8, 0xcf,
- 0xae, 0x05, 0x08, 0x18, 0x06, 0x83, 0x72, 0x65, 0x6e, 0x64, 0x65, 0xf2,
- 0xc8, 0xc1, 0x6f, 0x73, 0xae, 0x02, 0x84, 0x73, 0x74, 0x67, 0x2e, 0x66,
- 0x65, 0x64, 0x6f, 0x72, 0x61, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0xf4,
- 0xc6, 0x6e, 0x6c, 0x6d, 0x70, 0xed, 0xc8, 0xa3, 0xe7, 0xc7, 0xd6, 0xe2,
- 0x03, 0xc7, 0xeb, 0x61, 0x6e, 0x7a, 0x61, 0x69, 0x63, 0x6c, 0x6f, 0x75,
- 0xe4, 0xc8, 0x27, 0xe9, 0x02, 0x85, 0x67, 0x65, 0xe5, 0xc8, 0x1f, 0xae,
- 0x02, 0x88, 0x73, 0x74, 0x64, 0x6c, 0x69, 0xe2, 0xc8, 0x7d, 0x67, 0x6f,
- 0xf6, 0xc5, 0xc9, 0x61, 0xf2, 0x02, 0x87, 0x74, 0x6d, 0x65, 0x6e, 0xf4,
- 0xc6, 0xa2, 0x65, 0x63, 0x69, 0x64, 0xe1, 0xc7, 0xb5, 0xae, 0x05, 0x42,
- 0xfa, 0xc4, 0x03, 0x67, 0x6f, 0x76, 0xae, 0x44, 0xa7, 0xc3, 0x03, 0xad,
- 0x02, 0x90, 0x73, 0x6f, 0x75, 0x74, 0xe8, 0x02, 0x87, 0x65, 0x61, 0x73,
- 0x74, 0xad, 0x13, 0x82, 0xad, 0x92, 0x6e, 0x6f, 0x72, 0x74, 0x68, 0x65,
- 0x61, 0x73, 0x74, 0xad, 0x03, 0x02, 0x82, 0xb3, 0x84, 0xb2, 0x82, 0x31,
- 0x2e, 0x65, 0x6c, 0x61, 0x73, 0x74, 0x69, 0x63, 0x62, 0x65, 0x61, 0x6e,
- 0x73, 0x74, 0x61, 0x6c, 0xeb, 0xc8, 0x20, 0xef, 0x0a, 0x13, 0x0f, 0x03,
- 0x46, 0x3e, 0x40, 0x4e, 0xc1, 0x6a, 0x73, 0xf4, 0x03, 0xc1, 0xf3, 0xe1,
- 0x04, 0x01, 0xc6, 0xa6, 0x2d, 0x76, 0x61, 0x6c, 0x6c, 0x65, 0xf9, 0xc6,
- 0xa0, 0x6d, 0x6f, 0x72, 0x69, 0xae, 0x03, 0xc6, 0xf1, 0x61, 0x6f, 0x6d,
- 0x6f, 0xf2, 0xc6, 0x87, 0xeb, 0xc5, 0x70, 0x67, 0xe1, 0x41, 0x94, 0xc1,
- 0x48, 0xee, 0x0e, 0x12, 0x04, 0x0c, 0x20, 0x04, 0x07, 0x40, 0x67, 0x05,
- 0x45, 0x4b, 0xc0, 0x73, 0xf4, 0x03, 0xc1, 0x45, 0x68, 0x72, 0xef, 0x03,
- 0xc5, 0xad, 0x70, 0x6f, 0x6c, 0x6f, 0x67, 0xf9, 0xc5, 0xa7, 0x71, 0xf5,
- 0xc4, 0x5d, 0x70, 0x61, 0x63, 0x68, 0x69, 0x2e, 0x67, 0x69, 0x66, 0xf5,
- 0xc6, 0xb1, 0xee, 0x03, 0x08, 0x89, 0x65, 0x66, 0x72, 0x61, 0x6e, 0xeb,
- 0xc5, 0x8b, 0x61, 0x6b, 0x61, 0x2e, 0x67, 0x75, 0xee, 0xc3, 0x0c, 0x2d,
- 0x61, 0x72, 0x62, 0x6f, 0x72, 0x2e, 0x6d, 0xe9, 0xc2, 0xc1, 0x6a, 0xef,
- 0xc3, 0x18, 0x67, 0x72, 0x79, 0x2e, 0xea, 0xc6, 0xc4, 0xe4, 0x06, 0x05,
- 0x3d, 0x0c, 0x04, 0x88, 0x1f, 0x43, 0xf8, 0xc0, 0x42, 0xf2, 0x02, 0x84,
- 0x6f, 0xe9, 0xc5, 0xce, 0x69, 0xe1, 0x03, 0x06, 0x89, 0x74, 0x72, 0x61,
- 0x6e, 0xe9, 0x93, 0x62, 0x61, 0x72, 0x6c, 0x65, 0x74, 0x74, 0xe1, 0x9c,
- 0xad, 0x02, 0x8f, 0x74, 0x72, 0x61, 0x6e, 0x69, 0x2d, 0x62, 0x61, 0x72,
- 0x6c, 0x65, 0x74, 0xf4, 0xc1, 0xbb, 0x62, 0x61, 0x72, 0x6c, 0x65, 0x74,
- 0x74, 0x61, 0x2d, 0x74, 0x72, 0x61, 0x6e, 0xe9, 0xc5, 0xe3, 0xef, 0x02,
- 0x83, 0xf9, 0xc6, 0x53, 0x2e, 0x6e, 0x61, 0xf2, 0xc6, 0x0b, 0x65, 0xe2,
- 0xc1, 0xbe, 0x61, 0x73, 0x75, 0x6f, 0x6c, 0xef, 0xc6, 0x41, 0x2e, 0xed,
- 0x45, 0x09, 0xc2, 0x22, 0x63, 0x6f, 0xee, 0xc1, 0x88, 0xe1, 0x03, 0x0b,
- 0x8c, 0xee, 0x02, 0x83, 0xe9, 0xc6, 0x67, 0xae, 0x42, 0x6d, 0xc2, 0x2a,
- 0x6d, 0x69, 0x7a, 0x75, 0x2e, 0x69, 0x73, 0x68, 0x69, 0xeb, 0xc3, 0x45,
- 0x6c, 0x79, 0x74, 0x69, 0xe3, 0xc5, 0x35, 0xed, 0x0c, 0x08, 0x16, 0x04,
- 0x04, 0x0d, 0x04, 0x31, 0x11, 0x28, 0xc6, 0x58, 0x75, 0x73, 0x65, 0x6d,
- 0x65, 0xee, 0xc2, 0xb9, 0xf3, 0x02, 0x8a, 0x74, 0x65, 0x72, 0x64, 0x61,
- 0xed, 0x44, 0xbe, 0xc2, 0x2a, 0x63, 0x6f, 0x6d, 0x70, 0x75, 0x74, 0xe5,
- 0xc6, 0xd5, 0x6f, 0xf4, 0xc5, 0xe5, 0x6c, 0xe9, 0xc5, 0xe1, 0xe9, 0x03,
- 0xc5, 0xc8, 0x2e, 0x69, 0x62, 0x61, 0x72, 0x61, 0xeb, 0xc5, 0x54, 0x66,
- 0xe1, 0xc5, 0x09, 0xe5, 0x02, 0x83, 0xf8, 0xc6, 0xbe, 0x72, 0x69, 0x63,
- 0x61, 0xee, 0x05, 0x07, 0x08, 0xc4, 0x79, 0x66, 0x61, 0x6d, 0x69, 0xec,
- 0xc5, 0x0c, 0x65, 0x78, 0x70, 0x72, 0x65, 0xf3, 0xc4, 0xd2, 0xe1, 0x04,
- 0x04, 0xc4, 0x70, 0x72, 0xf4, 0xc4, 0x72, 0x6e, 0x74, 0x69, 0x71, 0x75,
- 0x65, 0xf3, 0xc4, 0x69, 0xe2, 0x02, 0x8a, 0x75, 0x6c, 0x61, 0x6e, 0x63,
- 0x65, 0xae, 0x44, 0x38, 0xa6, 0x65, 0xf2, 0xc4, 0x58, 0xe1, 0x05, 0x04,
- 0x04, 0x0e, 0x87, 0x7a, 0xef, 0xc4, 0xda, 0x6d, 0xe9, 0xc1, 0x20, 0x6b,
- 0x75, 0x73, 0x61, 0x2e, 0x6b, 0x75, 0x6d, 0x61, 0x6d, 0x6f, 0xf4, 0xc5,
- 0x55, 0x67, 0x61, 0x73, 0x61, 0xeb, 0xc2, 0x53, 0xae, 0x41, 0x70, 0xc0,
- 0x68, 0xae, 0x40, 0xeb, 0x03, 0xc4, 0xb1, 0xec, 0x0f, 0x0a, 0x06, 0x1b,
- 0x13, 0x2a, 0x12, 0x0c, 0x04, 0x08, 0x11, 0x0d, 0x1c, 0xc5, 0x76, 0x77,
- 0x61, 0x79, 0x73, 0x64, 0x61, 0x74, 0xe1, 0xc6, 0x16, 0x76, 0x64, 0x61,
- 0xec, 0xc5, 0x40, 0xf4, 0x06, 0x0b, 0x40, 0xef, 0xc1, 0x8d, 0xef, 0x02,
- 0x81, 0x2d, 0x61, 0x64, 0x69, 0x67, 0xe5, 0xc4, 0xb8, 0x65, 0x72, 0x76,
- 0x69, 0x73, 0x74, 0xe1, 0xc3, 0xd6, 0xf3, 0x02, 0x8d, 0xf4, 0x02, 0x83,
- 0xef, 0xc4, 0x55, 0x61, 0x68, 0x61, 0x75, 0xe7, 0xc5, 0x15, 0xe1, 0xc1,
- 0xc5, 0xf0, 0x02, 0x9b, 0x68, 0xe1, 0x02, 0x8e, 0x2e, 0x62, 0x6f, 0x75,
- 0x6e, 0x74, 0x79, 0x2d, 0x66, 0x75, 0x6c, 0xec, 0xc5, 0xe9, 0x2d, 0x6d,
- 0x79, 0x71, 0x6e, 0x61, 0xf0, 0xc1, 0x4e, 0x31, 0x2e, 0x61, 0x65, 0x2e,
- 0x66, 0x6c, 0x6f, 0x77, 0xae, 0xc5, 0xa1, 0xec, 0x04, 0x06, 0xc4, 0x2e,
- 0x73, 0x74, 0x61, 0xf4, 0xc4, 0xf6, 0x66, 0x69, 0x6e, 0x61, 0xee, 0xc3,
- 0xed, 0xe9, 0x02, 0x84, 0x70, 0xe1, 0xc4, 0x22, 0x62, 0x61, 0xe2, 0xc4,
- 0xb8, 0x67, 0xe1, 0xc2, 0x5e, 0x66, 0x61, 0x72, 0x6f, 0x6d, 0xe5, 0xc4,
- 0xc8, 0x65, 0xf3, 0x02, 0x84, 0x75, 0xee, 0xc2, 0x4f, 0x73, 0x61, 0x6e,
- 0x64, 0x72, 0x69, 0xe1, 0xc4, 0x37, 0x63, 0x65, 0x73, 0x2e, 0x6e, 0x65,
- 0x74, 0x77, 0x6f, 0x72, 0xeb, 0xc5, 0x82, 0xe1, 0x04, 0x03, 0x06, 0x88,
- 0x73, 0xeb, 0x92, 0x6e, 0x64, 0x2e, 0xe6, 0xc4, 0x06, 0x68, 0x65, 0x61,
- 0x64, 0x6a, 0xf5, 0xc4, 0x8a, 0x62, 0x61, 0x6d, 0xe1, 0xc3, 0x4e, 0xae,
- 0x09, 0x03, 0x03, 0x41, 0x30, 0x42, 0xcd, 0xc0, 0x76, 0xec, 0xc3, 0x9e,
- 0xe7, 0xc3, 0xb9, 0x65, 0xf5, 0xc3, 0x21, 0xeb, 0x0a, 0x0a, 0x0a, 0x08,
- 0x03, 0x0a, 0x06, 0x22, 0x03, 0xa3, 0x75, 0x6e, 0x65, 0x2e, 0x6b, 0x61,
- 0x67, 0xef, 0xc0, 0xac, 0x74, 0x79, 0x75, 0x62, 0x69, 0x6e, 0x73, 0xeb,
- 0xc3, 0xf4, 0x72, 0x65, 0x68, 0x61, 0x6d, 0xee, 0xc4, 0x49, 0xef, 0xc1,
- 0x2f, 0x6e, 0x6f, 0x6c, 0x75, 0x6f, 0x6b, 0x74, 0xe1, 0xc4, 0x3c, 0x6b,
- 0x65, 0x73, 0xe8, 0xc4, 0x10, 0xe9, 0x04, 0x0b, 0x07, 0x86, 0x74, 0x61,
- 0xae, 0x03, 0xc4, 0x0e, 0x61, 0x6b, 0xe9, 0xc1, 0xa5, 0x73, 0x68, 0x69,
- 0x6d, 0xe1, 0xc2, 0x68, 0x72, 0x75, 0x6e, 0xef, 0xc2, 0x62, 0x2e, 0x6b,
- 0xef, 0xc3, 0x92, 0xe4, 0xc3, 0x67, 0xe1, 0x04, 0x04, 0x0b, 0x8b, 0x73,
- 0xe8, 0xc0, 0xf0, 0x69, 0x77, 0x61, 0x2e, 0x6f, 0x6b, 0x61, 0x79, 0xe1,
- 0xc0, 0x51, 0x67, 0x69, 0x2e, 0x73, 0x68, 0x69, 0x6d, 0x61, 0xee, 0xc0,
- 0xea, 0x62, 0xe9, 0xc3, 0x99, 0xae, 0xc0, 0xa9, 0xea, 0xc4, 0x22, 0xe9,
- 0x10, 0x2c, 0x0a, 0x13, 0x40, 0x6e, 0x05, 0x09, 0x0b, 0x0b, 0x03, 0x42,
- 0x75, 0x41, 0x75, 0x81, 0x7a, 0xf5, 0x03, 0x0a, 0x8d, 0x77, 0x61, 0x6b,
- 0x61, 0x6d, 0x61, 0x74, 0x73, 0xf5, 0x93, 0x6d, 0xe9, 0x02, 0x85, 0x73,
- 0x61, 0x74, 0xef, 0x8a, 0x2e, 0x74, 0xef, 0x89, 0x62, 0x61, 0x6e, 0x67,
- 0x65, 0x2e, 0x66, 0x75, 0x6b, 0x75, 0x73, 0x68, 0x69, 0xed, 0xc3, 0x69,
- 0x76, 0x65, 0x6e, 0x63, 0x6c, 0x6f, 0x75, 0xe4, 0xc4, 0x8d, 0xf3, 0x02,
- 0x89, 0x68, 0x6f, 0x2e, 0x73, 0x68, 0x69, 0xe7, 0xc3, 0x53, 0x61, 0x69,
- 0x2e, 0x61, 0xe9, 0xc3, 0x0c, 0xf2, 0x0b, 0x0c, 0x04, 0x05, 0x14, 0x07,
- 0x06, 0x07, 0x04, 0xc2, 0x09, 0xf4, 0x02, 0x86, 0x72, 0x61, 0x66, 0xe6,
- 0xc1, 0x56, 0xe5, 0xc2, 0xb2, 0x70, 0x6f, 0xf2, 0xab, 0x6c, 0x69, 0xee,
- 0xc1, 0x3e, 0x6b, 0x69, 0x74, 0x61, 0x70, 0x70, 0xf3, 0x02, 0x87, 0xae,
- 0x03, 0xc4, 0x4b, 0xe5, 0xc3, 0x02, 0x2d, 0xe1, 0xc3, 0xb4, 0x67, 0x75,
- 0x61, 0x72, 0xe4, 0xc2, 0x1c, 0x66, 0x6f, 0x72, 0xe3, 0xc3, 0x65, 0x63,
- 0x72, 0x61, 0x66, 0xf4, 0xc1, 0xe9, 0x62, 0xf5, 0xc2, 0x62, 0xad, 0x02,
- 0x91, 0x74, 0x72, 0x61, 0x66, 0x66, 0x69, 0x63, 0x2d, 0x63, 0x6f, 0x6e,
- 0x74, 0x72, 0x6f, 0xec, 0xc1, 0xd1, 0x73, 0x75, 0x72, 0x76, 0x65, 0x69,
- 0x6c, 0x6c, 0x61, 0x6e, 0xe3, 0xc0, 0xf1, 0x70, 0x2e, 0xe5, 0xc3, 0x34,
- 0x6f, 0x69, 0x2e, 0x68, 0x79, 0x6f, 0xe7, 0xc2, 0xf5, 0x6e, 0x61, 0x6e,
- 0x2e, 0x65, 0x68, 0x69, 0x6d, 0xe5, 0xc2, 0xeb, 0x6b, 0x61, 0x77, 0x61,
- 0x2e, 0x6b, 0x61, 0x6e, 0x61, 0xe7, 0xa5, 0x64, 0xae, 0xb1, 0x62, 0x65,
- 0x74, 0x73, 0xf5, 0xc2, 0xcd, 0x68, 0xae, 0x03, 0xc2, 0xec, 0xe3, 0xc2,
- 0x40, 0xe7, 0x09, 0x0c, 0x2a, 0x14, 0x07, 0x42, 0x17, 0xc1, 0x6a, 0x75,
- 0x6e, 0x69, 0x2e, 0x6f, 0x6b, 0x69, 0x6e, 0x61, 0xf7, 0xc2, 0x92, 0xf2,
- 0x05, 0x08, 0x18, 0xc2, 0xe7, 0x6f, 0xae, 0x03, 0xc1, 0x9c, 0xf0, 0xc2,
- 0x02, 0xe9, 0x02, 0x86, 0x67, 0x65, 0x6e, 0xf4, 0xc2, 0x46, 0xe3, 0x02,
- 0x88, 0x75, 0x6c, 0x74, 0x75, 0x72, 0xe5, 0xc1, 0x7c, 0x2e, 0xfa, 0xc2,
- 0x9c, 0x61, 0xf2, 0xc3, 0x34, 0xe5, 0x02, 0x8a, 0xee, 0x02, 0x84, 0x74,
- 0xf3, 0xc1, 0x44, 0xe3, 0xc1, 0xf0, 0x6d, 0x61, 0x74, 0x73, 0xf5, 0xc1,
- 0x03, 0x64, 0x65, 0x6e, 0x65, 0xf3, 0xc2, 0x8f, 0xe1, 0x03, 0x02, 0x89,
- 0x6e, 0x6f, 0x2e, 0x6e, 0x69, 0x69, 0x67, 0x61, 0xf4, 0xc2, 0x3e, 0x6b,
- 0x68, 0xe1, 0xc1, 0xd1, 0xe6, 0x07, 0x07, 0x06, 0x41, 0xa0, 0xc1, 0xb8,
- 0x72, 0x69, 0x63, 0xe1, 0x43, 0x59, 0x88, 0x6a, 0x6f, 0x72, 0xe4, 0xc2,
- 0x66, 0x66, 0x69, 0x6e, 0x69, 0x74, 0x79, 0x6c, 0x6f, 0x74, 0x74, 0x65,
- 0x72, 0x79, 0x2e, 0x6f, 0x72, 0xe7, 0xc0, 0x90, 0xe5, 0x08, 0x04, 0x2d,
- 0x40, 0xc8, 0x42, 0x42, 0x81, 0x74, 0xee, 0xc2, 0x34, 0x72, 0xef, 0x07,
- 0x08, 0x07, 0x06, 0x07, 0xc3, 0x13, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0xe6,
- 0xc2, 0x72, 0x64, 0x72, 0x6f, 0x6d, 0xe5, 0xc0, 0xd2, 0x63, 0x6c, 0x75,
- 0xe2, 0xc0, 0xcc, 0x62, 0x61, 0x74, 0x69, 0xe3, 0xc0, 0xc5, 0xae, 0x03,
- 0xc1, 0xea, 0x6d, 0xf6, 0xc3, 0x0d, 0x6a, 0x72, 0xe9, 0xc2, 0x13, 0xe4,
- 0x0d, 0x0c, 0x13, 0x0a, 0x0f, 0x03, 0x0b, 0x40, 0xdf, 0x40, 0xc3, 0xc1,
- 0x10, 0x79, 0x67, 0x65, 0x79, 0x61, 0xae, 0x03, 0xc1, 0x9f, 0xf2, 0xc1,
- 0x9f, 0xf6, 0x02, 0x89, 0x69, 0x73, 0x6f, 0x72, 0x2e, 0x77, 0xf3, 0xc2,
- 0xd0, 0xae, 0x03, 0xc2, 0x24, 0xed, 0xc0, 0xfe, 0x75, 0x6c, 0xf4, 0x03,
- 0xc2, 0xd3, 0x2e, 0xe8, 0xc1, 0xad, 0x6f, 0x62, 0x65, 0x61, 0x65, 0x6d,
- 0x63, 0x6c, 0x6f, 0x75, 0x64, 0xae, 0x42, 0xa0, 0x9c, 0xed, 0xc2, 0x06,
- 0x69, 0x6d, 0x6f, 0x2e, 0x63, 0x6f, 0x2e, 0x75, 0xeb, 0xc2, 0xb0, 0x61,
- 0xe3, 0x03, 0xc2, 0xad, 0x68, 0x69, 0x2e, 0x74, 0x6f, 0x6b, 0xf9, 0xc1,
- 0x95, 0xe3, 0x0a, 0x10, 0x0a, 0x40, 0x48, 0x1b, 0x41, 0x32, 0xc0, 0xea,
- 0xf4, 0x02, 0x83, 0xef, 0xc1, 0xde, 0xae, 0x02, 0x84, 0x65, 0x64, 0x75,
- 0x2e, 0xe1, 0xc2, 0x23, 0x68, 0x69, 0x2e, 0x6e, 0x61, 0x67, 0x61, 0xee,
- 0xc1, 0x70, 0xe3, 0x04, 0x03, 0x0b, 0xa1, 0xf4, 0xc1, 0x8c, 0x6f, 0x75,
- 0x6e, 0x74, 0x61, 0x6e, 0xf4, 0x40, 0x9d, 0xc1, 0xd3, 0x69, 0x64, 0x65,
- 0x6e, 0x74, 0xad, 0x02, 0x87, 0x70, 0x72, 0x65, 0x76, 0x65, 0xee, 0x8a,
- 0x69, 0x6e, 0x76, 0x65, 0x73, 0x74, 0x69, 0x67, 0x61, 0x74, 0x69, 0x6f,
- 0x6e, 0x2e, 0x61, 0xe5, 0xc1, 0x62, 0xe5, 0x02, 0x8b, 0x73, 0x73, 0x63,
- 0x61, 0x6d, 0x2e, 0x6f, 0x72, 0xe7, 0xc2, 0x3b, 0x6e, 0x74, 0x75, 0xf2,
- 0xc1, 0x5e, 0xe1, 0x03, 0xc1, 0x46, 0x64, 0x65, 0xed, 0x02, 0x8c, 0xf9,
- 0x03, 0xc2, 0x2a, 0x2e, 0x6d, 0x75, 0x73, 0x65, 0xf5, 0xc0, 0x69, 0x69,
- 0x61, 0x2e, 0xe2, 0xc1, 0x34, 0xae, 0x13, 0x05, 0x03, 0x04, 0x05, 0x04,
- 0x05, 0x05, 0x04, 0x0e, 0x11, 0x04, 0x10, 0x07, 0x04, 0x0c, 0x03, 0xc0,
- 0x89, 0xfa, 0x2d, 0x1f, 0xc0, 0xb5, 0xf6, 0xc0, 0x64, 0xf5, 0x2f, 0xc1,
- 0xcf, 0xf4, 0x1e, 0x33, 0xc0, 0x9f, 0xf3, 0x19, 0xc1, 0x02, 0xf2, 0x18,
- 0x07, 0xc0, 0x84, 0xf0, 0x40, 0xe7, 0xc0, 0x4f, 0xee, 0x0b, 0xc0, 0x5d,
- 0xed, 0x07, 0x03, 0x40, 0xd4, 0x2b, 0xc0, 0x73, 0xfa, 0xc1, 0xdc, 0xf7,
- 0xc1, 0xd9, 0xec, 0x03, 0x03, 0x83, 0xf3, 0xc1, 0xd2, 0xeb, 0xc1, 0xcf,
- 0x65, 0x67, 0x2e, 0x62, 0xf2, 0xc1, 0xc4, 0xeb, 0x40, 0xea, 0xa4, 0xe9,
- 0x06, 0x03, 0x03, 0x17, 0xc0, 0xe7, 0xed, 0xc1, 0xba, 0xec, 0xc1, 0xb7,
- 0xe4, 0xc1, 0xb4, 0xe7, 0x02, 0x91, 0x6f, 0xf6, 0xc0, 0xf4, 0x66, 0xea,
- 0xc1, 0xa9, 0xe3, 0x05, 0x03, 0x1e, 0xc0, 0xc9, 0xf9, 0xc1, 0xa0, 0xee,
- 0xc1, 0x9d, 0xe2, 0xc0, 0xbf, 0xe1, 0x40, 0x73, 0xc0, 0x49, 0xe2, 0x0b,
- 0x16, 0x0b, 0x0c, 0x03, 0x0b, 0x0e, 0x0a, 0x05, 0x0e, 0x99, 0xf5, 0x02,
- 0x87, 0x64, 0x68, 0x61, 0x62, 0xe9, 0xc1, 0x7f, 0x2e, 0x79, 0x61, 0x6d,
- 0x61, 0x67, 0x75, 0x63, 0x68, 0xe9, 0xc0, 0x63, 0xf2, 0x02, 0x84, 0x75,
- 0x7a, 0x7a, 0x6f, 0x2e, 0xe9, 0xc0, 0x44, 0xef, 0x02, 0x85, 0x67, 0x61,
- 0xe4, 0xc0, 0x76, 0x2e, 0xf0, 0xc0, 0x56, 0xec, 0xc0, 0x7e, 0x6b, 0x68,
- 0x61, 0x7a, 0x69, 0x61, 0x2e, 0x73, 0xf5, 0xc1, 0x4a, 0xe9, 0x02, 0x83,
- 0x72, 0xe1, 0xae, 0x6b, 0x6f, 0x2e, 0x63, 0x68, 0x69, 0xe2, 0x89, 0x65,
- 0x6e, 0x6f, 0x2e, 0x6f, 0x73, 0x61, 0x6b, 0xe1, 0xa5, 0xe3, 0x40, 0x7a,
- 0xc0, 0xb9, 0xe2, 0x04, 0x04, 0xc1, 0x26, 0x76, 0xe9, 0xc0, 0x4d, 0x6f,
- 0x74, 0xf4, 0xc1, 0x23, 0xe1, 0x02, 0x91, 0x73, 0x68, 0x69, 0x72, 0x69,
- 0x2e, 0x68, 0x6f, 0x6b, 0x6b, 0x61, 0x69, 0x64, 0x6f, 0x2e, 0xea, 0x92,
- 0x72, 0x74, 0xe8, 0xc1, 0x0a, 0x2e, 0x63, 0xe1, 0xc1, 0x05, 0xe1, 0x03,
- 0x0b, 0x83, 0xf2, 0x02, 0x83, 0xf0, 0xc0, 0xfb, 0x62, 0x6f, 0x72, 0x74,
- 0x65, 0x2e, 0xee, 0x88, 0xe1, 0x03, 0xc0, 0xed, 0x2e, 0x70, 0x72, 0xef,
- 0xc0, 0xe9, 0xae, 0x05, 0x09, 0x09, 0xc0, 0xcd, 0xf3, 0x02, 0x83, 0x73,
- 0xec, 0x91, 0xe5, 0xc0, 0xda, 0x72, 0x75, 0x6e, 0x2e, 0x61, 0x70, 0xf0,
- 0xc0, 0xcd, 0x70, 0x72, 0x6f, 0x64, 0x2e, 0x66, 0x61, 0x73, 0x74, 0x6c,
- 0xf9, 0xc0, 0xa0, 0xb9, 0x03, 0xc0, 0xbb, 0x67, 0x75, 0x61, 0x63, 0x75,
- 0x2e, 0x62, 0xf2, 0xc0, 0xb6, 0xb8, 0xc0, 0xb0, 0xb7, 0xc0, 0xad, 0xb6,
- 0x04, 0x03, 0xc0, 0xa3, 0xb4, 0xc0, 0x50, 0x31, 0x31, 0x2e, 0xf4, 0xb3,
- 0xb5, 0xc0, 0x9d, 0xb4, 0x04, 0x03, 0xc0, 0x93, 0xf5, 0xc0, 0x90, 0x6c,
- 0x69, 0x6d, 0xe1, 0xc0, 0x50, 0xb3, 0x04, 0x0b, 0xc0, 0x7d, 0x75, 0x74,
- 0x69, 0x6c, 0x69, 0x74, 0x69, 0x65, 0xf3, 0xc0, 0x7a, 0xb2, 0xa7, 0xb2,
- 0x04, 0x03, 0xc0, 0x73, 0x69, 0xf8, 0xb5, 0xb0, 0x02, 0x87, 0x33, 0x38,
- 0x2e, 0x69, 0xef, 0xc0, 0x6a, 0x30, 0x30, 0x2e, 0x68, 0xf5, 0xc0, 0x67,
- 0xb1, 0x05, 0x06, 0x06, 0x0e, 0x8f, 0x6b, 0x61, 0x70, 0xf0, 0xc0, 0x53,
- 0x36, 0x2d, 0x62, 0x2e, 0xe9, 0xb3, 0x33, 0x33, 0x37, 0x2e, 0x70, 0x69,
- 0x63, 0x74, 0x75, 0x72, 0x65, 0xf3, 0xc0, 0x43, 0x32, 0x68, 0x70, 0xae,
- 0x03, 0x03, 0x83, 0x64, 0xe5, 0xb8, 0x63, 0xe8, 0xb5, 0xe1, 0x96, 0xae,
- 0x02, 0xb1, 0x61, 0x7a, 0x75, 0x72, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69,
- 0x63, 0x61, 0x70, 0x70, 0x73, 0x2e, 0x6e, 0x65, 0xf4, 0x9c, 0xb0, 0x03,
- 0x0e, 0x8a, 0xe5, 0x02, 0x87, 0x6d, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x8a,
- 0x2e, 0x76, 0xe3, 0x8a, 0x30, 0x31, 0x77, 0x77, 0x77, 0x2e, 0x63, 0x6f,
- 0x6d, 0x88, 0x2e, 0x62, 0x67, 0x84, 0x01,
-};
diff --git a/src/network/kernel/qurltlds_p.h.INFO b/src/network/kernel/qurltlds_p.h.INFO
deleted file mode 100644
index d6162a2a0d..0000000000
--- a/src/network/kernel/qurltlds_p.h.INFO
+++ /dev/null
@@ -1,17 +0,0 @@
-The file qurltlds_p.h is generated from the Public Suffix
-List (see [1] and [2]), by the program residing at
-qtbase/src/3rdparty/libpsl/src/psl-make-dafsa in the Qt source tree.
-
-To regenerate the file, run the following command from qtbase tree:
-
- src/3rdparty/libpsl/src/psl-make-dafsa public_suffix_list.dat src/network/kernel/qurltlds_p.h
- src/3rdparty/libpsl/src/psl-make-dafsa --output-format=binary public_suffix_list.dat \
- tests/auto/network/access/qnetworkcookiejar/testdata/publicsuffix/public_suffix_list.dafsa
-
-Those arrays in qurltlds_p.h are derived from the Public
-Suffix List ([2]), which was originally provided by
-Jo Hermans <jo.hermans@gmail.com>.
-
-----
-[1] list: https://publicsuffix.org/list/public_suffix_list.dat
-[2] homepage: https://publicsuffix.org/
diff --git a/src/network/qt_cmdline.cmake b/src/network/qt_cmdline.cmake
index 95fc6315ba..0b4c0c239a 100644
--- a/src/network/qt_cmdline.cmake
+++ b/src/network/qt_cmdline.cmake
@@ -1,10 +1,7 @@
# Copyright (C) 2022 The Qt Company Ltd.
-# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+# SPDX-License-Identifier: BSD-3-Clause
qt_commandline_option(libproxy TYPE boolean)
-qt_commandline_option(openssl TYPE optionalString VALUES no yes linked runtime)
-qt_commandline_option(openssl-linked TYPE void NAME openssl VALUE linked)
-qt_commandline_option(openssl-runtime TYPE void NAME openssl VALUE runtime)
qt_commandline_option(dtls TYPE boolean)
qt_commandline_option(ocsp TYPE boolean)
qt_commandline_option(sctp TYPE boolean)
diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp
index 87607ef413..e456d00713 100644
--- a/src/network/socket/qabstractsocket.cpp
+++ b/src/network/socket/qabstractsocket.cpp
@@ -173,8 +173,9 @@
parameter describes the type of error that occurred.
When this signal is emitted, the socket may not be ready for a reconnect
- attempt. In that case, attempts to reconnect should be done from the event
- loop. For example, use a QTimer::singleShot() with 0 as the timeout.
+ attempt. In that case, attempts to reconnect should be done from the
+ event loop. For example, use QChronoTimer::singleShot() with 0ns as
+ the timeout.
QAbstractSocket::SocketError is not a registered metatype, so for queued
connections, you will have to register it with Q_DECLARE_METATYPE() and
@@ -439,7 +440,7 @@
#include <qmetaobject.h>
#include <qpointer.h>
#include <qtimer.h>
-#include <qelapsedtimer.h>
+#include <qdeadlinetimer.h>
#include <qscopedvaluerollback.h>
#include <qvarlengtharray.h>
@@ -465,11 +466,12 @@
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
+using namespace std::chrono_literals;
QT_IMPL_METATYPE_EXTERN_TAGGED(QAbstractSocket::SocketState, QAbstractSocket__SocketState)
QT_IMPL_METATYPE_EXTERN_TAGGED(QAbstractSocket::SocketError, QAbstractSocket__SocketError)
-static const int DefaultConnectTimeout = 30000;
+static constexpr auto DefaultConnectTimeout = 30s;
static bool isProxyError(QAbstractSocket::SocketError error)
{
@@ -636,11 +638,19 @@ bool QAbstractSocketPrivate::canReadNotification()
return !q->isReadable();
}
} else {
- if (hasPendingData) {
+ const bool isUdpSocket = (socketType == QAbstractSocket::UdpSocket);
+ if (hasPendingData && (!isUdpSocket || hasPendingDatagram)) {
socketEngine->setReadNotificationEnabled(false);
return true;
}
- hasPendingData = true;
+ if (!isUdpSocket
+#if QT_CONFIG(udpsocket)
+ || socketEngine->hasPendingDatagrams()
+#endif
+ ) {
+ hasPendingData = true;
+ hasPendingDatagram = isUdpSocket;
+ }
}
emitReadyRead();
@@ -836,7 +846,7 @@ void QAbstractSocketPrivate::resolveProxy(const QString &hostname, quint16 port)
}
// return the first that we can use
- for (const QNetworkProxy &p : qAsConst(proxies)) {
+ for (const QNetworkProxy &p : std::as_const(proxies)) {
if (socketType == QAbstractSocket::UdpSocket &&
(p.capabilities() & QNetworkProxy::UdpTunnelingCapability) == 0)
continue;
@@ -1057,8 +1067,7 @@ void QAbstractSocketPrivate::_q_connectToNextAddress()
q, SLOT(_q_abortConnectionAttempt()),
Qt::DirectConnection);
}
- int connectTimeout = DefaultConnectTimeout;
- connectTimer->start(connectTimeout);
+ connectTimer->start(DefaultConnectTimeout);
}
// Wait for a write notification that will eventually call
@@ -2052,8 +2061,7 @@ bool QAbstractSocket::waitForConnected(int msecs)
bool wasPendingClose = d->pendingClose;
d->pendingClose = false;
- QElapsedTimer stopWatch;
- stopWatch.start();
+ QDeadlineTimer deadline{msecs};
if (d->state == HostLookupState) {
#if defined (QABSTRACTSOCKET_DEBUG)
@@ -2073,22 +2081,21 @@ bool QAbstractSocket::waitForConnected(int msecs)
if (state() == UnconnectedState)
return false; // connect not im progress anymore!
- int connectTimeout = DefaultConnectTimeout;
bool timedOut = true;
#if defined (QABSTRACTSOCKET_DEBUG)
int attempt = 1;
#endif
- while (state() == ConnectingState && (msecs == -1 || stopWatch.elapsed() < msecs)) {
- int timeout = qt_subtract_from_timeout(msecs, stopWatch.elapsed());
- if (msecs != -1 && timeout > connectTimeout)
- timeout = connectTimeout;
+ while (state() == ConnectingState && !deadline.hasExpired()) {
+ QDeadlineTimer timer = deadline;
+ if (!deadline.isForever() && deadline.remainingTimeAsDuration() > DefaultConnectTimeout)
+ timer = QDeadlineTimer(DefaultConnectTimeout);
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::waitForConnected(%i) waiting %.2f secs for connection attempt #%i",
- msecs, timeout / 1000.0, attempt++);
+ msecs, timer.remainingTime() / 1000.0, attempt++);
#endif
timedOut = false;
- if (d->socketEngine && d->socketEngine->waitForWrite(timeout, &timedOut) && !timedOut) {
+ if (d->socketEngine && d->socketEngine->waitForWrite(timer, &timedOut) && !timedOut) {
d->_q_testConnection();
} else {
d->_q_connectToNextAddress();
@@ -2143,8 +2150,7 @@ bool QAbstractSocket::waitForReadyRead(int msecs)
return false;
}
- QElapsedTimer stopWatch;
- stopWatch.start();
+ QDeadlineTimer deadline{msecs};
// handle a socket in connecting state
if (state() == HostLookupState || state() == ConnectingState) {
@@ -2160,7 +2166,7 @@ bool QAbstractSocket::waitForReadyRead(int msecs)
bool readyToRead = false;
bool readyToWrite = false;
if (!d->socketEngine->waitForReadOrWrite(&readyToRead, &readyToWrite, true, !d->writeBuffer.isEmpty(),
- qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
+ deadline)) {
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::waitForReadyRead(%i) failed (%i, %s)",
msecs, d->socketEngine->error(), d->socketEngine->errorString().toLatin1().constData());
@@ -2178,7 +2184,7 @@ bool QAbstractSocket::waitForReadyRead(int msecs)
if (readyToWrite)
d->canWriteNotification();
- } while (msecs == -1 || qt_subtract_from_timeout(msecs, stopWatch.elapsed()) > 0);
+ } while (!deadline.hasExpired());
return false;
}
@@ -2214,8 +2220,7 @@ bool QAbstractSocket::waitForBytesWritten(int msecs)
if (d->writeBuffer.isEmpty())
return false;
- QElapsedTimer stopWatch;
- stopWatch.start();
+ QDeadlineTimer deadline{msecs};
// handle a socket in connecting state
if (state() == HostLookupState || state() == ConnectingState) {
@@ -2223,13 +2228,13 @@ bool QAbstractSocket::waitForBytesWritten(int msecs)
return false;
}
- forever {
+ for (;;) {
bool readyToRead = false;
bool readyToWrite = false;
if (!d->socketEngine->waitForReadOrWrite(&readyToRead, &readyToWrite,
!d->readBufferMaxSize || d->buffer.size() < d->readBufferMaxSize,
!d->writeBuffer.isEmpty(),
- qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
+ deadline)) {
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::waitForBytesWritten(%i) failed (%i, %s)",
msecs, d->socketEngine->error(), d->socketEngine->errorString().toLatin1().constData());
@@ -2293,8 +2298,7 @@ bool QAbstractSocket::waitForDisconnected(int msecs)
return false;
}
- QElapsedTimer stopWatch;
- stopWatch.start();
+ QDeadlineTimer deadline{msecs};
// handle a socket in connecting state
if (state() == HostLookupState || state() == ConnectingState) {
@@ -2304,12 +2308,12 @@ bool QAbstractSocket::waitForDisconnected(int msecs)
return true;
}
- forever {
+ for (;;) {
bool readyToRead = false;
bool readyToWrite = false;
if (!d->socketEngine->waitForReadOrWrite(&readyToRead, &readyToWrite, state() == ConnectedState,
!d->writeBuffer.isEmpty(),
- qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
+ deadline)) {
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::waitForReadyRead(%i) failed (%i, %s)",
msecs, d->socketEngine->error(), d->socketEngine->errorString().toLatin1().constData());
diff --git a/src/network/socket/qabstractsocket.h b/src/network/socket/qabstractsocket.h
index f421955e7e..1d5d45096a 100644
--- a/src/network/socket/qabstractsocket.h
+++ b/src/network/socket/qabstractsocket.h
@@ -8,7 +8,7 @@
#if QT_VERSION >= QT_VERSION_CHECK(7, 0, 0)
#include <QtNetwork/qabstractsocket.h>
#endif
-#ifdef Q_CLANG_QDOC
+#ifdef Q_QDOC
#include <QtNetwork/qhostaddress.h>
#endif
#include <QtCore/qiodevice.h>
@@ -129,7 +129,7 @@ public:
virtual bool bind(const QHostAddress &address, quint16 port = 0,
BindMode mode = DefaultForPlatform);
-#if QT_VERSION >= QT_VERSION_CHECK(7,0,0) || defined(Q_CLANG_QDOC)
+#if QT_VERSION >= QT_VERSION_CHECK(7,0,0) || defined(Q_QDOC)
bool bind(QHostAddress::SpecialAddress addr, quint16 port = 0, BindMode mode = DefaultForPlatform)
{ return bind(QHostAddress(addr), port, mode); }
bool bind(quint16 port = 0, BindMode mode = DefaultForPlatform)
diff --git a/src/network/socket/qabstractsocket_p.h b/src/network/socket/qabstractsocket_p.h
index 98d74dcfd4..cc5f53179c 100644
--- a/src/network/socket/qabstractsocket_p.h
+++ b/src/network/socket/qabstractsocket_p.h
@@ -110,6 +110,7 @@ public:
qint64 readBufferMaxSize = 0;
bool isBuffered = false;
bool hasPendingData = false;
+ bool hasPendingDatagram = false;
QTimer *connectTimer = nullptr;
diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h
index 4209a32ca7..9ee79cebc1 100644
--- a/src/network/socket/qabstractsocketengine_p.h
+++ b/src/network/socket/qabstractsocketengine_p.h
@@ -19,8 +19,9 @@
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "QtNetwork/qhostaddress.h"
#include "QtNetwork/qabstractsocket.h"
-#include "private/qobject_p.h"
+#include <QtCore/qdeadlinetimer.h>
#include "private/qnetworkdatagram_p.h"
+#include "private/qobject_p.h"
QT_BEGIN_NAMESPACE
@@ -44,6 +45,8 @@ public:
#endif
};
+static constexpr std::chrono::seconds DefaultTimeout{30};
+
class Q_AUTOTEST_EXPORT QAbstractSocketEngine : public QObject
{
Q_OBJECT
@@ -98,7 +101,7 @@ public:
virtual bool connectToHostByName(const QString &name, quint16 port) = 0;
virtual bool bind(const QHostAddress &address, quint16 port) = 0;
virtual bool listen(int backlog) = 0;
- virtual int accept() = 0;
+ virtual qintptr accept() = 0;
virtual void close() = 0;
virtual qint64 bytesAvailable() const = 0;
@@ -128,11 +131,14 @@ public:
virtual int option(SocketOption option) const = 0;
virtual bool setOption(SocketOption option, int value) = 0;
- virtual bool waitForRead(int msecs = 30000, bool *timedOut = nullptr) = 0;
- virtual bool waitForWrite(int msecs = 30000, bool *timedOut = nullptr) = 0;
+ virtual bool waitForRead(QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
+ bool *timedOut = nullptr) = 0;
+ virtual bool waitForWrite(QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
+ bool *timedOut = nullptr) = 0;
virtual bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
- int msecs = 30000, bool *timedOut = nullptr) = 0;
+ QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
+ bool *timedOut = nullptr) = 0;
QAbstractSocket::SocketError error() const;
QString errorString() const;
diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp
index 260484aaf3..a700023bd5 100644
--- a/src/network/socket/qhttpsocketengine.cpp
+++ b/src/network/socket/qhttpsocketengine.cpp
@@ -7,7 +7,7 @@
#include "qurl.h"
#include "private/qhttpnetworkreply_p.h"
#include "private/qiodevice_p.h"
-#include "qelapsedtimer.h"
+#include "qdeadlinetimer.h"
#include "qnetworkinterface.h"
#if !defined(QT_NO_NETWORKPROXY)
@@ -164,7 +164,7 @@ bool QHttpSocketEngine::listen(int backlog)
return false;
}
-int QHttpSocketEngine::accept()
+qintptr QHttpSocketEngine::accept()
{
qWarning("Operation is not supported");
setError(QAbstractSocket::UnsupportedSocketOperationError, "Unsupported socket operation"_L1);
@@ -310,19 +310,16 @@ bool QHttpSocketEngine::setOption(SocketOption option, int value)
return false;
}
-bool QHttpSocketEngine::waitForRead(int msecs, bool *timedOut)
+bool QHttpSocketEngine::waitForRead(QDeadlineTimer deadline, bool *timedOut)
{
Q_D(const QHttpSocketEngine);
if (!d->socket || d->socket->state() == QAbstractSocket::UnconnectedState)
return false;
- QElapsedTimer stopWatch;
- stopWatch.start();
-
// Wait for more data if nothing is available.
if (!d->socket->bytesAvailable()) {
- if (!d->socket->waitForReadyRead(qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
+ if (!d->socket->waitForReadyRead(deadline.remainingTime())) {
if (d->socket->state() == QAbstractSocket::UnconnectedState)
return true;
setError(d->socket->error(), d->socket->errorString());
@@ -332,11 +329,7 @@ bool QHttpSocketEngine::waitForRead(int msecs, bool *timedOut)
}
}
- // If we're not connected yet, wait until we are, or until an error
- // occurs.
- while (d->state != Connected && d->socket->waitForReadyRead(qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
- // Loop while the protocol handshake is taking place.
- }
+ waitForProtocolHandshake(deadline);
// Report any error that may occur.
if (d->state != Connected) {
@@ -348,14 +341,14 @@ bool QHttpSocketEngine::waitForRead(int msecs, bool *timedOut)
return true;
}
-bool QHttpSocketEngine::waitForWrite(int msecs, bool *timedOut)
+bool QHttpSocketEngine::waitForWrite(QDeadlineTimer deadline, bool *timedOut)
{
Q_D(const QHttpSocketEngine);
// If we're connected, just forward the call.
if (d->state == Connected) {
if (d->socket->bytesToWrite()) {
- if (!d->socket->waitForBytesWritten(msecs)) {
+ if (!d->socket->waitForBytesWritten(deadline.remainingTime())) {
if (d->socket->error() == QAbstractSocket::SocketTimeoutError && timedOut)
*timedOut = true;
return false;
@@ -364,15 +357,7 @@ bool QHttpSocketEngine::waitForWrite(int msecs, bool *timedOut)
return true;
}
- QElapsedTimer stopWatch;
- stopWatch.start();
-
- // If we're not connected yet, wait until we are, and until bytes have
- // been received (i.e., the socket has connected, we have sent the
- // greeting, and then received the response).
- while (d->state != Connected && d->socket->waitForReadyRead(qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
- // Loop while the protocol handshake is taking place.
- }
+ waitForProtocolHandshake(deadline);
// Report any error that may occur.
if (d->state != Connected) {
@@ -386,25 +371,37 @@ bool QHttpSocketEngine::waitForWrite(int msecs, bool *timedOut)
bool QHttpSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
- int msecs, bool *timedOut)
+ QDeadlineTimer deadline, bool *timedOut)
{
Q_UNUSED(checkRead);
if (!checkWrite) {
// Not interested in writing? Then we wait for read notifications.
- bool canRead = waitForRead(msecs, timedOut);
+ bool canRead = waitForRead(deadline, timedOut);
if (readyToRead)
*readyToRead = canRead;
return canRead;
}
// Interested in writing? Then we wait for write notifications.
- bool canWrite = waitForWrite(msecs, timedOut);
+ bool canWrite = waitForWrite(deadline, timedOut);
if (readyToWrite)
*readyToWrite = canWrite;
return canWrite;
}
+void QHttpSocketEngine::waitForProtocolHandshake(QDeadlineTimer deadline) const
+{
+ Q_D(const QHttpSocketEngine);
+
+ // If we're not connected yet, wait until we are (and until bytes have
+ // been received, i.e. the socket has connected, we have sent the
+ // greeting, and then received the response), or until an error occurs.
+ while (d->state != Connected && d->socket->waitForReadyRead(deadline.remainingTime())) {
+ // Loop while the protocol handshake is taking place.
+ }
+}
+
bool QHttpSocketEngine::isReadNotificationEnabled() const
{
Q_D(const QHttpSocketEngine);
@@ -556,16 +553,8 @@ void QHttpSocketEngine::slotSocketReadNotification()
d->authenticator.detach();
priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
- if (d->credentialsSent && priv->phase != QAuthenticatorPrivate::Phase2) {
- // Remember that (e.g.) NTLM is two-phase, so only reset when the authentication is not currently in progress.
- //407 response again means the provided username/password were invalid.
- d->authenticator = QAuthenticator(); //this is needed otherwise parseHttpResponse won't set the state, and then signal isn't emitted.
- d->authenticator.detach();
- priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
- priv->hasFailed = true;
- }
-
- priv->parseHttpResponse(d->reply->header(), true, d->proxy.hostName());
+ const auto headers = d->reply->header();
+ priv->parseHttpResponse(headers, true, d->proxy.hostName());
if (priv->phase == QAuthenticatorPrivate::Invalid) {
// problem parsing the reply
@@ -576,6 +565,29 @@ void QHttpSocketEngine::slotSocketReadNotification()
return;
}
+ if (priv->phase == QAuthenticatorPrivate::Done
+ || (priv->phase == QAuthenticatorPrivate::Start
+ && (priv->method == QAuthenticatorPrivate::Ntlm
+ || priv->method == QAuthenticatorPrivate::Negotiate))) {
+ if (priv->phase == QAuthenticatorPrivate::Start)
+ priv->phase = QAuthenticatorPrivate::Phase1;
+ bool credentialsWasSent = d->credentialsSent;
+ if (d->credentialsSent) {
+ // Remember that (e.g.) NTLM is two-phase, so only reset when the authentication is
+ // not currently in progress. 407 response again means the provided
+ // username/password were invalid.
+ d->authenticator.detach();
+ priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
+ priv->hasFailed = true;
+ d->credentialsSent = false;
+ priv->phase = QAuthenticatorPrivate::Done;
+ }
+ if ((priv->method != QAuthenticatorPrivate::Ntlm
+ && priv->method != QAuthenticatorPrivate::Negotiate)
+ || credentialsWasSent)
+ proxyAuthenticationRequired(d->proxy, &d->authenticator);
+ }
+
bool willClose;
QByteArray proxyConnectionHeader = d->reply->headerField("Proxy-Connection");
// Although most proxies use the unofficial Proxy-Connection header, the Connection header
@@ -603,10 +615,8 @@ void QHttpSocketEngine::slotSocketReadNotification()
d->reply = new QHttpNetworkReply(QUrl(), this);
}
- if (priv->phase == QAuthenticatorPrivate::Done)
- proxyAuthenticationRequired(d->proxy, &d->authenticator);
- // priv->phase will get reset to QAuthenticatorPrivate::Start if the authenticator got modified in the signal above.
if (priv->phase == QAuthenticatorPrivate::Done) {
+ d->authenticator = QAuthenticator();
setError(QAbstractSocket::ProxyAuthenticationRequiredError, tr("Authentication required"));
d->socket->disconnectFromHost();
} else {
diff --git a/src/network/socket/qhttpsocketengine_p.h b/src/network/socket/qhttpsocketengine_p.h
index 77e3167ede..7926abf513 100644
--- a/src/network/socket/qhttpsocketengine_p.h
+++ b/src/network/socket/qhttpsocketengine_p.h
@@ -16,10 +16,12 @@
//
#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include "private/qabstractsocketengine_p.h"
+
+#include <QtNetwork/qnetworkproxy.h>
+
#include "qabstractsocket.h"
-#include "qnetworkproxy.h"
#include "private/qauthenticator_p.h"
+#include "private/qabstractsocketengine_p.h"
QT_REQUIRE_CONFIG(http);
@@ -60,7 +62,7 @@ public:
bool connectToHostByName(const QString &name, quint16 port) override;
bool bind(const QHostAddress &address, quint16 port) override;
bool listen(int backlog) override;
- int accept() override;
+ qintptr accept() override;
void close() override;
qint64 bytesAvailable() const override;
@@ -90,11 +92,16 @@ public:
int option(SocketOption option) const override;
bool setOption(SocketOption option, int value) override;
- bool waitForRead(int msecs = 30000, bool *timedOut = nullptr) override;
- bool waitForWrite(int msecs = 30000, bool *timedOut = nullptr) override;
+ bool waitForRead(QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
+ bool *timedOut = nullptr) override;
+ bool waitForWrite(QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
+ bool *timedOut = nullptr) override;
bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
- int msecs = 30000, bool *timedOut = nullptr) override;
+ QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
+ bool *timedOut = nullptr) override;
+
+ void waitForProtocolHandshake(QDeadlineTimer deadline) const;
bool isReadNotificationEnabled() const override;
void setReadNotificationEnabled(bool enable) override;
diff --git a/src/network/socket/qlocalserver.cpp b/src/network/socket/qlocalserver.cpp
index d96f540148..5ef2db6b94 100644
--- a/src/network/socket/qlocalserver.cpp
+++ b/src/network/socket/qlocalserver.cpp
@@ -265,9 +265,27 @@ bool QLocalServer::hasPendingConnections() const
*/
void QLocalServer::incomingConnection(quintptr socketDescriptor)
{
- Q_D(QLocalServer);
QLocalSocket *socket = new QLocalSocket(this);
socket->setSocketDescriptor(socketDescriptor);
+ addPendingConnection(socket);
+}
+
+/*!
+ This function is called by QLocalServer::incomingConnection()
+ to add the \a socket to the list of pending incoming connections.
+
+ \note Don't forget to call this member from reimplemented
+ incomingConnection() if you do not want to break the
+ Pending Connections mechanism. This function emits the
+ pendingConnectionAvailable() signal after the socket has been
+ added.
+
+ \sa incomingConnection(), pendingConnectionAvailable()
+ \since 6.8
+*/
+void QLocalServer::addPendingConnection(QLocalSocket *socket)
+{
+ Q_D(QLocalServer);
d->pendingConnections.enqueue(socket);
emit newConnection();
}
diff --git a/src/network/socket/qlocalserver.h b/src/network/socket/qlocalserver.h
index 81073450e2..685253e8be 100644
--- a/src/network/socket/qlocalserver.h
+++ b/src/network/socket/qlocalserver.h
@@ -68,6 +68,7 @@ public:
protected:
virtual void incomingConnection(quintptr socketDescriptor);
+ void addPendingConnection(QLocalSocket *socket);
private:
Q_DISABLE_COPY(QLocalServer)
diff --git a/src/network/socket/qlocalserver_unix.cpp b/src/network/socket/qlocalserver_unix.cpp
index dcd001a263..9aa9a5b86f 100644
--- a/src/network/socket/qlocalserver_unix.cpp
+++ b/src/network/socket/qlocalserver_unix.cpp
@@ -279,8 +279,7 @@ void QLocalServerPrivate::_q_onNewConnection()
void QLocalServerPrivate::waitForNewConnection(int msec, bool *timedOut)
{
pollfd pfd = qt_make_pollfd(listenSocket, POLLIN);
-
- switch (qt_poll_msecs(&pfd, 1, msec)) {
+ switch (qt_safe_poll(&pfd, 1, QDeadlineTimer(msec))) {
case 0:
if (timedOut)
*timedOut = true;
diff --git a/src/network/socket/qlocalsocket_unix.cpp b/src/network/socket/qlocalsocket_unix.cpp
index 0c447e90a4..af0dc988af 100644
--- a/src/network/socket/qlocalsocket_unix.cpp
+++ b/src/network/socket/qlocalsocket_unix.cpp
@@ -13,16 +13,17 @@
#include <errno.h>
#include <qdir.h>
+#include <qdeadlinetimer.h>
#include <qdebug.h>
-#include <qelapsedtimer.h>
#include <qstringconverter.h>
#ifdef Q_OS_VXWORKS
# include <selectLib.h>
#endif
-#define QT_CONNECT_TIMEOUT 30000
+using namespace std::chrono_literals;
+#define QT_CONNECT_TIMEOUT 30000
QT_BEGIN_NAMESPACE
@@ -401,8 +402,8 @@ bool QLocalSocketPrivate::parseSockaddr(const struct ::sockaddr_un &addr,
if (!name.isEmpty() && !toUtf16.hasError()) {
//conversion encodes the trailing zeros. So, in case of non-abstract namespace we
//chop them off as \0 character is not allowed in filenames
- if (!abstractNamespace && (name.at(name.length() - 1) == QChar::fromLatin1('\0'))) {
- int truncPos = name.length() - 1;
+ if (!abstractNamespace && (name.at(name.size() - 1) == QChar::fromLatin1('\0'))) {
+ int truncPos = name.size() - 1;
while (truncPos > 0 && name.at(truncPos - 1) == QChar::fromLatin1('\0'))
truncPos--;
name.truncate(truncPos);
@@ -585,21 +586,20 @@ bool QLocalSocket::waitForConnected(int msec)
if (state() != ConnectingState)
return (state() == ConnectedState);
- QElapsedTimer timer;
- timer.start();
-
pollfd pfd = qt_make_pollfd(d->connectingSocket, POLLIN);
- do {
- const int timeout = (msec > 0) ? qMax(msec - timer.elapsed(), Q_INT64_C(0)) : msec;
- const int result = qt_poll_msecs(&pfd, 1, timeout);
+ QDeadlineTimer deadline{msec};
+ auto remainingTime = deadline.remainingTimeAsDuration();
+ do {
+ const int result = qt_safe_poll(&pfd, 1, deadline);
if (result == -1)
d->setErrorAndEmit(QLocalSocket::UnknownSocketError,
"QLocalSocket::waitForConnected"_L1);
else if (result > 0)
d->_q_connectToSocket();
- } while (state() == ConnectingState && !timer.hasExpired(msec));
+ } while (state() == ConnectingState
+ && (remainingTime = deadline.remainingTimeAsDuration()) > 0ns);
return (state() == ConnectedState);
}
diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp
index 8e7f3d539b..4c8b3ebf3f 100644
--- a/src/network/socket/qnativesocketengine.cpp
+++ b/src/network/socket/qnativesocketengine.cpp
@@ -78,7 +78,7 @@
\sa readDatagram(), QNetworkDatagram
*/
-#include "qnativesocketengine_p.h"
+#include "qnativesocketengine_p_p.h"
#include <qabstracteventdispatcher.h>
#include <qsocketnotifier.h>
@@ -679,7 +679,7 @@ bool QNativeSocketEngine::listen(int backlog)
\sa bind(), listen()
*/
-int QNativeSocketEngine::accept()
+qintptr QNativeSocketEngine::accept()
{
Q_D(QNativeSocketEngine);
Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::accept(), -1);
@@ -948,23 +948,23 @@ void QNativeSocketEngine::close()
d->peerAddress.clear();
d->inboundStreamCount = d->outboundStreamCount = 0;
if (d->readNotifier) {
- qDeleteInEventHandler(d->readNotifier);
+ delete d->readNotifier;
d->readNotifier = nullptr;
}
if (d->writeNotifier) {
- qDeleteInEventHandler(d->writeNotifier);
+ delete d->writeNotifier;
d->writeNotifier = nullptr;
}
if (d->exceptNotifier) {
- qDeleteInEventHandler(d->exceptNotifier);
+ delete d->exceptNotifier;
d->exceptNotifier = nullptr;
}
}
/*!
- Waits for \a msecs milliseconds or until the socket is ready for
- reading. If \a timedOut is not \nullptr and \a msecs milliseconds
- have passed, the value of \a timedOut is set to true.
+ Waits until \a deadline has expired or until the socket is ready for
+ reading. If \a timedOut is not \nullptr and \a deadline has expired,
+ the value of \a timedOut is set to true.
Returns \c true if data is available for reading; otherwise returns
false.
@@ -976,7 +976,7 @@ void QNativeSocketEngine::close()
is to create a QSocketNotifier, passing the socket descriptor
returned by socketDescriptor() to its constructor.
*/
-bool QNativeSocketEngine::waitForRead(int msecs, bool *timedOut)
+bool QNativeSocketEngine::waitForRead(QDeadlineTimer deadline, bool *timedOut)
{
Q_D(const QNativeSocketEngine);
Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForRead(), false);
@@ -986,7 +986,7 @@ bool QNativeSocketEngine::waitForRead(int msecs, bool *timedOut)
if (timedOut)
*timedOut = false;
- int ret = d->nativeSelect(msecs, true);
+ int ret = d->nativeSelect(deadline, true);
if (ret == 0) {
if (timedOut)
*timedOut = true;
@@ -1002,9 +1002,9 @@ bool QNativeSocketEngine::waitForRead(int msecs, bool *timedOut)
}
/*!
- Waits for \a msecs milliseconds or until the socket is ready for
- writing. If \a timedOut is not \nullptr and \a msecs milliseconds
- have passed, the value of \a timedOut is set to true.
+ Waits until \a deadline has expired or until the socket is ready for
+ writing. If \a timedOut is not \nullptr and \a deadline has expired,
+ the value of \a timedOut is set to true.
Returns \c true if data is available for writing; otherwise returns
false.
@@ -1016,7 +1016,7 @@ bool QNativeSocketEngine::waitForRead(int msecs, bool *timedOut)
is to create a QSocketNotifier, passing the socket descriptor
returned by socketDescriptor() to its constructor.
*/
-bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut)
+bool QNativeSocketEngine::waitForWrite(QDeadlineTimer deadline, bool *timedOut)
{
Q_D(QNativeSocketEngine);
Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForWrite(), false);
@@ -1026,7 +1026,7 @@ bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut)
if (timedOut)
*timedOut = false;
- int ret = d->nativeSelect(msecs, false);
+ int ret = d->nativeSelect(deadline, false);
// On Windows, the socket is in connected state if a call to
// select(writable) is successful. In this case we should not
// issue a second call to WSAConnect()
@@ -1074,14 +1074,14 @@ bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut)
bool QNativeSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
- int msecs, bool *timedOut)
+ QDeadlineTimer deadline, bool *timedOut)
{
Q_D(QNativeSocketEngine);
Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::waitForReadOrWrite(), false);
Q_CHECK_NOT_STATE(QNativeSocketEngine::waitForReadOrWrite(),
QAbstractSocket::UnconnectedState, false);
- int ret = d->nativeSelect(msecs, checkRead, checkWrite, readyToRead, readyToWrite);
+ int ret = d->nativeSelect(deadline, checkRead, checkWrite, readyToRead, readyToWrite);
// On Windows, the socket is in connected state if a call to
// select(writable) is successful. In this case we should not
// issue a second call to WSAConnect()
@@ -1255,7 +1255,7 @@ bool QReadNotifier::event(QEvent *e)
class QWriteNotifier : public QSocketNotifier
{
public:
- QWriteNotifier(int fd, QNativeSocketEngine *parent)
+ QWriteNotifier(qintptr fd, QNativeSocketEngine *parent)
: QSocketNotifier(fd, QSocketNotifier::Write, parent) { engine = parent; }
protected:
@@ -1279,7 +1279,7 @@ bool QWriteNotifier::event(QEvent *e)
class QExceptionNotifier : public QSocketNotifier
{
public:
- QExceptionNotifier(int fd, QNativeSocketEngine *parent)
+ QExceptionNotifier(qintptr fd, QNativeSocketEngine *parent)
: QSocketNotifier(fd, QSocketNotifier::Exception, parent) { engine = parent; }
protected:
diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h
index a9c6da1977..4c185b7a4a 100644
--- a/src/network/socket/qnativesocketengine_p.h
+++ b/src/network/socket/qnativesocketengine_p.h
@@ -20,8 +20,9 @@
#include "QtNetwork/qhostaddress.h"
#include "QtNetwork/qnetworkinterface.h"
#include "private/qabstractsocketengine_p.h"
+#include "qplatformdefs.h"
+
#ifndef Q_OS_WIN
-# include "qplatformdefs.h"
# include <netinet/in.h>
#else
# include <winsock2.h>
@@ -34,51 +35,63 @@ QT_BEGIN_NAMESPACE
#ifdef Q_OS_WIN
# define QT_SOCKLEN_T int
# define QT_SOCKOPTLEN_T int
-
-// The following definitions are copied from the MinGW header mswsock.h which
-// was placed in the public domain. The WSASendMsg and WSARecvMsg functions
-// were introduced with Windows Vista, so some Win32 headers are lacking them.
-// There are no known versions of Windows CE or Embedded that contain them.
-# ifndef WSAID_WSARECVMSG
-typedef INT (WINAPI *LPFN_WSARECVMSG)(SOCKET s, LPWSAMSG lpMsg,
- LPDWORD lpdwNumberOfBytesRecvd,
- LPWSAOVERLAPPED lpOverlapped,
- LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
-# define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}}
-# endif // !WSAID_WSARECVMSG
-# ifndef WSAID_WSASENDMSG
-typedef struct {
- LPWSAMSG lpMsg;
- DWORD dwFlags;
- LPDWORD lpNumberOfBytesSent;
- LPWSAOVERLAPPED lpOverlapped;
- LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine;
-} WSASENDMSG, *LPWSASENDMSG;
-
-typedef INT (WSAAPI *LPFN_WSASENDMSG)(SOCKET s, LPWSAMSG lpMsg, DWORD dwFlags,
- LPDWORD lpNumberOfBytesSent,
- LPWSAOVERLAPPED lpOverlapped,
- LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
-
-# define WSAID_WSASENDMSG {0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}}
-# endif // !WSAID_WSASENDMSG
-#endif // Q_OS_WIN
-
-union qt_sockaddr {
- sockaddr a;
- sockaddr_in a4;
- sockaddr_in6 a6;
-};
+#endif
namespace {
namespace SetSALen {
template <typename T> void set(T *sa, typename std::enable_if<(&T::sa_len, true), QT_SOCKLEN_T>::type len)
{ sa->sa_len = len; }
+ template <typename T> void set(T *sa, typename std::enable_if<(&T::sin_len, true), QT_SOCKLEN_T>::type len)
+ { sa->sin_len = len; }
template <typename T> void set(T *sin6, typename std::enable_if<(&T::sin6_len, true), QT_SOCKLEN_T>::type len)
{ sin6->sin6_len = len; }
template <typename T> void set(T *, ...) {}
}
+
+inline QT_SOCKLEN_T setSockaddr(sockaddr_in *sin, const QHostAddress &addr, quint16 port = 0)
+{
+ *sin = {};
+ SetSALen::set(sin, sizeof(*sin));
+ sin->sin_family = AF_INET;
+ sin->sin_port = htons(port);
+ sin->sin_addr.s_addr = htonl(addr.toIPv4Address());
+ return sizeof(*sin);
+}
+
+inline QT_SOCKLEN_T setSockaddr(sockaddr_in6 *sin6, const QHostAddress &addr, quint16 port = 0)
+{
+ *sin6 = {};
+ SetSALen::set(sin6, sizeof(*sin6));
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons(port);
+ memcpy(sin6->sin6_addr.s6_addr, addr.toIPv6Address().c, sizeof(sin6->sin6_addr));
+#if QT_CONFIG(networkinterface)
+ sin6->sin6_scope_id = QNetworkInterface::interfaceIndexFromName(addr.scopeId());
+#else
+ // it had better be a number then, if it is not empty
+ sin6->sin6_scope_id = addr.scopeId().toUInt();
+#endif
+ return sizeof(*sin6);
+}
+
+inline QT_SOCKLEN_T setSockaddr(sockaddr *sa, const QHostAddress &addr, quint16 port = 0)
+{
+ switch (addr.protocol()) {
+ case QHostAddress::IPv4Protocol:
+ return setSockaddr(reinterpret_cast<sockaddr_in *>(sa), addr, port);
+
+ case QHostAddress::IPv6Protocol:
+ case QHostAddress::AnyIPProtocol:
+ return setSockaddr(reinterpret_cast<sockaddr_in6 *>(sa), addr, port);
+
+ case QHostAddress::UnknownNetworkLayerProtocol:
+ break;
+ }
+ *sa = {};
+ sa->sa_family = AF_UNSPEC;
+ return 0;
}
+} // unnamed namespace
class QNativeSocketEnginePrivate;
#ifndef QT_NO_NETWORKINTERFACE
@@ -103,7 +116,7 @@ public:
bool connectToHostByName(const QString &name, quint16 port) override;
bool bind(const QHostAddress &address, quint16 port) override;
bool listen(int backlog) override;
- int accept() override;
+ qintptr accept() override;
void close() override;
qint64 bytesAvailable() const override;
@@ -141,11 +154,14 @@ public:
int option(SocketOption option) const override;
bool setOption(SocketOption option, int value) override;
- bool waitForRead(int msecs = 30000, bool *timedOut = nullptr) override;
- bool waitForWrite(int msecs = 30000, bool *timedOut = nullptr) override;
+ bool waitForRead(QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
+ bool *timedOut = nullptr) override;
+ bool waitForWrite(QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
+ bool *timedOut = nullptr) override;
bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
- int msecs = 30000, bool *timedOut = nullptr) override;
+ QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
+ bool *timedOut = nullptr) override;
bool isReadNotificationEnabled() const override;
void setReadNotificationEnabled(bool enable) override;
@@ -163,132 +179,6 @@ private:
Q_DISABLE_COPY_MOVE(QNativeSocketEngine)
};
-class QSocketNotifier;
-
-class QNativeSocketEnginePrivate : public QAbstractSocketEnginePrivate
-{
- Q_DECLARE_PUBLIC(QNativeSocketEngine)
-public:
- QNativeSocketEnginePrivate();
- ~QNativeSocketEnginePrivate();
-
- qintptr socketDescriptor;
-
- QSocketNotifier *readNotifier, *writeNotifier, *exceptNotifier;
-
-#if defined(Q_OS_WIN)
- LPFN_WSASENDMSG sendmsg;
- LPFN_WSARECVMSG recvmsg;
-# endif
- enum ErrorString {
- NonBlockingInitFailedErrorString,
- BroadcastingInitFailedErrorString,
- NoIpV6ErrorString,
- RemoteHostClosedErrorString,
- TimeOutErrorString,
- ResourceErrorString,
- OperationUnsupportedErrorString,
- ProtocolUnsupportedErrorString,
- InvalidSocketErrorString,
- HostUnreachableErrorString,
- NetworkUnreachableErrorString,
- AccessErrorString,
- ConnectionTimeOutErrorString,
- ConnectionRefusedErrorString,
- AddressInuseErrorString,
- AddressNotAvailableErrorString,
- AddressProtectedErrorString,
- DatagramTooLargeErrorString,
- SendDatagramErrorString,
- ReceiveDatagramErrorString,
- WriteErrorString,
- ReadErrorString,
- PortInuseErrorString,
- NotSocketErrorString,
- InvalidProxyTypeString,
- TemporaryErrorString,
- NetworkDroppedConnectionErrorString,
- ConnectionResetErrorString,
-
- UnknownSocketErrorString = -1
- };
-
- void setError(QAbstractSocket::SocketError error, ErrorString errorString) const;
- QHostAddress adjustAddressProtocol(const QHostAddress &address) const;
-
- // native functions
- int option(QNativeSocketEngine::SocketOption option) const;
- bool setOption(QNativeSocketEngine::SocketOption option, int value);
-
- bool createNewSocket(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol &protocol);
-
- bool nativeConnect(const QHostAddress &address, quint16 port);
- bool nativeBind(const QHostAddress &address, quint16 port);
- bool nativeListen(int backlog);
- int nativeAccept();
-#ifndef QT_NO_NETWORKINTERFACE
- bool nativeJoinMulticastGroup(const QHostAddress &groupAddress,
- const QNetworkInterface &iface);
- bool nativeLeaveMulticastGroup(const QHostAddress &groupAddress,
- const QNetworkInterface &iface);
- QNetworkInterface nativeMulticastInterface() const;
- bool nativeSetMulticastInterface(const QNetworkInterface &iface);
-#endif
- qint64 nativeBytesAvailable() const;
-
- bool nativeHasPendingDatagrams() const;
- qint64 nativePendingDatagramSize() const;
- qint64 nativeReceiveDatagram(char *data, qint64 maxLength, QIpPacketHeader *header,
- QAbstractSocketEngine::PacketHeaderOptions options);
- qint64 nativeSendDatagram(const char *data, qint64 length, const QIpPacketHeader &header);
- qint64 nativeRead(char *data, qint64 maxLength);
- qint64 nativeWrite(const char *data, qint64 length);
- int nativeSelect(int timeout, bool selectForRead) const;
- int nativeSelect(int timeout, bool checkRead, bool checkWrite,
- bool *selectForRead, bool *selectForWrite) const;
-
- void nativeClose();
-
- bool checkProxy(const QHostAddress &address);
- bool fetchConnectionParameters();
-
-#if QT_CONFIG(networkinterface)
- static uint scopeIdFromString(const QString &scopeid)
- { return QNetworkInterface::interfaceIndexFromName(scopeid); }
-#endif
-
- /*! \internal
- Sets \a address and \a port in the \a aa sockaddr structure and the size in \a sockAddrSize.
- The address \a is converted to IPv6 if the current socket protocol is also IPv6.
- */
- void setPortAndAddress(quint16 port, const QHostAddress &address, qt_sockaddr *aa, QT_SOCKLEN_T *sockAddrSize)
- {
- if (address.protocol() == QAbstractSocket::IPv6Protocol
- || address.protocol() == QAbstractSocket::AnyIPProtocol
- || socketProtocol == QAbstractSocket::IPv6Protocol
- || socketProtocol == QAbstractSocket::AnyIPProtocol) {
- memset(&aa->a6, 0, sizeof(sockaddr_in6));
- aa->a6.sin6_family = AF_INET6;
-#if QT_CONFIG(networkinterface)
- aa->a6.sin6_scope_id = scopeIdFromString(address.scopeId());
-#endif
- aa->a6.sin6_port = htons(port);
- Q_IPV6ADDR tmp = address.toIPv6Address();
- memcpy(&aa->a6.sin6_addr, &tmp, sizeof(tmp));
- *sockAddrSize = sizeof(sockaddr_in6);
- SetSALen::set(&aa->a, sizeof(sockaddr_in6));
- } else {
- memset(&aa->a, 0, sizeof(sockaddr_in));
- aa->a4.sin_family = AF_INET;
- aa->a4.sin_port = htons(port);
- aa->a4.sin_addr.s_addr = htonl(address.toIPv4Address());
- *sockAddrSize = sizeof(sockaddr_in);
- SetSALen::set(&aa->a, sizeof(sockaddr_in));
- }
- }
-
-};
-
QT_END_NAMESPACE
#endif // QNATIVESOCKETENGINE_P_H
diff --git a/src/network/socket/qnativesocketengine_p_p.h b/src/network/socket/qnativesocketengine_p_p.h
new file mode 100644
index 0000000000..189a4327fd
--- /dev/null
+++ b/src/network/socket/qnativesocketengine_p_p.h
@@ -0,0 +1,189 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QNATIVESOCKETENGINE_P_P_H
+#define QNATIVESOCKETENGINE_P_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qabstractsocketengine_p.h"
+#include "private/qnativesocketengine_p.h"
+
+#ifndef Q_OS_WIN
+# include <netinet/in.h>
+#else
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# include <mswsock.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+#ifdef Q_OS_WIN
+
+// The following definitions are copied from the MinGW header mswsock.h which
+// was placed in the public domain. The WSASendMsg and WSARecvMsg functions
+// were introduced with Windows Vista, so some Win32 headers are lacking them.
+// There are no known versions of Windows CE or Embedded that contain them.
+# ifndef WSAID_WSARECVMSG
+typedef INT (WINAPI *LPFN_WSARECVMSG)(SOCKET s, LPWSAMSG lpMsg,
+ LPDWORD lpdwNumberOfBytesRecvd,
+ LPWSAOVERLAPPED lpOverlapped,
+ LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
+# define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}}
+# endif // !WSAID_WSARECVMSG
+# ifndef WSAID_WSASENDMSG
+typedef struct {
+ LPWSAMSG lpMsg;
+ DWORD dwFlags;
+ LPDWORD lpNumberOfBytesSent;
+ LPWSAOVERLAPPED lpOverlapped;
+ LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine;
+} WSASENDMSG, *LPWSASENDMSG;
+
+typedef INT (WSAAPI *LPFN_WSASENDMSG)(SOCKET s, LPWSAMSG lpMsg, DWORD dwFlags,
+ LPDWORD lpNumberOfBytesSent,
+ LPWSAOVERLAPPED lpOverlapped,
+ LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
+
+# define WSAID_WSASENDMSG {0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}}
+# endif // !WSAID_WSASENDMSG
+#endif // Q_OS_WIN
+
+union qt_sockaddr {
+ sockaddr a;
+ sockaddr_in a4;
+ sockaddr_in6 a6;
+};
+
+class QSocketNotifier;
+
+class QNativeSocketEnginePrivate : public QAbstractSocketEnginePrivate
+{
+ Q_DECLARE_PUBLIC(QNativeSocketEngine)
+public:
+ QNativeSocketEnginePrivate();
+ ~QNativeSocketEnginePrivate();
+
+ qintptr socketDescriptor;
+
+ QSocketNotifier *readNotifier, *writeNotifier, *exceptNotifier;
+
+#if defined(Q_OS_WIN)
+ LPFN_WSASENDMSG sendmsg;
+ LPFN_WSARECVMSG recvmsg;
+# endif
+ enum ErrorString {
+ NonBlockingInitFailedErrorString,
+ BroadcastingInitFailedErrorString,
+ NoIpV6ErrorString,
+ RemoteHostClosedErrorString,
+ TimeOutErrorString,
+ ResourceErrorString,
+ OperationUnsupportedErrorString,
+ ProtocolUnsupportedErrorString,
+ InvalidSocketErrorString,
+ HostUnreachableErrorString,
+ NetworkUnreachableErrorString,
+ AccessErrorString,
+ ConnectionTimeOutErrorString,
+ ConnectionRefusedErrorString,
+ AddressInuseErrorString,
+ AddressNotAvailableErrorString,
+ AddressProtectedErrorString,
+ DatagramTooLargeErrorString,
+ SendDatagramErrorString,
+ ReceiveDatagramErrorString,
+ WriteErrorString,
+ ReadErrorString,
+ PortInuseErrorString,
+ NotSocketErrorString,
+ InvalidProxyTypeString,
+ TemporaryErrorString,
+ NetworkDroppedConnectionErrorString,
+ ConnectionResetErrorString,
+
+ UnknownSocketErrorString = -1
+ };
+
+ void setError(QAbstractSocket::SocketError error, ErrorString errorString) const;
+ QHostAddress adjustAddressProtocol(const QHostAddress &address) const;
+
+ // native functions
+ int option(QNativeSocketEngine::SocketOption option) const;
+ bool setOption(QNativeSocketEngine::SocketOption option, int value);
+
+ bool createNewSocket(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol &protocol);
+
+ bool nativeConnect(const QHostAddress &address, quint16 port);
+ bool nativeBind(const QHostAddress &address, quint16 port);
+ bool nativeListen(int backlog);
+ qintptr nativeAccept();
+#ifndef QT_NO_NETWORKINTERFACE
+ bool nativeJoinMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface);
+ bool nativeLeaveMulticastGroup(const QHostAddress &groupAddress,
+ const QNetworkInterface &iface);
+ QNetworkInterface nativeMulticastInterface() const;
+ bool nativeSetMulticastInterface(const QNetworkInterface &iface);
+#endif
+ qint64 nativeBytesAvailable() const;
+
+ bool nativeHasPendingDatagrams() const;
+ qint64 nativePendingDatagramSize() const;
+ qint64 nativeReceiveDatagram(char *data, qint64 maxLength, QIpPacketHeader *header,
+ QAbstractSocketEngine::PacketHeaderOptions options);
+ qint64 nativeSendDatagram(const char *data, qint64 length, const QIpPacketHeader &header);
+ qint64 nativeRead(char *data, qint64 maxLength);
+ qint64 nativeWrite(const char *data, qint64 length);
+ int nativeSelect(QDeadlineTimer deadline, bool selectForRead) const;
+ int nativeSelect(QDeadlineTimer deadline, bool checkRead, bool checkWrite,
+ bool *selectForRead, bool *selectForWrite) const;
+
+ void nativeClose();
+
+ bool checkProxy(const QHostAddress &address);
+ bool fetchConnectionParameters();
+
+ /*! \internal
+ Sets \a address and \a port in the \a aa sockaddr structure and the size in \a sockAddrSize.
+ The address \a is converted to IPv6 if the current socket protocol is also IPv6.
+ */
+ void setPortAndAddress(quint16 port, const QHostAddress &address, qt_sockaddr *aa, QT_SOCKLEN_T *sockAddrSize)
+ {
+ switch (socketProtocol) {
+ case QHostAddress::IPv6Protocol:
+ case QHostAddress::AnyIPProtocol:
+ // force to IPv6
+ setSockaddr(&aa->a6, address, port);
+ *sockAddrSize = sizeof(sockaddr_in6);
+ return;
+
+ case QHostAddress::IPv4Protocol:
+ // force to IPv4
+ setSockaddr(&aa->a4, address, port);
+ *sockAddrSize = sizeof(sockaddr_in);
+ return;
+
+ case QHostAddress::UnknownNetworkLayerProtocol:
+ // don't force
+ break;
+ }
+ *sockAddrSize = setSockaddr(&aa->a, address, port);
+ }
+
+};
+
+QT_END_NAMESPACE
+
+#endif // QNATIVESOCKETENGINE_P_P_H
diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp
index 2c2ea706e5..b6df412253 100644
--- a/src/network/socket/qnativesocketengine_unix.cpp
+++ b/src/network/socket/qnativesocketengine_unix.cpp
@@ -3,11 +3,11 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define QNATIVESOCKETENGINE_DEBUG
-#include "qnativesocketengine_p.h"
+#include "qnativesocketengine_p_p.h"
#include "private/qnet_unix_p.h"
+#include "qdeadlinetimer.h"
#include "qiodevice.h"
#include "qhostaddress.h"
-#include "qelapsedtimer.h"
#include "qvarlengtharray.h"
#include "qnetworkinterface.h"
#include "qendian.h"
@@ -17,15 +17,6 @@
#include <time.h>
#include <errno.h>
#include <fcntl.h>
-#ifndef QT_NO_IPV6IFNAME
-#include <net/if.h>
-#endif
-#ifdef QT_LINUXBASE
-#include <arpa/inet.h>
-#endif
-#ifdef Q_OS_BSD4
-#include <net/if_dl.h>
-#endif
#ifdef Q_OS_INTEGRITY
#include <sys/uio.h>
#endif
@@ -40,6 +31,9 @@
#include <sys/socket.h>
#include <netinet/sctp.h>
#endif
+#ifdef Q_OS_BSD4
+# include <net/if_dl.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -442,6 +436,7 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &addr, quint16
case EFAULT:
case ENOTSOCK:
socketState = QAbstractSocket::UnconnectedState;
+ break;
default:
break;
}
@@ -559,7 +554,7 @@ bool QNativeSocketEnginePrivate::nativeListen(int backlog)
return true;
}
-int QNativeSocketEnginePrivate::nativeAccept()
+qintptr QNativeSocketEnginePrivate::nativeAccept()
{
int acceptedDescriptor = qt_safe_accept(socketDescriptor, nullptr, nullptr);
if (acceptedDescriptor == -1) {
@@ -605,7 +600,7 @@ int QNativeSocketEnginePrivate::nativeAccept()
}
}
- return acceptedDescriptor;
+ return qintptr(acceptedDescriptor);
}
#ifndef QT_NO_NETWORKINTERFACE
@@ -729,10 +724,10 @@ QNetworkInterface QNativeSocketEnginePrivate::nativeMulticastInterface() const
if (v.s_addr != 0 && sizeofv >= QT_SOCKOPTLEN_T(sizeof(v))) {
QHostAddress ipv4(ntohl(v.s_addr));
QList<QNetworkInterface> ifaces = QNetworkInterface::allInterfaces();
- for (int i = 0; i < ifaces.count(); ++i) {
+ for (int i = 0; i < ifaces.size(); ++i) {
const QNetworkInterface &iface = ifaces.at(i);
QList<QNetworkAddressEntry> entries = iface.addressEntries();
- for (int j = 0; j < entries.count(); ++j) {
+ for (int j = 0; j < entries.size(); ++j) {
const QNetworkAddressEntry &entry = entries.at(j);
if (entry.ip() == ipv4)
return iface;
@@ -752,7 +747,7 @@ bool QNativeSocketEnginePrivate::nativeSetMulticastInterface(const QNetworkInter
struct in_addr v;
if (iface.isValid()) {
QList<QNetworkAddressEntry> entries = iface.addressEntries();
- for (int i = 0; i < entries.count(); ++i) {
+ for (int i = 0; i < entries.size(); ++i) {
const QNetworkAddressEntry &entry = entries.at(i);
const QHostAddress &ip = entry.ip();
if (ip.protocol() == QAbstractSocket::IPv4Protocol) {
@@ -799,7 +794,7 @@ bool QNativeSocketEnginePrivate::nativeHasPendingDatagrams() const
// Peek 1 bytes into the next message.
ssize_t readBytes;
char c;
- EINTR_LOOP(readBytes, ::recv(socketDescriptor, &c, 1, MSG_PEEK));
+ QT_EINTR_LOOP(readBytes, ::recv(socketDescriptor, &c, 1, MSG_PEEK));
// If there's no error, or if our buffer was too small, there must be a
// pending datagram.
@@ -818,7 +813,7 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const
#ifdef Q_OS_LINUX
// Linux can return the actual datagram size if we use MSG_TRUNC
char c;
- EINTR_LOOP(recvResult, ::recv(socketDescriptor, &c, 1, MSG_PEEK | MSG_TRUNC));
+ QT_EINTR_LOOP(recvResult, ::recv(socketDescriptor, &c, 1, MSG_PEEK | MSG_TRUNC));
#elif defined(SO_NREAD)
// macOS can return the actual datagram size if we use SO_NREAD
int value;
@@ -1281,6 +1276,9 @@ qint64 QNativeSocketEnginePrivate::nativeWrite(const char *data, qint64 len)
setError(QAbstractSocket::RemoteHostClosedError, RemoteHostClosedErrorString);
q->close();
break;
+#if EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
case EAGAIN:
writtenBytes = 0;
break;
@@ -1350,16 +1348,17 @@ qint64 QNativeSocketEnginePrivate::nativeRead(char *data, qint64 maxSize)
return qint64(r);
}
-int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) const
+int QNativeSocketEnginePrivate::nativeSelect(QDeadlineTimer deadline, bool selectForRead) const
{
bool dummy;
- return nativeSelect(timeout, selectForRead, !selectForRead, &dummy, &dummy);
+ return nativeSelect(deadline, selectForRead, !selectForRead, &dummy, &dummy);
}
#ifndef Q_OS_WASM
-int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool checkRead, bool checkWrite,
- bool *selectForRead, bool *selectForWrite) const
+int QNativeSocketEnginePrivate::nativeSelect(QDeadlineTimer deadline, bool checkRead,
+ bool checkWrite, bool *selectForRead,
+ bool *selectForWrite) const
{
pollfd pfd = qt_make_pollfd(socketDescriptor, 0);
@@ -1369,7 +1368,7 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool checkRead, bool c
if (checkWrite)
pfd.events |= POLLOUT;
- const int ret = qt_poll_msecs(&pfd, 1, timeout);
+ const int ret = qt_safe_poll(&pfd, 1, deadline);
if (ret <= 0)
return ret;
@@ -1390,13 +1389,16 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool checkRead, bool c
#else
-int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool checkRead, bool checkWrite,
- bool *selectForRead, bool *selectForWrite) const
+int QNativeSocketEnginePrivate::nativeSelect(QDeadlineTimer deadline, bool checkRead,
+ bool checkWrite, bool *selectForRead,
+ bool *selectForWrite) const
{
*selectForRead = checkRead;
*selectForWrite = checkWrite;
bool socketDisconnect = false;
- QEventDispatcherWasm::socketSelect(timeout, socketDescriptor, checkRead, checkWrite,selectForRead, selectForWrite, &socketDisconnect);
+ QEventDispatcherWasm::socketSelect(deadline.remainingTime(), socketDescriptor, checkRead,
+ checkWrite, selectForRead, selectForWrite,
+ &socketDisconnect);
// The disconnect/close handling code in QAbstractsScket::canReadNotification()
// does not detect remote disconnect properly; do that here as a workardound.
diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp
index 13fe03afa2..6525f46e30 100644
--- a/src/network/socket/qnativesocketengine_win.cpp
+++ b/src/network/socket/qnativesocketengine_win.cpp
@@ -2,13 +2,10 @@
// Copyright (C) 2016 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
-// Prevent windows system header files from defining min/max as macros.
-#define NOMINMAX 1
-
#include <winsock2.h>
#include <ws2tcpip.h>
-#include "qnativesocketengine_p.h"
+#include "qnativesocketengine_p_p.h"
#include <qabstracteventdispatcher.h>
#include <qsocketnotifier.h>
@@ -19,6 +16,7 @@
#include <qvarlengtharray.h>
#include <algorithm>
+#include <chrono>
//#define QNATIVESOCKETENGINE_DEBUG
#if defined(QNATIVESOCKETENGINE_DEBUG)
@@ -773,10 +771,10 @@ bool QNativeSocketEnginePrivate::nativeListen(int backlog)
return true;
}
-int QNativeSocketEnginePrivate::nativeAccept()
+qintptr QNativeSocketEnginePrivate::nativeAccept()
{
- int acceptedDescriptor = WSAAccept(socketDescriptor, 0,0,0,0);
- if (acceptedDescriptor == -1) {
+ SOCKET acceptedDescriptor = WSAAccept(socketDescriptor, 0,0,0,0);
+ if (acceptedDescriptor == INVALID_SOCKET) {
int err = WSAGetLastError();
switch (err) {
case WSAEACCES:
@@ -810,7 +808,7 @@ int QNativeSocketEnginePrivate::nativeAccept()
setError(QAbstractSocket::UnknownSocketError, UnknownSocketErrorString);
break;
}
- } else if (acceptedDescriptor != -1 && QAbstractEventDispatcher::instance()) {
+ } else if (acceptedDescriptor != INVALID_SOCKET && QAbstractEventDispatcher::instance()) {
// Because of WSAAsyncSelect() WSAAccept returns a non blocking socket
// with the same attributes as the listening socket including the current
// WSAAsyncSelect(). To be able to change the socket to blocking mode the
@@ -820,9 +818,9 @@ int QNativeSocketEnginePrivate::nativeAccept()
n.setEnabled(false);
}
#if defined (QNATIVESOCKETENGINE_DEBUG)
- qDebug("QNativeSocketEnginePrivate::nativeAccept() == %i", acceptedDescriptor);
+ qDebug("QNativeSocketEnginePrivate::nativeAccept() == %lld", qint64(acceptedDescriptor));
#endif
- return acceptedDescriptor;
+ return qintptr(acceptedDescriptor);
}
static bool multicastMembershipHelper(QNativeSocketEnginePrivate *d,
@@ -1431,7 +1429,18 @@ qint64 QNativeSocketEnginePrivate::nativeRead(char *data, qint64 maxLength)
return ret;
}
-int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) const
+inline timeval durationToTimeval(std::chrono::nanoseconds dur) noexcept
+{
+ using namespace std::chrono;
+ const auto secs = duration_cast<seconds>(dur);
+ const auto frac = duration_cast<microseconds>(dur - secs);
+ struct timeval tval;
+ tval.tv_sec = secs.count();
+ tval.tv_usec = frac.count();
+ return tval;
+}
+
+int QNativeSocketEnginePrivate::nativeSelect(QDeadlineTimer deadline, bool selectForRead) const
{
bool readEnabled = selectForRead && readNotifier && readNotifier->isEnabled();
if (readEnabled)
@@ -1445,12 +1454,10 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) co
fds.fd_count = 1;
fds.fd_array[0] = (SOCKET)socketDescriptor;
- struct timeval tv;
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = (timeout % 1000) * 1000;
+ struct timeval tv = durationToTimeval(deadline.remainingTimeAsDuration());
if (selectForRead) {
- ret = select(0, &fds, 0, 0, timeout < 0 ? 0 : &tv);
+ ret = select(0, &fds, 0, 0, &tv);
} else {
// select for write
@@ -1459,7 +1466,7 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) co
FD_ZERO(&fdexception);
FD_SET((SOCKET)socketDescriptor, &fdexception);
- ret = select(0, 0, &fds, &fdexception, timeout < 0 ? 0 : &tv);
+ ret = select(0, 0, &fds, &fdexception, &tv);
// ... but if it is actually set, pretend it did not happen
if (ret > 0 && FD_ISSET((SOCKET)socketDescriptor, &fdexception))
@@ -1472,7 +1479,7 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout, bool selectForRead) co
return ret;
}
-int QNativeSocketEnginePrivate::nativeSelect(int timeout,
+int QNativeSocketEnginePrivate::nativeSelect(QDeadlineTimer deadline,
bool checkRead, bool checkWrite,
bool *selectForRead, bool *selectForWrite) const
{
@@ -1501,11 +1508,9 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout,
FD_SET((SOCKET)socketDescriptor, &fdexception);
}
- struct timeval tv;
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = (timeout % 1000) * 1000;
+ struct timeval tv = durationToTimeval(deadline.remainingTimeAsDuration());
- ret = select(socketDescriptor + 1, &fdread, &fdwrite, &fdexception, timeout < 0 ? 0 : &tv);
+ ret = select(socketDescriptor + 1, &fdread, &fdwrite, &fdexception, &tv);
//... but if it is actually set, pretend it did not happen
if (ret > 0 && FD_ISSET((SOCKET)socketDescriptor, &fdexception))
diff --git a/src/network/socket/qnet_unix_p.h b/src/network/socket/qnet_unix_p.h
index c72e5745b0..a172a14a10 100644
--- a/src/network/socket/qnet_unix_p.h
+++ b/src/network/socket/qnet_unix_p.h
@@ -108,7 +108,7 @@ static inline int qt_safe_connect(int sockfd, const struct sockaddr *addr, QT_SO
{
int ret;
// Solaris e.g. expects a non-const 2nd parameter
- EINTR_LOOP(ret, QT_SOCKET_CONNECT(sockfd, const_cast<struct sockaddr *>(addr), addrlen));
+ QT_EINTR_LOOP(ret, QT_SOCKET_CONNECT(sockfd, const_cast<struct sockaddr *>(addr), addrlen));
return ret;
}
#undef QT_SOCKET_CONNECT
@@ -124,15 +124,10 @@ static inline int qt_safe_connect(int sockfd, const struct sockaddr *addr, QT_SO
# undef listen
#endif
-// VxWorks' headers specify 'int' instead of '...' for the 3rd ioctl() parameter.
template <typename T>
static inline int qt_safe_ioctl(int sockfd, unsigned long request, T arg)
{
-#ifdef Q_OS_VXWORKS
- return ::ioctl(sockfd, request, (int) arg);
-#else
return ::ioctl(sockfd, request, arg);
-#endif
}
static inline int qt_safe_sendmsg(int sockfd, const struct msghdr *msg, int flags)
@@ -144,7 +139,7 @@ static inline int qt_safe_sendmsg(int sockfd, const struct msghdr *msg, int flag
#endif
int ret;
- EINTR_LOOP(ret, ::sendmsg(sockfd, msg, flags));
+ QT_EINTR_LOOP(ret, ::sendmsg(sockfd, msg, flags));
return ret;
}
@@ -152,7 +147,7 @@ static inline int qt_safe_recvmsg(int sockfd, struct msghdr *msg, int flags)
{
int ret;
- EINTR_LOOP(ret, ::recvmsg(sockfd, msg, flags));
+ QT_EINTR_LOOP(ret, ::recvmsg(sockfd, msg, flags));
return ret;
}
diff --git a/src/network/socket/qsctpserver.cpp b/src/network/socket/qsctpserver.cpp
index 4e61a6c6bd..cd060d93e8 100644
--- a/src/network/socket/qsctpserver.cpp
+++ b/src/network/socket/qsctpserver.cpp
@@ -46,7 +46,7 @@
between endpoints. Call nextPendingDatagramConnection() to accept
the pending datagram-mode connection as a connected QSctpSocket.
- \note This feature is not supported on the Windows platform.
+ \note This class is not supported on the Windows platform.
\sa QTcpServer, QSctpSocket, QAbstractSocket
*/
diff --git a/src/network/socket/qsctpserver.h b/src/network/socket/qsctpserver.h
index 23ffdc770d..f82ae71f16 100644
--- a/src/network/socket/qsctpserver.h
+++ b/src/network/socket/qsctpserver.h
@@ -9,7 +9,7 @@
QT_BEGIN_NAMESPACE
-#if !defined(QT_NO_SCTP) || defined(Q_CLANG_QDOC)
+#if !defined(QT_NO_SCTP) || defined(Q_QDOC)
class QSctpServerPrivate;
class QSctpSocket;
diff --git a/src/network/socket/qsctpsocket.cpp b/src/network/socket/qsctpsocket.cpp
index 546c2d18bf..868c9e3785 100644
--- a/src/network/socket/qsctpsocket.cpp
+++ b/src/network/socket/qsctpsocket.cpp
@@ -74,7 +74,7 @@
etc. is allowed in datagram mode with the same limitations as in
continuous byte stream mode.
- \note This feature is not supported on the Windows platform.
+ \note This class is not supported on the Windows platform.
\sa QSctpServer, QTcpSocket, QAbstractSocket
*/
@@ -83,7 +83,6 @@
#include "qsctpsocket_p.h"
#include "qabstractsocketengine_p.h"
-#include "private/qbytearray_p.h"
#ifdef QSCTPSOCKET_DEBUG
#include <qdebug.h>
@@ -133,7 +132,7 @@ bool QSctpSocketPrivate::canReadNotification()
bytesToRead = 4096;
}
- Q_ASSERT((datagramSize + qsizetype(bytesToRead)) < MaxByteArraySize);
+ Q_ASSERT((datagramSize + qsizetype(bytesToRead)) < QByteArray::max_size());
incomingDatagram.resize(datagramSize + int(bytesToRead));
#if defined (QSCTPSOCKET_DEBUG)
diff --git a/src/network/socket/qsctpsocket.h b/src/network/socket/qsctpsocket.h
index 13279ddb4a..8f0578ac26 100644
--- a/src/network/socket/qsctpsocket.h
+++ b/src/network/socket/qsctpsocket.h
@@ -9,7 +9,7 @@
QT_BEGIN_NAMESPACE
-#if !defined(QT_NO_SCTP) || defined(Q_CLANG_QDOC)
+#if !defined(QT_NO_SCTP) || defined(Q_QDOC)
class QSctpSocketPrivate;
diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp
index 8e1f9a5e34..0564ad7a33 100644
--- a/src/network/socket/qsocks5socketengine.cpp
+++ b/src/network/socket/qsocks5socketengine.cpp
@@ -9,6 +9,7 @@
#include "qdebug.h"
#include "qhash.h"
#include "qqueue.h"
+#include "qdeadlinetimer.h"
#include "qelapsedtimer.h"
#include "qmutex.h"
#include "qthread.h"
@@ -20,18 +21,21 @@
#include <qendian.h>
#include <qnetworkinterface.h>
+#include <QtCore/qpointer.h>
+
#include <memory>
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
+using namespace std::chrono_literals;
static const int MaxWriteBufferSize = 128*1024;
//#define QSOCKS5SOCKETLAYER_DEBUG
#define MAX_DATA_DUMP 256
-#define SOCKS5_BLOCKING_BIND_TIMEOUT 5000
+static constexpr auto Socks5BlockingBindTimeout = 5s;
#define Q_INIT_CHECK(returnValue) do { \
if (!d->data) { \
@@ -154,11 +158,11 @@ static bool qt_socks5_set_host_name_and_port(const QString &hostname, quint16 po
QByteArray encodedHostName = QUrl::toAce(hostname);
QByteArray &buf = *pBuf;
- if (encodedHostName.length() > 255)
+ if (encodedHostName.size() > 255)
return false;
buf.append(S5_DOMAINNAME);
- buf.append(uchar(encodedHostName.length()));
+ buf.append(uchar(encodedHostName.size()));
buf.append(encodedHostName);
// add port
@@ -317,9 +321,10 @@ void QSocks5BindStore::add(qintptr socketDescriptor, QSocks5BindData *bindData)
}
bindData->timeStamp.start();
store.insert(socketDescriptor, bindData);
+
// start sweep timer if not started
if (sweepTimerId == -1)
- sweepTimerId = startTimer(60000);
+ sweepTimerId = startTimer(1min);
}
bool QSocks5BindStore::contains(qintptr socketDescriptor)
@@ -1325,11 +1330,8 @@ bool QSocks5SocketEngine::bind(const QHostAddress &addr, quint16 port)
return false;
}
- int msecs = SOCKS5_BLOCKING_BIND_TIMEOUT;
- QElapsedTimer stopWatch;
- stopWatch.start();
d->data->controlSocket->connectToHost(d->proxyInfo.hostName(), d->proxyInfo.port());
- if (!d->waitForConnected(msecs, nullptr) ||
+ if (!d->waitForConnected(QDeadlineTimer{Socks5BlockingBindTimeout}, nullptr) ||
d->data->controlSocket->state() == QAbstractSocket::UnconnectedState) {
// waitForConnected sets the error state and closes the socket
QSOCKS5_Q_DEBUG << "waitForConnected to proxy server" << d->data->controlSocket->errorString();
@@ -1379,7 +1381,7 @@ bool QSocks5SocketEngine::listen(int backlog)
return false;
}
-int QSocks5SocketEngine::accept()
+qintptr QSocks5SocketEngine::accept()
{
Q_D(QSocks5SocketEngine);
// check we are listing ---
@@ -1420,11 +1422,9 @@ void QSocks5SocketEngine::close()
Q_D(QSocks5SocketEngine);
if (d->data && d->data->controlSocket) {
if (d->data->controlSocket->state() == QAbstractSocket::ConnectedState) {
- int msecs = 100;
- QElapsedTimer stopWatch;
- stopWatch.start();
+ QDeadlineTimer deadline(100ms);
while (!d->data->controlSocket->bytesToWrite()) {
- if (!d->data->controlSocket->waitForBytesWritten(qt_subtract_from_timeout(msecs, stopWatch.elapsed())))
+ if (!d->data->controlSocket->waitForBytesWritten(deadline.remainingTime()))
break;
}
}
@@ -1445,7 +1445,7 @@ qint64 QSocks5SocketEngine::bytesAvailable() const
#ifndef QT_NO_UDPSOCKET
else if (d->mode == QSocks5SocketEnginePrivate::UdpAssociateMode
&& !d->udpData->pendingDatagrams.isEmpty())
- return d->udpData->pendingDatagrams.first().data.size();
+ return d->udpData->pendingDatagrams.constFirst().data.size();
#endif
return 0;
}
@@ -1677,7 +1677,7 @@ bool QSocks5SocketEngine::setOption(SocketOption option, int value)
return false;
}
-bool QSocks5SocketEnginePrivate::waitForConnected(int msecs, bool *timedOut)
+bool QSocks5SocketEnginePrivate::waitForConnected(QDeadlineTimer deadline, bool *timedOut)
{
if (data->controlSocket->state() == QAbstractSocket::UnconnectedState)
return false;
@@ -1687,11 +1687,8 @@ bool QSocks5SocketEnginePrivate::waitForConnected(int msecs, bool *timedOut)
mode == BindMode ? BindSuccess :
UdpAssociateSuccess;
- QElapsedTimer stopWatch;
- stopWatch.start();
-
while (socks5State != wantedState) {
- if (!data->controlSocket->waitForReadyRead(qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
+ if (!data->controlSocket->waitForReadyRead(deadline.remainingTime())) {
if (data->controlSocket->state() == QAbstractSocket::UnconnectedState)
return true;
@@ -1705,18 +1702,15 @@ bool QSocks5SocketEnginePrivate::waitForConnected(int msecs, bool *timedOut)
return true;
}
-bool QSocks5SocketEngine::waitForRead(int msecs, bool *timedOut)
+bool QSocks5SocketEngine::waitForRead(QDeadlineTimer deadline, bool *timedOut)
{
Q_D(QSocks5SocketEngine);
- QSOCKS5_DEBUG << "waitForRead" << msecs;
+ QSOCKS5_DEBUG << "waitForRead" << deadline.remainingTimeAsDuration();
d->readNotificationActivated = false;
- QElapsedTimer stopWatch;
- stopWatch.start();
-
// are we connected yet?
- if (!d->waitForConnected(msecs, timedOut))
+ if (!d->waitForConnected(deadline, timedOut))
return false;
if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
return true;
@@ -1730,7 +1724,7 @@ bool QSocks5SocketEngine::waitForRead(int msecs, bool *timedOut)
if (d->mode == QSocks5SocketEnginePrivate::ConnectMode ||
d->mode == QSocks5SocketEnginePrivate::BindMode) {
while (!d->readNotificationActivated) {
- if (!d->data->controlSocket->waitForReadyRead(qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
+ if (!d->data->controlSocket->waitForReadyRead(deadline.remainingTime())) {
if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
return true;
@@ -1743,7 +1737,7 @@ bool QSocks5SocketEngine::waitForRead(int msecs, bool *timedOut)
#ifndef QT_NO_UDPSOCKET
} else {
while (!d->readNotificationActivated) {
- if (!d->udpData->udpSocket->waitForReadyRead(qt_subtract_from_timeout(msecs, stopWatch.elapsed()))) {
+ if (!d->udpData->udpSocket->waitForReadyRead(deadline.remainingTime())) {
setError(d->udpData->udpSocket->error(), d->udpData->udpSocket->errorString());
if (timedOut && d->udpData->udpSocket->error() == QAbstractSocket::SocketTimeoutError)
*timedOut = true;
@@ -1762,16 +1756,13 @@ bool QSocks5SocketEngine::waitForRead(int msecs, bool *timedOut)
}
-bool QSocks5SocketEngine::waitForWrite(int msecs, bool *timedOut)
+bool QSocks5SocketEngine::waitForWrite(QDeadlineTimer deadline, bool *timedOut)
{
Q_D(QSocks5SocketEngine);
- QSOCKS5_DEBUG << "waitForWrite" << msecs;
-
- QElapsedTimer stopWatch;
- stopWatch.start();
+ QSOCKS5_DEBUG << "waitForWrite" << deadline.remainingTimeAsDuration();
// are we connected yet?
- if (!d->waitForConnected(msecs, timedOut))
+ if (!d->waitForConnected(deadline, timedOut))
return false;
if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState)
return true;
@@ -1780,27 +1771,32 @@ bool QSocks5SocketEngine::waitForWrite(int msecs, bool *timedOut)
// flush any bytes we may still have buffered in the time that we have left
if (d->data->controlSocket->bytesToWrite())
- d->data->controlSocket->waitForBytesWritten(qt_subtract_from_timeout(msecs, stopWatch.elapsed()));
- while ((msecs == -1 || stopWatch.elapsed() < msecs)
- && d->data->controlSocket->state() == QAbstractSocket::ConnectedState
- && d->data->controlSocket->bytesToWrite() >= MaxWriteBufferSize)
- d->data->controlSocket->waitForBytesWritten(qt_subtract_from_timeout(msecs, stopWatch.elapsed()));
+ d->data->controlSocket->waitForBytesWritten(deadline.remainingTime());
+
+ auto shouldWriteBytes = [&]() {
+ return d->data->controlSocket->state() == QAbstractSocket::ConnectedState
+ && d->data->controlSocket->bytesToWrite() >= MaxWriteBufferSize;
+ };
+
+ qint64 remainingTime = deadline.remainingTime();
+ for (; remainingTime > 0 && shouldWriteBytes(); remainingTime = deadline.remainingTime())
+ d->data->controlSocket->waitForBytesWritten(remainingTime);
return d->data->controlSocket->bytesToWrite() < MaxWriteBufferSize;
}
bool QSocks5SocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
- int msecs, bool *timedOut)
+ QDeadlineTimer deadline, bool *timedOut)
{
Q_UNUSED(checkRead);
if (!checkWrite) {
- bool canRead = waitForRead(msecs, timedOut);
+ bool canRead = waitForRead(deadline, timedOut);
if (readyToRead)
*readyToRead = canRead;
return canRead;
}
- bool canWrite = waitForWrite(msecs, timedOut);
+ bool canWrite = waitForWrite(deadline, timedOut);
if (readyToWrite)
*readyToWrite = canWrite;
return canWrite;
diff --git a/src/network/socket/qsocks5socketengine_p.h b/src/network/socket/qsocks5socketengine_p.h
index 3d9ccf0ba1..3a169812df 100644
--- a/src/network/socket/qsocks5socketengine_p.h
+++ b/src/network/socket/qsocks5socketengine_p.h
@@ -16,8 +16,10 @@
//
#include <QtNetwork/private/qtnetworkglobal_p.h>
+
+#include <QtNetwork/qnetworkproxy.h>
+
#include "qabstractsocketengine_p.h"
-#include "qnetworkproxy.h"
QT_REQUIRE_CONFIG(socks5);
@@ -46,7 +48,7 @@ public:
bool connectToHostByName(const QString &name, quint16 port) override;
bool bind(const QHostAddress &address, quint16 port) override;
bool listen(int backlog) override;
- int accept() override;
+ qintptr accept() override;
void close() override;
qint64 bytesAvailable() const override;
@@ -76,11 +78,14 @@ public:
int option(SocketOption option) const override;
bool setOption(SocketOption option, int value) override;
- bool waitForRead(int msecs = 30000, bool *timedOut = nullptr) override;
- bool waitForWrite(int msecs = 30000, bool *timedOut = nullptr) override;
+ bool waitForRead(QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
+ bool *timedOut = nullptr) override;
+ bool waitForWrite(QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
+ bool *timedOut = nullptr) override;
bool waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
bool checkRead, bool checkWrite,
- int msecs = 30000, bool *timedOut = nullptr) override;
+ QDeadlineTimer deadline = QDeadlineTimer{DefaultTimeout},
+ bool *timedOut = nullptr) override;
bool isReadNotificationEnabled() const override;
void setReadNotificationEnabled(bool enable) override;
@@ -206,7 +211,7 @@ public:
void parseRequestMethodReply();
void parseNewConnection();
- bool waitForConnected(int msecs, bool *timedOut);
+ bool waitForConnected(QDeadlineTimer deadline, bool *timedOut);
void _q_controlSocketConnected();
void _q_controlSocketReadNotification();
diff --git a/src/network/socket/qtcpserver.cpp b/src/network/socket/qtcpserver.cpp
index e6ff34cee9..a0c0f00aaa 100644
--- a/src/network/socket/qtcpserver.cpp
+++ b/src/network/socket/qtcpserver.cpp
@@ -43,8 +43,8 @@
use waitForNewConnection(), which blocks until either a
connection is available or a timeout expires.
- \sa QTcpSocket, {Fortune Server Example}, {Threaded Fortune Server Example},
- {Loopback Example}, {Torrent Example}
+ \sa QTcpSocket, {Fortune Server}, {Threaded Fortune Server},
+ {Torrent Example}
*/
/*! \fn void QTcpServer::newConnection()
@@ -133,7 +133,7 @@ QNetworkProxy QTcpServerPrivate::resolveProxy(const QHostAddress &address, quint
}
// return the first that we can use
- for (const QNetworkProxy &p : qAsConst(proxies)) {
+ for (const QNetworkProxy &p : std::as_const(proxies)) {
if (socketType == QAbstractSocket::TcpSocket &&
(p.capabilities() & QNetworkProxy::ListeningCapability) != 0)
return p;
@@ -181,7 +181,7 @@ void QTcpServerPrivate::readNotification()
return;
}
- int descriptor = socketEngine->accept();
+ qintptr descriptor = socketEngine->accept();
if (descriptor == -1) {
if (socketEngine->error() != QAbstractSocket::TemporaryError) {
q->pauseAccepting();
@@ -498,7 +498,7 @@ bool QTcpServer::waitForNewConnection(int msec, bool *timedOut)
if (d->state != QAbstractSocket::ListeningState)
return false;
- if (!d->socketEngine->waitForRead(msec, timedOut)) {
+ if (!d->socketEngine->waitForRead(QDeadlineTimer(msec), timedOut)) {
d->serverSocketError = d->socketEngine->error();
d->serverSocketErrorString = d->socketEngine->errorString();
return false;
diff --git a/src/network/socket/qtcpsocket.cpp b/src/network/socket/qtcpsocket.cpp
index 3d06197b1e..979382f26c 100644
--- a/src/network/socket/qtcpsocket.cpp
+++ b/src/network/socket/qtcpsocket.cpp
@@ -23,9 +23,9 @@
\note TCP sockets cannot be opened in QIODevice::Unbuffered mode.
\sa QTcpServer, QUdpSocket, QNetworkAccessManager,
- {Fortune Server Example}, {Fortune Client Example},
- {Threaded Fortune Server Example}, {Blocking Fortune Client Example},
- {Loopback Example}, {Torrent Example}
+ {Fortune Server}, {Fortune Client},
+ {Threaded Fortune Server}, {Blocking Fortune Client},
+ {Torrent Example}
*/
#include "qtcpsocket.h"
diff --git a/src/network/socket/qtcpsocket.h b/src/network/socket/qtcpsocket.h
index aeac92c678..a1c610b69b 100644
--- a/src/network/socket/qtcpsocket.h
+++ b/src/network/socket/qtcpsocket.h
@@ -21,7 +21,7 @@ public:
explicit QTcpSocket(QObject *parent = nullptr);
virtual ~QTcpSocket();
-#if QT_VERSION < QT_VERSION_CHECK(7,0,0) && !defined(Q_CLANG_QDOC)
+#if QT_VERSION < QT_VERSION_CHECK(7,0,0) && !defined(Q_QDOC)
// ### Qt7: move into QAbstractSocket
using QAbstractSocket::bind;
bool bind(QHostAddress::SpecialAddress addr, quint16 port = 0, BindMode mode = DefaultForPlatform)
diff --git a/src/network/socket/qudpsocket.cpp b/src/network/socket/qudpsocket.cpp
index 2a7982057e..bfeea307b2 100644
--- a/src/network/socket/qudpsocket.cpp
+++ b/src/network/socket/qudpsocket.cpp
@@ -382,7 +382,7 @@ qint64 QUdpSocket::writeDatagram(const QNetworkDatagram &datagram)
if (state() == UnconnectedState)
bind();
- qint64 sent = d->socketEngine->writeDatagram(datagram.d->data,
+ qint64 sent = d->socketEngine->writeDatagram(datagram.d->data.constData(),
datagram.d->data.size(),
datagram.d->header);
d->cachedSocketDescriptor = d->socketEngine->socketDescriptor();
@@ -430,6 +430,7 @@ QNetworkDatagram QUdpSocket::receiveDatagram(qint64 maxSize)
qint64 readBytes = d->socketEngine->readDatagram(result.d->data.data(), maxSize, &result.d->header,
QAbstractSocketEngine::WantAll);
d->hasPendingData = false;
+ d->hasPendingDatagram = false;
d->socketEngine->setReadNotificationEnabled(true);
if (readBytes < 0) {
d->setErrorAndEmit(d->socketEngine->error(), d->socketEngine->errorString());
@@ -479,6 +480,7 @@ qint64 QUdpSocket::readDatagram(char *data, qint64 maxSize, QHostAddress *addres
}
d->hasPendingData = false;
+ d->hasPendingDatagram = false;
d->socketEngine->setReadNotificationEnabled(true);
if (readBytes < 0) {
if (readBytes == -2) {
diff --git a/src/network/socket/qudpsocket.h b/src/network/socket/qudpsocket.h
index 34657fffa5..3fd1d3710a 100644
--- a/src/network/socket/qudpsocket.h
+++ b/src/network/socket/qudpsocket.h
@@ -24,7 +24,7 @@ public:
explicit QUdpSocket(QObject *parent = nullptr);
virtual ~QUdpSocket();
-#if QT_VERSION < QT_VERSION_CHECK(7,0,0) && !defined(Q_CLANG_QDOC)
+#if QT_VERSION < QT_VERSION_CHECK(7,0,0) && !defined(Q_QDOC)
// ### Qt7: move into QAbstractSocket
using QAbstractSocket::bind;
bool bind(QHostAddress::SpecialAddress addr, quint16 port = 0, BindMode mode = DefaultForPlatform)
diff --git a/src/network/ssl/qdtls.h b/src/network/ssl/qdtls.h
index 903ba5b4e1..dd24aa219a 100644
--- a/src/network/ssl/qdtls.h
+++ b/src/network/ssl/qdtls.h
@@ -15,7 +15,7 @@
Q_MOC_INCLUDE(<QtNetwork/QSslPreSharedKeyAuthenticator>)
-#ifndef Q_CLANG_QDOC
+#ifndef Q_QDOC
QT_REQUIRE_CONFIG(dtls);
#endif
diff --git a/src/network/ssl/qocspresponse.h b/src/network/ssl/qocspresponse.h
index d208edab7a..68251a1547 100644
--- a/src/network/ssl/qocspresponse.h
+++ b/src/network/ssl/qocspresponse.h
@@ -11,7 +11,7 @@
#include <QtCore/qmetatype.h>
#include <QtCore/qobject.h>
-#ifndef Q_CLANG_QDOC
+#ifndef Q_QDOC
QT_REQUIRE_CONFIG(ssl);
#endif
diff --git a/src/network/ssl/qpassworddigestor.cpp b/src/network/ssl/qpassworddigestor.cpp
index a6c6e7666c..94de14abd4 100644
--- a/src/network/ssl/qpassworddigestor.cpp
+++ b/src/network/ssl/qpassworddigestor.cpp
@@ -6,9 +6,20 @@
#include <QtCore/QDebug>
#include <QtCore/QMessageAuthenticationCode>
#include <QtCore/QtEndian>
+#include <QtCore/QList>
+
+#include "qtcore-config_p.h"
#include <limits>
+#if QT_CONFIG(opensslv30) && QT_CONFIG(openssl_linked)
+#define USING_OPENSSL30
+#include <openssl/core_names.h>
+#include <openssl/kdf.h>
+#include <openssl/params.h>
+#include <openssl/provider.h>
+#endif
+
QT_BEGIN_NAMESPACE
namespace QPasswordDigestor {
@@ -86,6 +97,85 @@ Q_NETWORK_EXPORT QByteArray deriveKeyPbkdf1(QCryptographicHash::Algorithm algori
return key.left(dkLen);
}
+#ifdef USING_OPENSSL30
+// Copied from QCryptographicHashPrivate
+static constexpr const char * methodToName(QCryptographicHash::Algorithm method) noexcept
+{
+ switch (method) {
+#define CASE(Enum, Name) \
+ case QCryptographicHash:: Enum : \
+ return Name \
+ /*end*/
+ CASE(Sha1, "SHA1");
+ CASE(Md4, "MD4");
+ CASE(Md5, "MD5");
+ CASE(Sha224, "SHA224");
+ CASE(Sha256, "SHA256");
+ CASE(Sha384, "SHA384");
+ CASE(Sha512, "SHA512");
+ CASE(RealSha3_224, "SHA3-224");
+ CASE(RealSha3_256, "SHA3-256");
+ CASE(RealSha3_384, "SHA3-384");
+ CASE(RealSha3_512, "SHA3-512");
+ CASE(Keccak_224, "SHA3-224");
+ CASE(Keccak_256, "SHA3-256");
+ CASE(Keccak_384, "SHA3-384");
+ CASE(Keccak_512, "SHA3-512");
+ CASE(Blake2b_512, "BLAKE2B512");
+ CASE(Blake2s_256, "BLAKE2S256");
+#undef CASE
+ default: return nullptr;
+ }
+}
+
+static QByteArray opensslDeriveKeyPbkdf2(QCryptographicHash::Algorithm algorithm,
+ const QByteArray &data, const QByteArray &salt,
+ uint64_t iterations, quint64 dkLen)
+{
+ EVP_KDF *kdf = EVP_KDF_fetch(nullptr, "PBKDF2", nullptr);
+
+ if (!kdf)
+ return QByteArray();
+
+ auto cleanUpKdf = qScopeGuard([kdf] {
+ EVP_KDF_free(kdf);
+ });
+
+ EVP_KDF_CTX *ctx = EVP_KDF_CTX_new(kdf);
+
+ if (!ctx)
+ return QByteArray();
+
+ auto cleanUpCtx = qScopeGuard([ctx] {
+ EVP_KDF_CTX_free(ctx);
+ });
+
+ // Do not enable SP800-132 compliance check, otherwise we will require:
+ // - the iteration count is at least 1000
+ // - the salt length is at least 128 bits
+ // - the derived key length is at least 112 bits
+ // This would be a different behavior from the original implementation.
+ int checkDisabled = 1;
+ QList<OSSL_PARAM> params;
+ params.append(OSSL_PARAM_construct_utf8_string(OSSL_KDF_PARAM_DIGEST, const_cast<char*>(methodToName(algorithm)), 0));
+ params.append(OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_SALT, const_cast<char*>(salt.data()), salt.size()));
+ params.append(OSSL_PARAM_construct_octet_string(OSSL_KDF_PARAM_PASSWORD, const_cast<char*>(data.data()), data.size()));
+ params.append(OSSL_PARAM_construct_uint64(OSSL_KDF_PARAM_ITER, &iterations));
+ params.append(OSSL_PARAM_construct_int(OSSL_KDF_PARAM_PKCS5, &checkDisabled));
+ params.append(OSSL_PARAM_construct_end());
+
+ if (EVP_KDF_CTX_set_params(ctx, params.data()) <= 0)
+ return QByteArray();
+
+ QByteArray derived(dkLen, '\0');
+
+ if (!EVP_KDF_derive(ctx, reinterpret_cast<unsigned char*>(derived.data()), derived.size(), nullptr))
+ return QByteArray();
+
+ return derived;
+}
+#endif
+
/*!
\since 5.12
@@ -107,8 +197,6 @@ Q_NETWORK_EXPORT QByteArray deriveKeyPbkdf2(QCryptographicHash::Algorithm algori
const QByteArray &data, const QByteArray &salt,
int iterations, quint64 dkLen)
{
- // https://tools.ietf.org/html/rfc8018#section-5.2
-
// The RFC recommends checking that 'dkLen' is not greater than '(2^32 - 1) * hLen'
int hashLen = QCryptographicHash::hashLength(algorithm);
const quint64 maxLen = quint64(std::numeric_limits<quint32>::max() - 1) * hashLen;
@@ -122,11 +210,17 @@ Q_NETWORK_EXPORT QByteArray deriveKeyPbkdf2(QCryptographicHash::Algorithm algori
if (iterations < 1 || dkLen < 1)
return QByteArray();
+#ifdef USING_OPENSSL30
+ if (methodToName(algorithm))
+ return opensslDeriveKeyPbkdf2(algorithm, data, salt, iterations, dkLen);
+#endif
+
+ // https://tools.ietf.org/html/rfc8018#section-5.2
QByteArray key;
quint32 currentIteration = 1;
QMessageAuthenticationCode hmac(algorithm, data);
QByteArray index(4, Qt::Uninitialized);
- while (quint64(key.length()) < dkLen) {
+ while (quint64(key.size()) < dkLen) {
hmac.addData(salt);
qToBigEndian(currentIteration, index.data());
diff --git a/src/network/ssl/qssl.cpp b/src/network/ssl/qssl.cpp
index 7525df8c87..dfd3745d3e 100644
--- a/src/network/ssl/qssl.cpp
+++ b/src/network/ssl/qssl.cpp
@@ -256,3 +256,5 @@ Q_LOGGING_CATEGORY(lcSsl, "qt.network.ssl");
*/
QT_END_NAMESPACE
+
+#include "moc_qssl.cpp"
diff --git a/src/network/ssl/qssl.h b/src/network/ssl/qssl.h
index 3d01168172..e52b8c6361 100644
--- a/src/network/ssl/qssl.h
+++ b/src/network/ssl/qssl.h
@@ -10,21 +10,26 @@
#endif
#include <QtNetwork/qtnetworkglobal.h>
+#include <QtCore/qobjectdefs.h>
#include <QtCore/QFlags>
QT_BEGIN_NAMESPACE
namespace QSsl {
+ Q_NAMESPACE_EXPORT(Q_NETWORK_EXPORT)
+
enum KeyType {
PrivateKey,
PublicKey
};
+ Q_ENUM_NS(KeyType)
enum EncodingFormat {
Pem,
Der
};
+ Q_ENUM_NS(EncodingFormat)
enum KeyAlgorithm {
Opaque,
@@ -33,12 +38,14 @@ namespace QSsl {
Ec,
Dh,
};
+ Q_ENUM_NS(KeyAlgorithm)
enum AlternativeNameEntryType {
EmailEntry,
DnsEntry,
IpAddressEntry
};
+ Q_ENUM_NS(AlternativeNameEntryType)
enum SslProtocol {
TlsV1_0 QT_DEPRECATED_VERSION_X_6_3("Use TlsV1_2OrLater instead."),
@@ -61,6 +68,7 @@ namespace QSsl {
UnknownProtocol = -1
};
+ Q_ENUM_NS(SslProtocol)
enum SslOption {
SslOptionDisableEmptyFragments = 0x01,
@@ -72,6 +80,7 @@ namespace QSsl {
SslOptionDisableSessionPersistence = 0x40,
SslOptionDisableServerCipherPreference = 0x80
};
+ Q_ENUM_NS(SslOption)
Q_DECLARE_FLAGS(SslOptions, SslOption)
enum class AlertLevel {
@@ -79,6 +88,7 @@ namespace QSsl {
Fatal,
Unknown
};
+ Q_ENUM_NS(AlertLevel)
enum class AlertType {
CloseNotify,
@@ -116,6 +126,7 @@ namespace QSsl {
NoApplicationProtocol = 120,
UnknownAlertMessage = 255
};
+ Q_ENUM_NS(AlertType)
enum class ImplementedClass
{
@@ -127,6 +138,7 @@ namespace QSsl {
Dtls,
DtlsCookie
};
+ Q_ENUM_NS(ImplementedClass)
enum class SupportedFeature
{
@@ -138,6 +150,7 @@ namespace QSsl {
SessionTicket,
Alerts
};
+ Q_ENUM_NS(SupportedFeature)
}
Q_DECLARE_OPERATORS_FOR_FLAGS(QSsl::SslOptions)
diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp
index 0f851ec7c9..9878c603b6 100644
--- a/src/network/ssl/qsslcertificate.cpp
+++ b/src/network/ssl/qsslcertificate.cpp
@@ -110,7 +110,7 @@
#endif
#include <QtCore/qdir.h>
-#include <QtCore/qdiriterator.h>
+#include <QtCore/qdirlisting.h>
#include <QtCore/qfile.h>
QT_BEGIN_NAMESPACE
@@ -186,7 +186,7 @@ QSslCertificate::QSslCertificate(const QByteArray &data, QSsl::EncodingFormat fo
return;
}
- QList<QSslCertificate> certs = X509Reader(data, 1);
+ const QList<QSslCertificate> certs = X509Reader(data, 1);
if (!certs.isEmpty())
d = certs.first().d;
}
@@ -624,7 +624,7 @@ QList<QSslCertificate> QSslCertificate::fromPath(const QString &path,
QString sourcePath = QDir::fromNativeSeparators(path);
// Find the path without the filename
- QString pathPrefix = sourcePath.left(sourcePath.lastIndexOf(u'/'));
+ QStringView pathPrefix = QStringView(sourcePath).left(sourcePath.lastIndexOf(u'/'));
// Check if the path contains any special chars
int pos = -1;
@@ -647,7 +647,7 @@ QList<QSslCertificate> QSslCertificate::fromPath(const QString &path,
if (lastIndexOfSlash != -1)
pathPrefix = pathPrefix.left(lastIndexOfSlash);
else
- pathPrefix.clear();
+ pathPrefix = {};
} else {
// Check if the path is a file.
if (QFileInfo(sourcePath).isFile()) {
@@ -664,10 +664,12 @@ QList<QSslCertificate> QSslCertificate::fromPath(const QString &path,
// Special case - if the prefix ends up being nothing, use "." instead.
int startIndex = 0;
if (pathPrefix.isEmpty()) {
- pathPrefix = "."_L1;
+ pathPrefix = u".";
startIndex = 2;
}
+ const QString pathPrefixString = pathPrefix.toString();
+
// The path can be a file or directory.
QList<QSslCertificate> certs;
@@ -678,9 +680,12 @@ QList<QSslCertificate> QSslCertificate::fromPath(const QString &path,
QRegularExpression pattern(QRegularExpression::anchoredPattern(sourcePath));
#endif
- QDirIterator it(pathPrefix, QDir::Files, QDirIterator::FollowSymlinks | QDirIterator::Subdirectories);
- while (it.hasNext()) {
- QString filePath = startIndex == 0 ? it.next() : it.next().mid(startIndex);
+ using F = QDirListing::IteratorFlag;
+ constexpr auto iterFlags = F::FollowSymlinks | F::Recursive;
+ for (const auto &dirEntry : QDirListing(pathPrefixString, QDir::Files, iterFlags)) {
+ QString filePath = dirEntry.filePath();
+ if (startIndex > 0)
+ filePath.remove(0, startIndex);
#if QT_CONFIG(regularexpression)
if (!pattern.match(filePath).hasMatch())
@@ -878,7 +883,7 @@ static const char *const certificate_blacklist[] = {
bool QSslCertificatePrivate::isBlacklisted(const QSslCertificate &certificate)
{
for (int a = 0; certificate_blacklist[a] != nullptr; a++) {
- QString blacklistedCommonName = QString::fromUtf8(certificate_blacklist[(a+1)]);
+ auto blacklistedCommonName = QAnyStringView(QUtf8StringView(certificate_blacklist[(a+1)]));
if (certificate.serialNumber() == certificate_blacklist[a++] &&
(certificate.subjectInfo(QSslCertificate::CommonName).contains(blacklistedCommonName) ||
certificate.issuerInfo(QSslCertificate::CommonName).contains(blacklistedCommonName)))
@@ -889,19 +894,18 @@ bool QSslCertificatePrivate::isBlacklisted(const QSslCertificate &certificate)
QByteArray QSslCertificatePrivate::subjectInfoToString(QSslCertificate::SubjectInfo info)
{
- QByteArray str;
switch (info) {
- case QSslCertificate::Organization: str = QByteArray("O"); break;
- case QSslCertificate::CommonName: str = QByteArray("CN"); break;
- case QSslCertificate::LocalityName: str = QByteArray("L"); break;
- case QSslCertificate::OrganizationalUnitName: str = QByteArray("OU"); break;
- case QSslCertificate::CountryName: str = QByteArray("C"); break;
- case QSslCertificate::StateOrProvinceName: str = QByteArray("ST"); break;
- case QSslCertificate::DistinguishedNameQualifier: str = QByteArray("dnQualifier"); break;
- case QSslCertificate::SerialNumber: str = QByteArray("serialNumber"); break;
- case QSslCertificate::EmailAddress: str = QByteArray("emailAddress"); break;
+ case QSslCertificate::Organization: return "O"_ba;
+ case QSslCertificate::CommonName: return "CN"_ba;
+ case QSslCertificate::LocalityName: return"L"_ba;
+ case QSslCertificate::OrganizationalUnitName: return "OU"_ba;
+ case QSslCertificate::CountryName: return "C"_ba;
+ case QSslCertificate::StateOrProvinceName: return "ST"_ba;
+ case QSslCertificate::DistinguishedNameQualifier: return "dnQualifier"_ba;
+ case QSslCertificate::SerialNumber: return "serialNumber"_ba;
+ case QSslCertificate::EmailAddress: return "emailAddress"_ba;
}
- return str;
+ return QByteArray();
}
/*!
@@ -918,13 +922,13 @@ QString QSslCertificate::issuerDisplayName() const
QStringList names;
names = issuerInfo(QSslCertificate::CommonName);
if (!names.isEmpty())
- return names.first();
+ return names.constFirst();
names = issuerInfo(QSslCertificate::Organization);
if (!names.isEmpty())
- return names.first();
+ return names.constFirst();
names = issuerInfo(QSslCertificate::OrganizationalUnitName);
if (!names.isEmpty())
- return names.first();
+ return names.constFirst();
return QString();
}
@@ -943,13 +947,13 @@ QString QSslCertificate::subjectDisplayName() const
QStringList names;
names = subjectInfo(QSslCertificate::CommonName);
if (!names.isEmpty())
- return names.first();
+ return names.constFirst();
names = subjectInfo(QSslCertificate::Organization);
if (!names.isEmpty())
- return names.first();
+ return names.constFirst();
names = subjectInfo(QSslCertificate::OrganizationalUnitName);
if (!names.isEmpty())
- return names.first();
+ return names.constFirst();
return QString();
}
@@ -974,15 +978,15 @@ QDebug operator<<(QDebug debug, const QSslCertificate &certificate)
QDebugStateSaver saver(debug);
debug.resetFormat().nospace();
debug << "QSslCertificate("
- << certificate.version()
- << ", " << certificate.serialNumber()
- << ", " << certificate.digest().toBase64()
- << ", " << certificate.issuerDisplayName()
- << ", " << certificate.subjectDisplayName()
- << ", " << certificate.subjectAlternativeNames()
+ << "Version=" << certificate.version()
+ << ", SerialNumber=" << certificate.serialNumber()
+ << ", Digest=" << certificate.digest().toBase64()
+ << ", Issuer=" << certificate.issuerDisplayName()
+ << ", Subject=" << certificate.subjectDisplayName()
+ << ", AlternativeSubjectNames=" << certificate.subjectAlternativeNames()
#if QT_CONFIG(datestring)
- << ", " << certificate.effectiveDate()
- << ", " << certificate.expiryDate()
+ << ", EffectiveDate=" << certificate.effectiveDate()
+ << ", ExpiryDate=" << certificate.expiryDate()
#endif
<< ')';
return debug;
diff --git a/src/network/ssl/qsslcertificate.h b/src/network/ssl/qsslcertificate.h
index 5807c8d2c7..cdf11b28b0 100644
--- a/src/network/ssl/qsslcertificate.h
+++ b/src/network/ssl/qsslcertificate.h
@@ -14,8 +14,8 @@
#include <QtCore/qbytearray.h>
#include <QtCore/qcryptographichash.h>
#include <QtCore/qdatetime.h>
-#include <QtCore/qsharedpointer.h>
#include <QtCore/qmap.h>
+#include <QtCore/qshareddata.h>
#include <QtNetwork/qssl.h>
QT_BEGIN_NAMESPACE
diff --git a/src/network/ssl/qsslcertificate_p.h b/src/network/ssl/qsslcertificate_p.h
index 02cf002460..ca59abae82 100644
--- a/src/network/ssl/qsslcertificate_p.h
+++ b/src/network/ssl/qsslcertificate_p.h
@@ -35,8 +35,8 @@ public:
~QSslCertificatePrivate();
QList<QSslCertificateExtension> extensions() const;
- Q_NETWORK_PRIVATE_EXPORT static bool isBlacklisted(const QSslCertificate &certificate);
- Q_NETWORK_PRIVATE_EXPORT static QByteArray subjectInfoToString(QSslCertificate::SubjectInfo info);
+ Q_NETWORK_EXPORT static bool isBlacklisted(const QSslCertificate &certificate);
+ Q_NETWORK_EXPORT static QByteArray subjectInfoToString(QSslCertificate::SubjectInfo info);
QAtomicInt ref;
std::unique_ptr<QTlsPrivate::X509Certificate> backend;
diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp
index 80377f3426..fd308d7037 100644
--- a/src/network/ssl/qsslconfiguration.cpp
+++ b/src/network/ssl/qsslconfiguration.cpp
@@ -105,6 +105,12 @@ const char QSslConfiguration::NextProtocolHttp1_1[] = "http/1.1";
*/
/*!
+ \variable QSslConfiguration::ALPNProtocolHTTP2
+ \brief The value used for negotiating HTTP 2 during the Application-Layer
+ Protocol Negotiation.
+*/
+
+/*!
Constructs an empty SSL configuration. This configuration contains
no valid settings and the state will be empty. isNull() will
return true after this constructor is called.
@@ -218,15 +224,15 @@ bool QSslConfiguration::isNull() const
d->peerVerifyMode == QSslSocket::AutoVerifyPeer &&
d->peerVerifyDepth == 0 &&
d->allowRootCertOnDemandLoading == true &&
- d->caCertificates.count() == 0 &&
- d->ciphers.count() == 0 &&
+ d->caCertificates.size() == 0 &&
+ d->ciphers.size() == 0 &&
d->ellipticCurves.isEmpty() &&
d->ephemeralServerKey.isNull() &&
d->dhParams == QSslDiffieHellmanParameters::defaultParameters() &&
d->localCertificateChain.isEmpty() &&
d->privateKey.isNull() &&
d->peerCertificate.isNull() &&
- d->peerCertificateChain.count() == 0 &&
+ d->peerCertificateChain.size() == 0 &&
d->backendConfig.isEmpty() &&
d->sslOptions == QSslConfigurationPrivate::defaultSslOptions &&
d->sslSession.isNull() &&
@@ -550,8 +556,6 @@ void QSslConfiguration::setPrivateKey(const QSslKey &key)
ciphers. You can revert to using the entire set by calling
setCiphers() with the list returned by supportedCiphers().
- \note This is not currently supported in the Schannel backend.
-
\sa setCiphers(), supportedCiphers()
*/
QList<QSslCipher> QSslConfiguration::ciphers() const
@@ -567,8 +571,6 @@ QList<QSslCipher> QSslConfiguration::ciphers() const
Restricting the cipher suite must be done before the handshake
phase, where the session cipher is chosen.
- \note This is not currently supported in the Schannel backend.
-
\sa ciphers(), supportedCiphers()
*/
void QSslConfiguration::setCiphers(const QList<QSslCipher> &ciphers)
@@ -581,16 +583,14 @@ void QSslConfiguration::setCiphers(const QList<QSslCipher> &ciphers)
Sets the cryptographic cipher suite for this configuration to \a ciphers,
which is a colon-separated list of cipher suite names. The ciphers are listed
- in order of preference, starting with the most preferred cipher. For example:
-
- \snippet code/src_network_ssl_qsslconfiguration.cpp 1
-
+ in order of preference, starting with the most preferred cipher.
Each cipher name in \a ciphers must be the name of a cipher in the
list returned by supportedCiphers(). Restricting the cipher suite
must be done before the handshake phase, where the session cipher
is chosen.
- \note This is not currently supported in the Schannel backend.
+ \note With the Schannel backend the order of the ciphers is ignored and Schannel
+ picks the most secure one during the handshake.
\sa ciphers()
*/
@@ -922,7 +922,11 @@ void QSslConfiguration::setPreSharedKeyIdentityHint(const QByteArray &hint)
Retrieves the current set of Diffie-Hellman parameters.
If no Diffie-Hellman parameters have been set, the QSslConfiguration object
- defaults to using the 1024-bit MODP group from RFC 2409.
+ defaults to using the 2048-bit MODP group from RFC 3526.
+
+ \note The default parameters may change in future Qt versions.
+ Please check the documentation of the \e{exact Qt version} that you
+ are using in order to know what defaults that version uses.
*/
QSslDiffieHellmanParameters QSslConfiguration::diffieHellmanParameters() const
{
@@ -936,7 +940,14 @@ QSslDiffieHellmanParameters QSslConfiguration::diffieHellmanParameters() const
a server to \a dhparams.
If no Diffie-Hellman parameters have been set, the QSslConfiguration object
- defaults to using the 1024-bit MODP group from RFC 2409.
+ defaults to using the 2048-bit MODP group from RFC 3526.
+
+ Since 6.7 you can provide an empty Diffie-Hellman parameter to use auto selection
+ (see SSL_CTX_set_dh_auto of openssl) if the tls backend supports it.
+
+ \note The default parameters may change in future Qt versions.
+ Please check the documentation of the \e{exact Qt version} that you
+ are using in order to know what defaults that version uses.
*/
void QSslConfiguration::setDiffieHellmanParameters(const QSslDiffieHellmanParameters &dhparams)
{
@@ -1098,7 +1109,7 @@ void QSslConfiguration::setDefaultConfiguration(const QSslConfiguration &configu
QSslConfigurationPrivate::setDefaultConfiguration(configuration);
}
-#if QT_CONFIG(dtls) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(dtls) || defined(Q_QDOC)
/*!
This function returns true if DTLS cookie verification was enabled on a
diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h
index 85c287bb2f..dd2dd2a97c 100644
--- a/src/network/ssl/qsslconfiguration.h
+++ b/src/network/ssl/qsslconfiguration.h
@@ -125,7 +125,7 @@ public:
static QSslConfiguration defaultConfiguration();
static void setDefaultConfiguration(const QSslConfiguration &configuration);
-#if QT_CONFIG(dtls) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(dtls) || defined(Q_QDOC)
bool dtlsCookieVerificationEnabled() const;
void setDtlsCookieVerificationEnabled(bool enable);
diff --git a/src/network/ssl/qssldiffiehellmanparameters.cpp b/src/network/ssl/qssldiffiehellmanparameters.cpp
index 04add93116..7da14f3536 100644
--- a/src/network/ssl/qssldiffiehellmanparameters.cpp
+++ b/src/network/ssl/qssldiffiehellmanparameters.cpp
@@ -33,17 +33,18 @@
QT_BEGIN_NAMESPACE
-// The 1024-bit MODP group from RFC 2459 (Second Oakley Group)
+// The 2048-bit MODP group from RFC 3526
Q_AUTOTEST_EXPORT const char *qssl_dhparams_default_base64 =
- "MIGHAoGBAP//////////yQ/aoiFowjTExmKLgNwc0SkCTgiKZ8x0Agu+pjsTmyJR"
- "Sgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHCReSFtXZiXn7G9ExC6aY37WsL"
- "/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR7OZTgf//////////AgEC";
+ "MIIBCAKCAQEA///////////JD9qiIWjCNMTGYouA3BzRKQJOCIpnzHQCC76mOxObIlFKCHmO"
+ "NATd75UZs806QxswKwpt8l8UN0/hNW1tUcJF5IW1dmJefsb0TELppjftawv/XLb0Brft7jhr"
+ "+1qJn6WunyQRfEsf5kkoZlHs5Fs9wgB8uKFjvwWY2kg2HFXTmmkWP6j9JM9fg2VdI9yjrZYc"
+ "YvNWIIVSu57VKQdwlpZtZww1Tkq8mATxdGwIyhghfDKQXkYuNs474553LBgOhgObJ4Oi7Aei"
+ "j7XFXfBvTFLJ3ivL9pVYFxg5lUl86pVq5RXSJhiY+gUQFXKOWoqsqmj//////////wIBAg==";
/*!
Returns the default QSslDiffieHellmanParameters used by QSslSocket.
- This is currently the 1024-bit MODP group from RFC 2459, also
- known as the Second Oakley Group.
+ This is currently the 2048-bit MODP group from RFC 3526.
*/
QSslDiffieHellmanParameters QSslDiffieHellmanParameters::defaultParameters()
{
@@ -241,8 +242,7 @@ QString QSslDiffieHellmanParameters::errorString() const noexcept
return QCoreApplication::translate("QSslDiffieHellmanParameter", "The given Diffie-Hellman parameters are deemed unsafe");
}
- Q_UNREACHABLE();
- return QString();
+ Q_UNREACHABLE_RETURN(QString());
}
/*!
diff --git a/src/network/ssl/qsslkey.h b/src/network/ssl/qsslkey.h
index db14c89c98..decfc4b5a1 100644
--- a/src/network/ssl/qsslkey.h
+++ b/src/network/ssl/qsslkey.h
@@ -8,7 +8,7 @@
#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qnamespace.h>
#include <QtCore/qbytearray.h>
-#include <QtCore/qsharedpointer.h>
+#include <QtCore/qshareddata.h>
#include <QtNetwork/qssl.h>
QT_BEGIN_NAMESPACE
diff --git a/src/network/ssl/qsslpresharedkeyauthenticator.h b/src/network/ssl/qsslpresharedkeyauthenticator.h
index 98cfad19ed..a3912406d3 100644
--- a/src/network/ssl/qsslpresharedkeyauthenticator.h
+++ b/src/network/ssl/qsslpresharedkeyauthenticator.h
@@ -16,6 +16,7 @@ QT_BEGIN_NAMESPACE
class QSslPreSharedKeyAuthenticatorPrivate;
class QSslPreSharedKeyAuthenticator
{
+ Q_GADGET_EXPORT(Q_NETWORK_EXPORT)
public:
Q_NETWORK_EXPORT QSslPreSharedKeyAuthenticator();
Q_NETWORK_EXPORT ~QSslPreSharedKeyAuthenticator();
diff --git a/src/network/ssl/qsslserver.cpp b/src/network/ssl/qsslserver.cpp
index 497ea5bd3c..40a6a6f526 100644
--- a/src/network/ssl/qsslserver.cpp
+++ b/src/network/ssl/qsslserver.cpp
@@ -16,7 +16,7 @@
Transport Layer Security (TLS).
To configure the secure handshake settings, use the applicable setter
- functions on a QSslConfiguration object, and then use it as a argument
+ functions on a QSslConfiguration object, and then use it as an argument
to the setSslConfiguration() function. All following incoming
connections handled will use these settings.
@@ -32,7 +32,7 @@
nextPendingConnection() function to fetch the next incoming connection and
take it out of the pending connection queue. The QSslSocket is a child of
the QSslServer and will be deleted when the QSslServer is deleted. It is
- still a good a good idea to destroy the object explicitly when you are done
+ still a good idea to destroy the object explicitly when you are done
with it, to avoid wasting memory.
\sa QTcpServer, QSslConfiguration, QSslSocket
@@ -171,6 +171,13 @@
QSslConfiguration::setHandshakeMustInterruptOnError()
*/
+/*!
+ \fn void QSslServer::startedEncryptionHandshake(QSslSocket *socket)
+
+ This signal is emitted when the client, connected to \a socket,
+ initiates the TLS handshake.
+*/
+
#include "qsslserver.h"
#include "qsslserver_p.h"
@@ -334,7 +341,7 @@ void QSslServerPrivate::initializeHandshakeProcess(QSslSocket *socket)
});
auto it = socketData.emplace(quintptr(socket), readyRead, destroyed, std::make_shared<QTimer>());
it->timeoutTimer->setSingleShot(true);
- it->timeoutTimer->callOnTimeout([this, socket]() { handleHandshakeTimedOut(socket); });
+ it->timeoutTimer->callOnTimeout(q, [this, socket]() { handleHandshakeTimedOut(socket); });
it->timeoutTimer->setInterval(handshakeTimeout);
it->timeoutTimer->start();
}
@@ -401,3 +408,5 @@ void QSslServerPrivate::handleHandshakeTimedOut(QSslSocket *socket)
}
QT_END_NAMESPACE
+
+#include "moc_qsslserver.cpp"
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index cd76517c25..395394d432 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -97,8 +97,7 @@
\list
\li The socket's cryptographic cipher suite can be customized before
- the handshake phase with QSslConfiguration::setCiphers()
- and QSslConfiguration::setDefaultCiphers().
+ the handshake phase with QSslConfiguration::setCiphers().
\li The socket's local certificate and private key can be customized
before the handshake phase with setLocalCertificate() and
setPrivateKey().
@@ -365,6 +364,12 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
+#ifdef Q_OS_VXWORKS
+constexpr auto isVxworks = true;
+#else
+constexpr auto isVxworks = false;
+#endif
+
class QSslSocketGlobalData
{
public:
@@ -1539,7 +1544,12 @@ QList<QString> QSslSocket::availableBackends()
from the list of available backends.
\note When selecting a default backend implicitly, QSslSocket prefers
- the OpenSSL backend if available.
+ the OpenSSL backend if available. If it's not available, the Schannel backend
+ is implicitly selected on Windows, and Secure Transport on Darwin platforms.
+ Failing these, if a custom TLS backend is found, it is used.
+ If no other backend is found, the "certificate only" backend is selected.
+ For more information about TLS plugins, please see
+ \l {Enabling and Disabling SSL Support when Building Qt from Source}.
\sa setActiveBackend(), availableBackends()
*/
@@ -1973,6 +1983,10 @@ QSslSocketPrivate::QSslSocketPrivate()
, flushTriggered(false)
{
QSslConfigurationPrivate::deepCopyDefaultConfiguration(&configuration);
+ // If the global configuration doesn't allow root certificates to be loaded
+ // on demand then we have to disable it for this socket as well.
+ if (!configuration.allowRootCertOnDemandLoading)
+ allowRootCertOnDemandLoading = false;
const auto *tlsBackend = tlsBackendInUse();
if (!tlsBackend) {
@@ -2281,6 +2295,7 @@ void QSslConfigurationPrivate::deepCopyDefaultConfiguration(QSslConfigurationPri
ptr->sessionProtocol = global->sessionProtocol;
ptr->ciphers = global->ciphers;
ptr->caCertificates = global->caCertificates;
+ ptr->allowRootCertOnDemandLoading = global->allowRootCertOnDemandLoading;
ptr->protocol = global->protocol;
ptr->peerVerifyMode = global->peerVerifyMode;
ptr->peerVerifyDepth = global->peerVerifyDepth;
@@ -2661,7 +2676,7 @@ bool QSslSocketPrivate::verifyErrorsHaveBeenIgnored()
// was called)
const auto &sslErrors = backend->tlsErrors();
doEmitSslError = false;
- for (int a = 0; a < sslErrors.count(); a++) {
+ for (int a = 0; a < sslErrors.size(); a++) {
if (!ignoreErrorsList.contains(sslErrors.at(a))) {
doEmitSslError = true;
break;
@@ -2799,11 +2814,11 @@ QByteArray QSslSocketPrivate::peek(qint64 maxSize)
QByteArray ret;
ret.reserve(maxSize);
ret.resize(buffer.peek(ret.data(), maxSize, transactionPos));
- if (ret.length() == maxSize)
+ if (ret.size() == maxSize)
return ret;
//peek at data in the plain socket
if (plainSocket)
- return ret + plainSocket->peek(maxSize - ret.length());
+ return ret + plainSocket->peek(maxSize - ret.size());
return QByteArray();
} else {
@@ -2955,7 +2970,13 @@ QList<QByteArray> QSslSocketPrivate::unixRootCertDirectories()
ba("/opt/openssl/certs/"), // HP-UX
ba("/etc/ssl/"), // OpenBSD
};
- return QList<QByteArray>::fromReadOnlyData(dirs);
+ QList<QByteArray> result = QList<QByteArray>::fromReadOnlyData(dirs);
+ if constexpr (isVxworks) {
+ static QByteArray vxworksCertsDir = qgetenv("VXWORKS_CERTS_DIR");
+ if (!vxworksCertsDir.isEmpty())
+ result.push_back(vxworksCertsDir);
+ }
+ return result;
}
/*!
@@ -3031,7 +3052,7 @@ bool QSslSocketPrivate::isMatchingHostname(const QString &cn, const QString &hos
qsizetype secondCnDot = cn.indexOf(u'.', firstCnDot+1);
// Check at least 3 components
- if ((-1 == secondCnDot) || (secondCnDot+1 >= cn.length()))
+ if ((-1 == secondCnDot) || (secondCnDot+1 >= cn.size()))
return false;
// Check * is last character of 1st component (ie. there's a following .)
@@ -3086,10 +3107,11 @@ QTlsBackend *QSslSocketPrivate::tlsBackendInUse()
tlsBackend = QTlsBackend::findBackend(activeBackendName);
if (tlsBackend) {
- QObject::connect(tlsBackend, &QObject::destroyed, [] {
+ QObject::connect(tlsBackend, &QObject::destroyed, tlsBackend, [] {
const QMutexLocker locker(&backendMutex);
tlsBackend = nullptr;
- });
+ },
+ Qt::DirectConnection);
}
return tlsBackend;
}
diff --git a/src/network/ssl/qsslsocket.h b/src/network/ssl/qsslsocket.h
index 3174a68953..3ed1bc45cc 100644
--- a/src/network/ssl/qsslsocket.h
+++ b/src/network/ssl/qsslsocket.h
@@ -35,6 +35,7 @@ public:
SslClientMode,
SslServerMode
};
+ Q_ENUM(SslMode)
enum PeerVerifyMode {
VerifyNone,
@@ -42,6 +43,7 @@ public:
VerifyPeer,
AutoVerifyPeer
};
+ Q_ENUM(PeerVerifyMode)
explicit QSslSocket(QObject *parent = nullptr);
~QSslSocket();
diff --git a/src/network/ssl/qtlsbackend.cpp b/src/network/ssl/qtlsbackend.cpp
index bc5b492d0f..761ab33fbe 100644
--- a/src/network/ssl/qtlsbackend.cpp
+++ b/src/network/ssl/qtlsbackend.cpp
@@ -27,7 +27,7 @@ QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
-Q_APPLICATION_STATIC(QFactoryLoader, loader, QTlsBackend_iid,
+Q_APPLICATION_STATIC(QFactoryLoader, qtlsbLoader, QTlsBackend_iid,
QStringLiteral("/tls"))
namespace {
@@ -54,7 +54,7 @@ public:
bool tryPopulateCollection()
{
- if (!loader())
+ if (!qtlsbLoader())
return false;
Q_CONSTINIT static QBasicMutex mutex;
@@ -63,10 +63,10 @@ public:
return true;
#if QT_CONFIG(library)
- loader->update();
+ qtlsbLoader->update();
#endif
int index = 0;
- while (loader->instance(index))
+ while (qtlsbLoader->instance(index))
++index;
return true;
@@ -889,20 +889,28 @@ QSslCipher QTlsBackend::createCiphersuite(const QString &suiteName, QSsl::SslPro
/*!
\internal
- Auxiliary function. Creates a new QSslCipher from \a name (which is an implementation-specific
- string), \a protocol and \a protocolString, e.g.:
+ Auxiliary function. Creates a new QSslCipher from \a name, \a keyExchangeMethod, \a encryptionMethod,
+ \a authenticationMethod, \a bits, \a protocol version and \a protocolString.
+ For example:
\code
- createCipher(QStringLiteral("schannel"), QSsl::TlsV1_2, "TLSv1.2"_L1);
+ createCiphersuite("ECDHE-RSA-AES256-GCM-SHA256"_L1, "ECDH"_L1, "AES"_L1, "RSA"_L1, 256,
+ QSsl::TlsV1_2, "TLSv1.2"_L1);
\endcode
*/
-QSslCipher QTlsBackend::createCipher(const QString &name, QSsl::SslProtocol protocol,
- const QString &protocolString)
+QSslCipher QTlsBackend::createCiphersuite(const QString &name, const QString &keyExchangeMethod,
+ const QString &encryptionMethod,
+ const QString &authenticationMethod,
+ int bits, QSsl::SslProtocol protocol,
+ const QString &protocolString)
{
- // Note the name 'createCipher' (not 'ciphersuite'): we don't provide
- // information about Kx, Au, bits/supported etc.
QSslCipher cipher;
cipher.d->isNull = false;
cipher.d->name = name;
+ cipher.d->bits = bits;
+ cipher.d->supportedBits = bits;
+ cipher.d->keyExchangeMethod = keyExchangeMethod;
+ cipher.d->encryptionMethod = encryptionMethod;
+ cipher.d->authenticationMethod = authenticationMethod;
cipher.d->protocol = protocol;
cipher.d->protocolString = protocolString;
return cipher;
@@ -1382,8 +1390,7 @@ QByteArray TlsKey::pemHeader() const
else if (algorithm() == QSsl::Dh)
return QByteArrayLiteral("-----BEGIN PRIVATE KEY-----");
- Q_UNREACHABLE();
- return {};
+ Q_UNREACHABLE_RETURN({});
}
/*!
@@ -1404,8 +1411,7 @@ QByteArray TlsKey::pemFooter() const
else if (algorithm() == QSsl::Dh)
return QByteArrayLiteral("-----END PRIVATE KEY-----");
- Q_UNREACHABLE();
- return {};
+ Q_UNREACHABLE_RETURN({});
}
/*!
diff --git a/src/network/ssl/qtlsbackend_p.h b/src/network/ssl/qtlsbackend_p.h
index c69e641854..090531014b 100644
--- a/src/network/ssl/qtlsbackend_p.h
+++ b/src/network/ssl/qtlsbackend_p.h
@@ -31,7 +31,6 @@
#include <QtNetwork/qssl.h>
#include <QtCore/qloggingcategory.h>
-#include <QtCore/qsharedpointer.h>
#include <QtCore/qnamespace.h>
#include <QtCore/qobject.h>
#include <QtCore/qglobal.h>
@@ -58,7 +57,7 @@ class QSslKey;
namespace QTlsPrivate {
-class Q_NETWORK_PRIVATE_EXPORT TlsKey {
+class Q_NETWORK_EXPORT TlsKey {
public:
virtual ~TlsKey();
@@ -95,7 +94,7 @@ public:
QByteArray pemFooter() const;
};
-class Q_NETWORK_PRIVATE_EXPORT X509Certificate
+class Q_NETWORK_EXPORT X509Certificate
{
public:
virtual ~X509Certificate();
@@ -152,7 +151,7 @@ using X509Pkcs12ReaderPtr = bool (*)(QIODevice *device, QSslKey *key, QSslCertif
#if QT_CONFIG(ssl)
// TLS over TCP. Handshake, encryption/decryption.
-class Q_NETWORK_PRIVATE_EXPORT TlsCryptograph : public QObject
+class Q_NETWORK_EXPORT TlsCryptograph : public QObject
{
public:
virtual ~TlsCryptograph();
@@ -188,7 +187,7 @@ class TlsCryptograph;
#if QT_CONFIG(dtls)
-class Q_NETWORK_PRIVATE_EXPORT DtlsBase
+class Q_NETWORK_EXPORT DtlsBase
{
public:
virtual ~DtlsBase();
@@ -218,7 +217,7 @@ public:
};
// TLS over UDP. Handshake, encryption/decryption.
-class Q_NETWORK_PRIVATE_EXPORT DtlsCryptograph : virtual public DtlsBase
+class Q_NETWORK_EXPORT DtlsCryptograph : virtual public DtlsBase
{
public:
@@ -347,8 +346,11 @@ public:
static QSslCipher createCiphersuite(const QString &description, int bits, int supportedBits);
static QSslCipher createCiphersuite(const QString &suiteName, QSsl::SslProtocol protocol,
const QString &protocolString);
- static QSslCipher createCipher(const QString &name, QSsl::SslProtocol protocol,
- const QString &protocolString);
+ static QSslCipher createCiphersuite(const QString &name, const QString &keyExchangeMethod,
+ const QString &encryptionMethod,
+ const QString &authenticationMethod,
+ int bits, QSsl::SslProtocol protocol,
+ const QString &protocolString);
// Those statics are implemented using QSslSocketPrivate (which is not exported,
// unlike QTlsBackend).