summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--mkspecs/features/ctest_testcase_common.prf16
-rw-r--r--mkspecs/features/qt_common.prf5
-rw-r--r--mkspecs/features/qt_module.prf2
-rw-r--r--mkspecs/features/qt_module_headers.prf15
-rw-r--r--mkspecs/features/uikit/bitcode.prf2
-rw-r--r--mkspecs/features/uikit/default_post.prf2
-rw-r--r--mkspecs/features/uikit/default_pre.prf4
-rw-r--r--src/corelib/io/qfsfileengine.cpp4
-rw-r--r--src/corelib/kernel/qvariant.cpp34
-rw-r--r--src/corelib/thread/qthread.cpp2
-rw-r--r--src/corelib/thread/qthreadpool.cpp33
-rw-r--r--src/corelib/thread/qthreadpool.h4
-rw-r--r--src/corelib/thread/qthreadpool_p.h12
-rw-r--r--src/corelib/tools/qbytearray.cpp15
-rw-r--r--src/network/access/access.pri6
-rw-r--r--src/network/access/qhsts.cpp61
-rw-r--r--src/network/access/qhsts_p.h7
-rw-r--r--src/network/access/qhstsstore.cpp202
-rw-r--r--src/network/access/qhstsstore_p.h93
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp44
-rw-r--r--src/network/access/qnetworkaccessmanager.h3
-rw-r--r--src/network/access/qnetworkaccessmanager_p.h2
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.cpp7
-rw-r--r--src/plugins/platforms/windows/qwindowswindow.h10
-rw-r--r--tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp30
-rw-r--r--tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp34
-rw-r--r--tests/auto/network/access/hsts/tst_qhsts.cpp72
27 files changed, 637 insertions, 84 deletions
diff --git a/mkspecs/features/ctest_testcase_common.prf b/mkspecs/features/ctest_testcase_common.prf
index cdc5cca1d7..4c7e03f0f7 100644
--- a/mkspecs/features/ctest_testcase_common.prf
+++ b/mkspecs/features/ctest_testcase_common.prf
@@ -27,22 +27,8 @@ isEmpty(CTEST_VERSION) {
}
CMAKE_VERSION = $$last(CMAKE_VERSION)
-CMAKE_VERSION_MAJOR = $$section(CMAKE_VERSION, ., 0, 0)
-CMAKE_VERSION_MINOR = $$section(CMAKE_VERSION, ., 1, 1)
-CMAKE_VERSION_PATCH = $$section(CMAKE_VERSION, ., 2, 2)
-# CMake can report versions like 2.8.11-rc1, so strip off the rc part.
-CMAKE_VERSION_PATCH ~= s,-.*,,
-
-VERSION_OK =
-greaterThan(CMAKE_VERSION_MAJOR, 2) {
- VERSION_OK = 1
-} else:greaterThan(CMAKE_VERSION_MAJOR, 1):greaterThan(CMAKE_VERSION_MINOR, 8) {
- VERSION_OK = 1
-} else:greaterThan(CMAKE_VERSION_MAJOR, 1):greaterThan(CMAKE_VERSION_MINOR, 7):greaterThan(CMAKE_VERSION_PATCH, 2) {
- VERSION_OK = 1
-}
-isEmpty(VERSION_OK) {
+!versionAtLeast(CMAKE_VERSION, 2.8.3) {
message("cmake $$CMAKE_VERSION is too old for this test.")
return()
}
diff --git a/mkspecs/features/qt_common.prf b/mkspecs/features/qt_common.prf
index e66c24b027..65574d2635 100644
--- a/mkspecs/features/qt_common.prf
+++ b/mkspecs/features/qt_common.prf
@@ -55,9 +55,8 @@ host_build:cross_compile: return()
# -Wdate-time: warn if we use __DATE__ or __TIME__ (we want to be able to reproduce the exact same binary)
# -Wvla: use of variable-length arrays (an extension to C++)
clang {
- # Clang 3.5 introduced -Wdate-time
- # The conditional assumes we aren't compiling against Clang 2.x anymore
- greaterThan(QT_CLANG_MAJOR_VERSION, 3)|greaterThan(QT_CLANG_MINOR_VERSION, 4): \
+ clang_ver = $${QT_CLANG_MAJOR_VERSION}.$${QT_CLANG_MINOR_VERSION}
+ versionAtLeast(clang_ver, 3.5): \
QMAKE_CXXFLAGS_WARN_ON += -Wdate-time
} else: gcc:!intel_icc {
QMAKE_CXXFLAGS_WARN_ON += -Wvla
diff --git a/mkspecs/features/qt_module.prf b/mkspecs/features/qt_module.prf
index 36f632e8ca..8a8c17f01b 100644
--- a/mkspecs/features/qt_module.prf
+++ b/mkspecs/features/qt_module.prf
@@ -195,7 +195,7 @@ sse2:!contains(QT_CPU_FEATURES.$$QT_ARCH, sse2):!host_build:!if(static:qtConfig(
clang {
apple_clang_ver = $${QT_APPLE_CLANG_MAJOR_VERSION}.$${QT_APPLE_CLANG_MINOR_VERSION}
reg_clang_ver = $${QT_CLANG_MAJOR_VERSION}.$${QT_CLANG_MINOR_VERSION}
- !lessThan(apple_clang_ver, "5.1")|!lessThan(reg_clang_ver, "3.4"): \
+ versionAtLeast(apple_clang_ver, 5.1)|versionAtLeast(reg_clang_ver, 3.4): \
CONFIG += compiler_supports_fpmath
} else: gcc {
CONFIG += compiler_supports_fpmath
diff --git a/mkspecs/features/qt_module_headers.prf b/mkspecs/features/qt_module_headers.prf
index e5b99e1406..6cc72bb5e3 100644
--- a/mkspecs/features/qt_module_headers.prf
+++ b/mkspecs/features/qt_module_headers.prf
@@ -214,20 +214,15 @@ headersclean:!internal_module {
!contains(QT_ARCH, arm):!contains(QT_ARCH, mips): \
hcleanFLAGS += -Wcast-align
- greaterThan(QT_CLANG_MAJOR_VERSION, 3) {
- hcleanFLAGS += -Wdouble-promotion
- } greaterThan(QT_CLANG_MAJOR_VERSION, 2):greaterThan(QT_CLANG_MINOR_VERSION, 7) {
- hcleanFLAGS += -Wdouble-promotion
- }
+ clang_ver = $${QT_CLANG_MAJOR_VERSION}.$${QT_CLANG_MINOR_VERSION}
+ versionAtLeast(clang_ver, 3.8): hcleanFLAGS += -Wdouble-promotion
!clang {
# options accepted only by GCC
- greaterThan(QT_GCC_MAJOR_VERSION, 4) {
- hcleanFLAGS += -Wdouble-promotion
- } greaterThan(QT_GCC_MAJOR_VERSION, 3):greaterThan(QT_GCC_MINOR_VERSION, 4) {
- hcleanFLAGS += -Wdouble-promotion
- }
+ gcc_ver = $${QT_GCC_MAJOR_VERSION}.$${QT_GCC_MINOR_VERSION}
+ versionAtLeast(gcc_ver, 4.5): hcleanFLAGS += -Wdouble-promotion
+
c++11 {
# only enabled for actual c++11 builds due to
# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=52806
diff --git a/mkspecs/features/uikit/bitcode.prf b/mkspecs/features/uikit/bitcode.prf
index df298171c0..f1fd33981a 100644
--- a/mkspecs/features/uikit/bitcode.prf
+++ b/mkspecs/features/uikit/bitcode.prf
@@ -1,4 +1,4 @@
-lessThan(QMAKE_XCODE_VERSION, "7.0") {
+!versionAtLeast(QMAKE_XCODE_VERSION, 7.0) {
warning("You need to update Xcode to version 7 or newer to support bitcode")
} else: !macx-xcode {
# Simulator builds and all debug builds SHOULD use -fembed-bitcode-marker,
diff --git a/mkspecs/features/uikit/default_post.prf b/mkspecs/features/uikit/default_post.prf
index f7245e48b1..c1b6f38a6c 100644
--- a/mkspecs/features/uikit/default_post.prf
+++ b/mkspecs/features/uikit/default_post.prf
@@ -39,7 +39,7 @@ macx-xcode {
qmake_launch_images.files = $$qmake_copy_image.output
QMAKE_BUNDLE_DATA += qmake_launch_images
- lessThan(QMAKE_XCODE_VERSION, "6.0") {
+ !versionAtLeast(QMAKE_XCODE_VERSION, 6.0) {
warning("You need to update Xcode to version 6 or newer to fully support iPhone6/6+")
} else {
# Set up default LaunchScreen to support iPhone6/6+
diff --git a/mkspecs/features/uikit/default_pre.prf b/mkspecs/features/uikit/default_pre.prf
index 00e29a5c8b..6a44a67bca 100644
--- a/mkspecs/features/uikit/default_pre.prf
+++ b/mkspecs/features/uikit/default_pre.prf
@@ -21,8 +21,8 @@ unset(sim_and_dev)
load(default_pre)
# Check for supported Xcode versions
-lessThan(QMAKE_XCODE_VERSION, "4.3"): \
+!versionAtLeast(QMAKE_XCODE_VERSION, 4.3): \
error("This mkspec requires Xcode 4.3 or later")
-ios:shared:lessThan(QMAKE_IOS_DEPLOYMENT_TARGET, "8.0"): \
+ios:shared:!versionAtLeast(QMAKE_IOS_DEPLOYMENT_TARGET, 8.0): \
QMAKE_IOS_DEPLOYMENT_TARGET = 8.0
diff --git a/src/corelib/io/qfsfileengine.cpp b/src/corelib/io/qfsfileengine.cpp
index 037deb8942..b7a5440224 100644
--- a/src/corelib/io/qfsfileengine.cpp
+++ b/src/corelib/io/qfsfileengine.cpp
@@ -923,10 +923,6 @@ bool QFSFileEngine::supportsExtension(Extension extension) const
\reimp
*/
-/*! \fn QDateTime QFSFileEngine::fileTime(FileTime time) const
- \reimp
-*/
-
/*! \fn QString QFSFileEngine::homePath()
Returns the home path of the current user.
diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp
index 29af0647ea..b73c83bc49 100644
--- a/src/corelib/kernel/qvariant.cpp
+++ b/src/corelib/kernel/qvariant.cpp
@@ -1459,7 +1459,11 @@ Q_CORE_EXPORT void QVariantPrivate::registerHandler(const int /* Modules::Names
/*!
\fn QVariant::QVariant(Type type)
- Constructs a null variant of type \a type.
+ Constructs an uninitialized variant of type \a type. This will create a
+ variant in a special null state that if accessed will return a default
+ constructed value of the \a type.
+
+ \sa isNull()
*/
@@ -3305,17 +3309,20 @@ bool QVariant::canConvert(int targetTypeId) const
/*!
Casts the variant to the requested type, \a targetTypeId. If the cast cannot be
- done, the variant is cleared. Returns \c true if the current type of
- the variant was successfully cast; otherwise returns \c false.
+ done, the variant is still changed to the requested type, but is left in a cleared
+ null state similar to that constructed by QVariant(Type).
+
+ Returns \c true if the current type of the variant was successfully cast;
+ otherwise returns \c false.
A QVariant containing a pointer to a type derived from QObject will also convert
and return true for this function if a qobject_cast to the type described
by \a targetTypeId would succeed. Note that this only works for QObject subclasses
which use the Q_OBJECT macro.
- \warning For historical reasons, converting a null QVariant results
- in a null value of the desired type (e.g., an empty string for
- QString) and a result of false.
+ \note converting QVariants that are null due to not being initialized or having
+ failed a previous conversion will always fail, changing the type, remaining null,
+ and returning \c false.
\sa canConvert(), clear()
*/
@@ -3332,7 +3339,8 @@ bool QVariant::convert(int targetTypeId)
return false;
create(targetTypeId, 0);
- if (oldValue.isNull())
+ // Fail if the value is not initialized or was forced null by a previous failed convert.
+ if (oldValue.d.is_null)
return false;
if ((QMetaType::typeFlags(oldValue.userType()) & QMetaType::PointerToQObject) && (QMetaType::typeFlags(targetTypeId) & QMetaType::PointerToQObject)) {
@@ -3732,12 +3740,14 @@ void* QVariant::data()
/*!
Returns \c true if this is a null variant, false otherwise. A variant is
- considered null if it contains a default constructed value or a built-in
- type instance that has an isNull method, in which case the result
- would be the same as calling isNull on the wrapped object.
+ considered null if it contains no initialized value or it contains an instance
+ of built-in type that has an isNull method, in which case the result would be
+ the same as calling isNull on the wrapped object.
+
+ \warning Null variants is not a single state and two null variants may easily
+ return \c false on the == operator if they do not contain similar null values.
- \warning The result of the function doesn't affect == operator, which means
- that two values can be equal even if one of them is null and another is not.
+ \sa QVariant(Type), convert(int)
*/
bool QVariant::isNull() const
{
diff --git a/src/corelib/thread/qthread.cpp b/src/corelib/thread/qthread.cpp
index b662475003..1ec626a53b 100644
--- a/src/corelib/thread/qthread.cpp
+++ b/src/corelib/thread/qthread.cpp
@@ -291,7 +291,7 @@ QThreadPrivate::~QThreadPrivate()
\fn int QThread::idealThreadCount()
Returns the ideal number of threads that can be run on the system. This is done querying
- the number of processor cores, both real and logical, in the system. This function returns -1
+ the number of processor cores, both real and logical, in the system. This function returns 1
if the number of processor cores could not be detected.
*/
diff --git a/src/corelib/thread/qthreadpool.cpp b/src/corelib/thread/qthreadpool.cpp
index f3ce1f258f..9937201619 100644
--- a/src/corelib/thread/qthreadpool.cpp
+++ b/src/corelib/thread/qthreadpool.cpp
@@ -74,7 +74,9 @@ public:
*/
QThreadPoolThread::QThreadPoolThread(QThreadPoolPrivate *manager)
:manager(manager), runnable(0)
-{ }
+{
+ setStackSize(manager->stackSize);
+}
/*
\internal
@@ -154,11 +156,6 @@ void QThreadPoolThread::registerThreadInactive()
\internal
*/
QThreadPoolPrivate:: QThreadPoolPrivate()
- : isExiting(false),
- expiryTimeout(30000),
- maxThreadCount(qAbs(QThread::idealThreadCount())),
- reservedThreads(0),
- activeThreads(0)
{ }
bool QThreadPoolPrivate::tryStart(QRunnable *task)
@@ -609,6 +606,30 @@ void QThreadPool::reserveThread()
++d->reservedThreads;
}
+/*! \property QThreadPool::stacksize
+
+ This property contains the stack size for the thread pool worker
+ threads.
+
+ The value of the property is uses when the thread pool creates
+ new threads only. Changing it has no effect for already created
+ or running threads.
+
+ The default value is 0, which makes QThread use the operating
+ system default stack stize.
+*/
+void QThreadPool::setStackSize(uint stackSize)
+{
+ Q_D(QThreadPool);
+ d->stackSize = stackSize;
+}
+
+uint QThreadPool::stackSize() const
+{
+ Q_D(const QThreadPool);
+ return d->stackSize;
+}
+
/*!
Releases a thread previously reserved by a call to reserveThread().
diff --git a/src/corelib/thread/qthreadpool.h b/src/corelib/thread/qthreadpool.h
index 09b7f96f48..a65eacc996 100644
--- a/src/corelib/thread/qthreadpool.h
+++ b/src/corelib/thread/qthreadpool.h
@@ -58,6 +58,7 @@ class Q_CORE_EXPORT QThreadPool : public QObject
Q_PROPERTY(int expiryTimeout READ expiryTimeout WRITE setExpiryTimeout)
Q_PROPERTY(int maxThreadCount READ maxThreadCount WRITE setMaxThreadCount)
Q_PROPERTY(int activeThreadCount READ activeThreadCount)
+ Q_PROPERTY(uint stackSize READ stackSize WRITE setStackSize)
friend class QFutureInterfaceBase;
public:
@@ -77,6 +78,9 @@ public:
int activeThreadCount() const;
+ void setStackSize(uint stackSize);
+ uint stackSize() const;
+
void reserveThread();
void releaseThread();
diff --git a/src/corelib/thread/qthreadpool_p.h b/src/corelib/thread/qthreadpool_p.h
index 4a9f9e5cfa..8b6a8cc476 100644
--- a/src/corelib/thread/qthreadpool_p.h
+++ b/src/corelib/thread/qthreadpool_p.h
@@ -53,6 +53,7 @@
//
#include "QtCore/qmutex.h"
+#include "QtCore/qthread.h"
#include "QtCore/qwaitcondition.h"
#include "QtCore/qset.h"
#include "QtCore/qqueue.h"
@@ -91,11 +92,12 @@ public:
QVector<QPair<QRunnable *, int> > queue;
QWaitCondition noActiveThreads;
- bool isExiting;
- int expiryTimeout;
- int maxThreadCount;
- int reservedThreads;
- int activeThreads;
+ int expiryTimeout = 30000;
+ int maxThreadCount = QThread::idealThreadCount();
+ int reservedThreads = 0;
+ int activeThreads = 0;
+ uint stackSize = 0;
+ bool isExiting = false;
};
QT_END_NAMESPACE
diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp
index 3ecb2ac662..9b648adb06 100644
--- a/src/corelib/tools/qbytearray.cpp
+++ b/src/corelib/tools/qbytearray.cpp
@@ -686,14 +686,6 @@ QByteArray qCompress(const uchar* data, int nbytes, int compressionLevel)
\sa qCompress()
*/
-/*! \relates QByteArray
-
- \overload
-
- Uncompresses the first \a nbytes of \a data and returns a new byte
- array with the uncompressed data.
-*/
-
#ifndef QT_NO_COMPRESS
namespace {
struct QByteArrayDataDeleter
@@ -709,6 +701,13 @@ static QByteArray invalidCompressedData()
return QByteArray();
}
+/*! \relates QByteArray
+
+ \overload
+
+ Uncompresses the first \a nbytes of \a data and returns a new byte
+ array with the uncompressed data.
+*/
QByteArray qUncompress(const uchar* data, int nbytes)
{
if (!data) {
diff --git a/src/network/access/access.pri b/src/network/access/access.pri
index 13d52ea44a..4281a870a7 100644
--- a/src/network/access/access.pri
+++ b/src/network/access/access.pri
@@ -41,7 +41,8 @@ HEADERS += \
access/qnetworkfile_p.h \
access/qhttp2protocolhandler_p.h \
access/qhsts_p.h \
- access/qhstspolicy.h
+ access/qhstspolicy.h \
+ access/qhstsstore_p.h
SOURCES += \
access/qftp.cpp \
@@ -76,7 +77,8 @@ SOURCES += \
access/qnetworkfile.cpp \
access/qhttp2protocolhandler.cpp \
access/qhsts.cpp \
- access/qhstspolicy.cpp
+ access/qhstspolicy.cpp \
+ access/qhstsstore.cpp
mac: LIBS_PRIVATE += -framework Security
diff --git a/src/network/access/qhsts.cpp b/src/network/access/qhsts.cpp
index ca9f3b977b..6a731afc2f 100644
--- a/src/network/access/qhsts.cpp
+++ b/src/network/access/qhsts.cpp
@@ -37,6 +37,7 @@
**
****************************************************************************/
+#include "qhstsstore_p.h"
#include "qhsts_p.h"
#include "QtCore/private/qipaddress_p.h"
@@ -80,14 +81,24 @@ void QHstsCache::updateFromHeaders(const QList<QPair<QByteArray, QByteArray>> &h
return;
QHstsHeaderParser parser;
- if (parser.parse(headers))
+ if (parser.parse(headers)) {
updateKnownHost(url.host(), parser.expirationDate(), parser.includeSubDomains());
+ if (hstsStore)
+ hstsStore->synchronize();
+ }
}
void QHstsCache::updateFromPolicies(const QVector<QHstsPolicy> &policies)
{
for (const auto &policy : policies)
updateKnownHost(policy.host(), policy.expiry(), policy.includesSubDomains());
+
+ if (hstsStore && policies.size()) {
+ // These policies are coming either from store or from QNAM's setter
+ // function. As a result we can notice expired or new policies, time
+ // to sync ...
+ hstsStore->synchronize();
+ }
}
void QHstsCache::updateKnownHost(const QUrl &url, const QDateTime &expires,
@@ -97,6 +108,8 @@ void QHstsCache::updateKnownHost(const QUrl &url, const QDateTime &expires,
return;
updateKnownHost(url.host(), expires, includeSubDomains);
+ if (hstsStore)
+ hstsStore->synchronize();
}
void QHstsCache::updateKnownHost(const QString &host, const QDateTime &expires,
@@ -124,13 +137,20 @@ void QHstsCache::updateKnownHost(const QString &host, const QDateTime &expires,
}
knownHosts.insert(pos, hostName, newPolicy);
+ if (hstsStore)
+ hstsStore->addToObserved(newPolicy);
return;
}
if (newPolicy.isExpired())
knownHosts.erase(pos);
- else
+ else if (*pos != newPolicy)
*pos = std::move(newPolicy);
+ else
+ return;
+
+ if (hstsStore)
+ hstsStore->addToObserved(newPolicy);
}
bool QHstsCache::isKnownHost(const QUrl &url) const
@@ -165,10 +185,15 @@ bool QHstsCache::isKnownHost(const QUrl &url) const
while (nameToTest.fragment.size()) {
auto const pos = knownHosts.find(nameToTest);
if (pos != knownHosts.end()) {
- if (pos.value().isExpired())
+ if (pos.value().isExpired()) {
knownHosts.erase(pos);
- else if (!superDomainMatch || pos.value().includesSubDomains())
+ if (hstsStore) {
+ // Inform our store that this policy has expired.
+ hstsStore->addToObserved(pos.value());
+ }
+ } else if (!superDomainMatch || pos.value().includesSubDomains()) {
return true;
+ }
}
const int dot = nameToTest.fragment.indexOf(QLatin1Char('.'));
@@ -196,6 +221,34 @@ QVector<QHstsPolicy> QHstsCache::policies() const
return values;
}
+void QHstsCache::setStore(QHstsStore *store)
+{
+ // Caller retains ownership of store, which must outlive this cache.
+ if (store != hstsStore) {
+ hstsStore = store;
+
+ if (!hstsStore)
+ return;
+
+ // First we augment our store with the policies we already know about
+ // (and thus the cached policy takes priority over whatever policy we
+ // had in the store for the same host, if any).
+ if (knownHosts.size()) {
+ const QVector<QHstsPolicy> observed(policies());
+ for (const auto &policy : observed)
+ hstsStore->addToObserved(policy);
+ hstsStore->synchronize();
+ }
+
+ // Now we update the cache with anything we have not observed yet, but
+ // the store knows about (well, it can happen we synchronize again as a
+ // result if some policies managed to expire or if we add a new one
+ // from the store to cache):
+ const QVector<QHstsPolicy> restored(store->readPolicies());
+ updateFromPolicies(restored);
+ }
+}
+
// The parser is quite simple: 'nextToken' knowns exactly what kind of tokens
// are valid and it will return false if something else was found; then
// we immediately stop parsing. 'parseDirective' knows how these tokens can
diff --git a/src/network/access/qhsts_p.h b/src/network/access/qhsts_p.h
index ab3ca536fb..2feb73b446 100644
--- a/src/network/access/qhsts_p.h
+++ b/src/network/access/qhsts_p.h
@@ -51,6 +51,8 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
#include <QtNetwork/qhstspolicy.h>
#include <QtCore/qbytearray.h>
@@ -66,6 +68,8 @@ QT_BEGIN_NAMESPACE
template<typename T> class QList;
template <typename T> class QVector;
+class QHstsStore;
+
class Q_AUTOTEST_EXPORT QHstsCache
{
public:
@@ -80,6 +84,8 @@ public:
QVector<QHstsPolicy> policies() const;
+ void setStore(QHstsStore *store);
+
private:
void updateKnownHost(const QString &hostName, const QDateTime &expires,
@@ -112,6 +118,7 @@ private:
};
mutable QMap<HostName, QHstsPolicy> knownHosts;
+ QHstsStore *hstsStore = nullptr;
};
class Q_AUTOTEST_EXPORT QHstsHeaderParser
diff --git a/src/network/access/qhstsstore.cpp b/src/network/access/qhstsstore.cpp
new file mode 100644
index 0000000000..239a52b7a4
--- /dev/null
+++ b/src/network/access/qhstsstore.cpp
@@ -0,0 +1,202 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qhstsstore_p.h"
+#include "qhstspolicy.h"
+
+#include "qstandardpaths.h"
+#include "qdatastream.h"
+#include "qbytearray.h"
+#include "qdatetime.h"
+#include "qvariant.h"
+#include "qstring.h"
+#include "qdir.h"
+
+#include <utility>
+
+QT_BEGIN_NAMESPACE
+
+static QString host_name_to_settings_key(const QString &hostName)
+{
+ const QByteArray hostNameAsHex(hostName.toUtf8().toHex());
+ return QString::fromLatin1(hostNameAsHex);
+}
+
+static QString settings_key_to_host_name(const QString &key)
+{
+ const QByteArray hostNameAsUtf8(QByteArray::fromHex(key.toLatin1()));
+ return QString::fromUtf8(hostNameAsUtf8);
+}
+
+QHstsStore::QHstsStore(const QString &dirName)
+ : store(absoluteFilePath(dirName), QSettings::IniFormat)
+{
+ // Disable fallbacks, we do not want to use anything but our own ini file.
+ store.setFallbacksEnabled(false);
+}
+
+QHstsStore::~QHstsStore()
+{
+ synchronize();
+}
+
+QVector<QHstsPolicy> QHstsStore::readPolicies()
+{
+ // This function only attempts to read policies, making no decision about
+ // expired policies. It's up to a user (QHstsCache) to mark these policies
+ // for deletion and sync the store later. But we immediately remove keys/values
+ // (if the store isWritable) for the policies that we fail to read.
+ QVector<QHstsPolicy> policies;
+
+ beginHstsGroups();
+
+ const QStringList keys = store.childKeys();
+ for (const auto &key : keys) {
+ QHstsPolicy restoredPolicy;
+ if (deserializePolicy(key, restoredPolicy)) {
+ restoredPolicy.setHost(settings_key_to_host_name(key));
+ policies.push_back(std::move(restoredPolicy));
+ } else if (isWritable()) {
+ evictPolicy(key);
+ }
+ }
+
+ endHstsGroups();
+
+ return policies;
+}
+
+void QHstsStore::addToObserved(const QHstsPolicy &policy)
+{
+ observedPolicies.push_back(policy);
+}
+
+void QHstsStore::synchronize()
+{
+ if (!isWritable())
+ return;
+
+ if (observedPolicies.size()) {
+ beginHstsGroups();
+ for (const QHstsPolicy &policy : 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))
+ evictPolicy(key);
+ }
+ observedPolicies.clear();
+ endHstsGroups();
+ }
+
+ store.sync();
+}
+
+bool QHstsStore::isWritable() const
+{
+ return store.isWritable();
+}
+
+QString QHstsStore::absoluteFilePath(const QString &dirName)
+{
+ const QDir dir(dirName.isEmpty() ? QStandardPaths::writableLocation(QStandardPaths::CacheLocation)
+ : dirName);
+ return dir.absoluteFilePath(QLatin1String("hstsstore"));
+}
+
+void QHstsStore::beginHstsGroups()
+{
+ store.beginGroup(QLatin1String("StrictTransportSecurity"));
+ store.beginGroup(QLatin1String("Policies"));
+}
+
+void QHstsStore::endHstsGroups()
+{
+ store.endGroup();
+ store.endGroup();
+}
+
+bool QHstsStore::deserializePolicy(const QString &key, QHstsPolicy &policy)
+{
+ Q_ASSERT(store.contains(key));
+
+ const QVariant data(store.value(key));
+ if (data.isNull() || !data.canConvert<QByteArray>())
+ return false;
+
+ const QByteArray serializedData(data.toByteArray());
+ QDataStream streamer(serializedData);
+ qint64 expiryInMS = 0;
+ streamer >> expiryInMS;
+ if (streamer.status() != QDataStream::Ok)
+ return false;
+ bool includesSubDomains = false;
+ streamer >> includesSubDomains;
+ if (streamer.status() != QDataStream::Ok)
+ return false;
+
+ policy.setExpiry(QDateTime::fromMSecsSinceEpoch(expiryInMS));
+ policy.setIncludesSubDomains(includesSubDomains);
+
+ return true;
+}
+
+bool QHstsStore::serializePolicy(const QString &key, const QHstsPolicy &policy)
+{
+ Q_ASSERT(store.isWritable());
+
+ QByteArray serializedData;
+ QDataStream streamer(&serializedData, QIODevice::WriteOnly);
+ streamer << policy.expiry().toMSecsSinceEpoch();
+ streamer << policy.includesSubDomains();
+
+ if (streamer.status() != QDataStream::Ok)
+ return false;
+
+ store.setValue(key, serializedData);
+ return true;
+}
+
+void QHstsStore::evictPolicy(const QString &key)
+{
+ Q_ASSERT(store.isWritable());
+ if (store.contains(key))
+ store.remove(key);
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qhstsstore_p.h b/src/network/access/qhstsstore_p.h
new file mode 100644
index 0000000000..13042839c4
--- /dev/null
+++ b/src/network/access/qhstsstore_p.h
@@ -0,0 +1,93 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHSTSSTORE_P_H
+#define QHSTSSTORE_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 <QtNetwork/private/qtnetworkglobal_p.h>
+
+#include <QtCore/qsettings.h>
+#include <QtCore/qvector.h>
+
+QT_BEGIN_NAMESPACE
+
+class QHstsPolicy;
+class QByteArray;
+class QString;
+
+class Q_AUTOTEST_EXPORT QHstsStore
+{
+public:
+ explicit QHstsStore(const QString &dirName);
+ ~QHstsStore();
+
+ QVector<QHstsPolicy> readPolicies();
+ void addToObserved(const QHstsPolicy &policy);
+ void synchronize();
+
+ bool isWritable() const;
+
+ static QString absoluteFilePath(const QString &dirName);
+private:
+ void beginHstsGroups();
+ bool serializePolicy(const QString &key, const QHstsPolicy &policy);
+ bool deserializePolicy(const QString &key, QHstsPolicy &policy);
+ void evictPolicy(const QString &key);
+ void endHstsGroups();
+
+ QVector<QHstsPolicy> observedPolicies;
+ QSettings store;
+
+ Q_DISABLE_COPY(QHstsStore)
+};
+
+QT_END_NAMESPACE
+
+#endif // QHSTSSTORE_P_H
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index 79f0aa8038..eeee82a87c 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -730,6 +730,48 @@ bool QNetworkAccessManager::isStrictTransportSecurityEnabled() const
}
/*!
+ \since 5.10
+
+ If \a enabled is \c true, the internal HSTS cache will use a persistent store
+ to read and write HSTS policies. \a storeDir defines where this store will be
+ located. The default location is defined by QStandardPaths::CacheLocation.
+ If there is no writable QStandartPaths::CacheLocation and \a storeDir is an
+ empty string, the store will be located in the program's working directory.
+
+ \note If HSTS cache already contains HSTS policies by the time persistent
+ store is enabled, these policies will be preserved in the store. In case both
+ cache and store contain the same known hosts, policies from cache are considered
+ to be more up-to-date (and thus will overwrite the previous values in the store).
+ If this behavior is undesired, enable HSTS store before enabling Strict Tranport
+ Security. By default, the persistent store of HSTS policies is disabled.
+
+ \sa isStrictTransportSecurityStoreEnabled(), setStrictTransportSecurityEnabled(),
+ QStandardPaths::standardLocations()
+*/
+
+void QNetworkAccessManager::enableStrictTransportSecurityStore(bool enabled, const QString &storeDir)
+{
+ Q_D(QNetworkAccessManager);
+ d->stsStore.reset(enabled ? new QHstsStore(storeDir) : nullptr);
+ d->stsCache.setStore(d->stsStore.data());
+}
+
+/*!
+ \since 5.10
+
+ Returns true if HSTS cache uses a permanent store to load and store HSTS
+ policies.
+
+ \sa enableStrictTransportSecurityStore()
+*/
+
+bool QNetworkAccessManager::isStrictTransportSecurityStoreEnabled() const
+{
+ Q_D(const QNetworkAccessManager);
+ return bool(d->stsStore.data());
+}
+
+/*!
\since 5.9
Adds HTTP Strict Transport Security policies contained in \a knownHosts into HSTS cache.
@@ -744,7 +786,7 @@ bool QNetworkAccessManager::isStrictTransportSecurityEnabled() const
policies, but this information can be overridden by "Strict-Transport-Security"
response headers.
- \sa addStrictTransportSecurityHosts(), QHstsPolicy
+ \sa addStrictTransportSecurityHosts(), enableStrictTransportSecurityStore(), QHstsPolicy
*/
void QNetworkAccessManager::addStrictTransportSecurityHosts(const QVector<QHstsPolicy> &knownHosts)
diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h
index f035ac5b00..4806ec0475 100644
--- a/src/network/access/qnetworkaccessmanager.h
+++ b/src/network/access/qnetworkaccessmanager.h
@@ -42,6 +42,7 @@
#include <QtNetwork/qtnetworkglobal.h>
#include <QtNetwork/qnetworkrequest.h>
+#include <QtCore/QString>
#include <QtCore/QVector>
#include <QtCore/QObject>
#ifndef QT_NO_SSL
@@ -124,6 +125,8 @@ public:
void setStrictTransportSecurityEnabled(bool enabled);
bool isStrictTransportSecurityEnabled() const;
+ void enableStrictTransportSecurityStore(bool enabled, const QString &storeDir = QString());
+ bool isStrictTransportSecurityStoreEnabled() const;
void addStrictTransportSecurityHosts(const QVector<QHstsPolicy> &knownHosts);
QVector<QHstsPolicy> strictTransportSecurityHosts() const;
diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h
index 13a26a54f1..e5257251a4 100644
--- a/src/network/access/qnetworkaccessmanager_p.h
+++ b/src/network/access/qnetworkaccessmanager_p.h
@@ -56,6 +56,7 @@
#include "qnetworkaccesscache_p.h"
#include "qnetworkaccessbackend_p.h"
#include "qnetworkrequest.h"
+#include "qhstsstore_p.h"
#include "qhsts_p.h"
#include "private/qobject_p.h"
#include "QtNetwork/qnetworkproxy.h"
@@ -211,6 +212,7 @@ public:
Q_AUTOTEST_EXPORT static void clearConnectionCache(QNetworkAccessManager *manager);
QHstsCache stsCache;
+ QScopedPointer<QHstsStore> stsStore;
bool stsEnabled = false;
#ifndef QT_NO_BEARERMANAGEMENT
diff --git a/src/plugins/platforms/windows/qwindowswindow.cpp b/src/plugins/platforms/windows/qwindowswindow.cpp
index 15906961ac..3e445f0f7f 100644
--- a/src/plugins/platforms/windows/qwindowswindow.cpp
+++ b/src/plugins/platforms/windows/qwindowswindow.cpp
@@ -640,7 +640,7 @@ QWindowsWindowData
// Capture events before CreateWindowEx() returns. The context is cleared in
// the QWindowsWindow constructor.
- const QWindowCreationContextPtr context(new QWindowCreationContext(w, rect, data.customMargins, style, exStyle));
+ const QWindowCreationContextPtr context(new QWindowCreationContext(w, data.geometry, rect, data.customMargins, style, exStyle));
QWindowsContext::instance()->setWindowCreationContext(context);
qCDebug(lcQpaWindows).nospace()
@@ -1004,10 +1004,11 @@ void QWindowsForeignWindow::setVisible(bool visible)
*/
QWindowCreationContext::QWindowCreationContext(const QWindow *w,
- const QRect &geometry,
+ const QRect &geometryIn, const QRect &geometry,
const QMargins &cm,
DWORD style_, DWORD exStyle_) :
geometryHint(w, cm), window(w), style(style_), exStyle(exStyle_),
+ requestedGeometryIn(geometryIn),
requestedGeometry(geometry), obtainedGeometry(geometry),
margins(QWindowsGeometryHint::frame(style, exStyle)), customMargins(cm)
{
@@ -1124,7 +1125,7 @@ void QWindowsWindow::initialize()
if (w->type() != Qt::Desktop) {
const Qt::WindowState state = w->windowState();
if (state != Qt::WindowMaximized && state != Qt::WindowFullScreen
- && creationContext->requestedGeometry != creationContext->obtainedGeometry) {
+ && creationContext->requestedGeometryIn != creationContext->obtainedGeometry) {
QWindowSystemInterface::handleGeometryChange(w, creationContext->obtainedGeometry);
}
QPlatformScreen *obtainedScreen = screenForGeometry(creationContext->obtainedGeometry);
diff --git a/src/plugins/platforms/windows/qwindowswindow.h b/src/plugins/platforms/windows/qwindowswindow.h
index 982f3dfd30..f0789e5167 100644
--- a/src/plugins/platforms/windows/qwindowswindow.h
+++ b/src/plugins/platforms/windows/qwindowswindow.h
@@ -81,9 +81,10 @@ struct QWindowsGeometryHint
struct QWindowCreationContext
{
- QWindowCreationContext(const QWindow *w, const QRect &r,
- const QMargins &customMargins,
- DWORD style, DWORD exStyle);
+ explicit QWindowCreationContext(const QWindow *w,
+ const QRect &geometryIn, const QRect &geometry,
+ const QMargins &customMargins,
+ DWORD style, DWORD exStyle);
void applyToMinMaxInfo(MINMAXINFO *mmi) const
{ geometryHint.applyToMinMaxInfo(style, exStyle, mmi); }
@@ -91,7 +92,8 @@ struct QWindowCreationContext
const QWindow *window;
DWORD style;
DWORD exStyle;
- QRect requestedGeometry;
+ QRect requestedGeometryIn; // QWindow scaled
+ QRect requestedGeometry; // after QPlatformWindow::initialGeometry()
QRect obtainedGeometry;
QMargins margins;
QMargins customMargins; // User-defined, additional frame for WM_NCCALCSIZE
diff --git a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
index bddc1cd2b2..906587248e 100644
--- a/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
+++ b/tests/auto/corelib/kernel/qvariant/tst_qvariant.cpp
@@ -277,6 +277,8 @@ private slots:
void compareSanity();
void compareRich();
+ void nullConvert();
+
void accessSequentialContainerKey();
private:
@@ -4861,6 +4863,33 @@ void tst_QVariant::compareRich()
<< QStringLiteral("d"));
}
+void tst_QVariant::nullConvert()
+{
+ // Test quirks with QVariants different types of null states.
+
+ // null variant with no initialized value
+ QVariant nullVar(QVariant::String);
+ QVERIFY(nullVar.isValid());
+ QVERIFY(nullVar.isNull());
+ // We can not convert a variant with no value
+ QVERIFY(!nullVar.convert(QVariant::Url));
+ QCOMPARE(nullVar.type(), QVariant::Url);
+ QVERIFY(nullVar.isNull());
+
+ // variant initialized with null value
+ QVariant nullStr = QVariant::fromValue(QString());
+ QVERIFY(nullStr.isValid());
+ QVERIFY(nullStr.isNull());
+ // We can convert an initialized null value however
+ QVERIFY(nullStr.convert(QVariant::Url));
+ QCOMPARE(nullStr.type(), QVariant::Url);
+ QVERIFY(nullStr.isValid());
+ // QUrl does not have an isNull method
+ QVERIFY(!nullStr.isNull());
+ // The URL is not valid however
+ QVERIFY(!nullStr.toUrl().isValid());
+}
+
void tst_QVariant::accessSequentialContainerKey()
{
QString nameResult;
@@ -4885,6 +4914,5 @@ void tst_QVariant::accessSequentialContainerKey()
QCOMPARE(nameResult, QStringLiteral("Seven"));
}
-
QTEST_MAIN(tst_QVariant)
#include "tst_qvariant.moc"
diff --git a/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp b/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp
index 1dcd642023..782eed03e8 100644
--- a/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp
+++ b/tests/auto/corelib/thread/qthreadpool/tst_qthreadpool.cpp
@@ -92,6 +92,7 @@ private slots:
void tryTake();
void waitForDoneTimeout();
void destroyingWaitsForTasksToFinish();
+ void stackSize();
void stressTest();
private:
@@ -1136,6 +1137,39 @@ void tst_QThreadPool::destroyingWaitsForTasksToFinish()
}
}
+// Verify that QThreadPool::stackSize is used when creating
+// new threads. Note that this tests the Qt property only
+// since QThread::stackSize() does not reflect the actual
+// stack size used by the native thread.
+void tst_QThreadPool::stackSize()
+{
+ uint targetStackSize = 512 * 1024;
+ uint threadStackSize = 1; // impossible value
+
+ class StackSizeChecker : public QRunnable
+ {
+ public:
+ uint *stackSize;
+
+ StackSizeChecker(uint *stackSize)
+ :stackSize(stackSize)
+ {
+
+ }
+
+ void run()
+ {
+ *stackSize = QThread::currentThread()->stackSize();
+ }
+ };
+
+ QThreadPool threadPool;
+ threadPool.setStackSize(targetStackSize);
+ threadPool.start(new StackSizeChecker(&threadStackSize));
+ QVERIFY(threadPool.waitForDone(30000)); // 30s timeout
+ QCOMPARE(threadStackSize, targetStackSize);
+}
+
void tst_QThreadPool::stressTest()
{
class Task : public QRunnable
diff --git a/tests/auto/network/access/hsts/tst_qhsts.cpp b/tests/auto/network/access/hsts/tst_qhsts.cpp
index 656516f46b..d72991a2eb 100644
--- a/tests/auto/network/access/hsts/tst_qhsts.cpp
+++ b/tests/auto/network/access/hsts/tst_qhsts.cpp
@@ -32,7 +32,9 @@
#include <QtCore/qvector.h>
#include <QtCore/qpair.h>
#include <QtCore/qurl.h>
+#include <QtCore/qdir.h>
+#include <QtNetwork/private/qhstsstore_p.h>
#include <QtNetwork/private/qhsts_p.h>
QT_USE_NAMESPACE
@@ -46,6 +48,7 @@ private Q_SLOTS:
void testMultilpeKnownHosts();
void testPolicyExpiration();
void testSTSHeaderParser();
+ void testStore();
};
void tst_QHsts::testSingleKnownHost_data()
@@ -313,6 +316,75 @@ void tst_QHsts::testSTSHeaderParser()
QVERIFY(!parser.expirationDate().isValid());
}
+const QLatin1String storeDir(".");
+
+struct TestStoreDeleter
+{
+ ~TestStoreDeleter()
+ {
+ QDir cwd;
+ if (!cwd.remove(QHstsStore::absoluteFilePath(storeDir)))
+ qWarning() << "tst_QHsts::testStore: failed to remove the hsts store file";
+ }
+};
+
+void tst_QHsts::testStore()
+{
+ // Delete the store's file after we finish the test.
+ TestStoreDeleter cleaner;
+
+ const QUrl exampleCom(QStringLiteral("http://example.com"));
+ const QUrl subDomain(QStringLiteral("http://subdomain.example.com"));
+ const QDateTime validDate(QDateTime::currentDateTimeUtc().addDays(1));
+
+ {
+ // We start from an empty cache and empty store:
+ QHstsCache cache;
+ QHstsStore store(storeDir);
+ cache.setStore(&store);
+ QVERIFY(!cache.isKnownHost(exampleCom));
+ QVERIFY(!cache.isKnownHost(subDomain));
+ // (1) This will also store the policy:
+ cache.updateKnownHost(exampleCom, validDate, true);
+ QVERIFY(cache.isKnownHost(exampleCom));
+ QVERIFY(cache.isKnownHost(subDomain));
+ }
+ {
+ // Test the policy stored at (1):
+ QHstsCache cache;
+ QHstsStore store(storeDir);
+ cache.setStore(&store);
+ QVERIFY(cache.isKnownHost(exampleCom));
+ QVERIFY(cache.isKnownHost(subDomain));
+ // (2) Remove subdomains:
+ cache.updateKnownHost(exampleCom, validDate, false);
+ QVERIFY(!cache.isKnownHost(subDomain));
+ }
+ {
+ // Test the previous update (2):
+ QHstsCache cache;
+ QHstsStore store(storeDir);
+ cache.setStore(&store);
+ QVERIFY(cache.isKnownHost(exampleCom));
+ QVERIFY(!cache.isKnownHost(subDomain));
+ }
+ {
+ QHstsCache cache;
+ cache.updateKnownHost(subDomain, validDate, false);
+ QVERIFY(cache.isKnownHost(subDomain));
+ QHstsStore store(storeDir);
+ // (3) This should store policy from cache, over old policy from store:
+ cache.setStore(&store);
+ }
+ {
+ // Test that (3) was stored:
+ QHstsCache cache;
+ QHstsStore store(storeDir);
+ cache.setStore(&store);
+ QVERIFY(cache.isKnownHost(subDomain));
+ }
+}
+
QTEST_MAIN(tst_QHsts)
#include "tst_qhsts.moc"