summaryrefslogtreecommitdiffstats
path: root/src/network/access
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/access')
-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/qhttpmultipart.cpp21
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp8
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp23
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel_p.h4
-rw-r--r--src/network/access/qhttpthreaddelegate.cpp16
-rw-r--r--src/network/access/qhttpthreaddelegate_p.h4
-rw-r--r--src/network/access/qnetworkaccessbackend.cpp22
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp46
-rw-r--r--src/network/access/qnetworkaccessmanager.h3
-rw-r--r--src/network/access/qnetworkaccessmanager_p.h2
-rw-r--r--src/network/access/qnetworkreply.cpp6
-rw-r--r--src/network/access/qnetworkreplyhttpimpl.cpp17
-rw-r--r--src/network/access/qnetworkreplyhttpimpl_p.h5
18 files changed, 478 insertions, 68 deletions
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/qhttpmultipart.cpp b/src/network/access/qhttpmultipart.cpp
index 5c704efeef..303c145394 100644
--- a/src/network/access/qhttpmultipart.cpp
+++ b/src/network/access/qhttpmultipart.cpp
@@ -41,7 +41,7 @@
#include "qhttpmultipart_p.h"
#include "QtCore/qdatetime.h" // for initializing the random number generator with QTime
#include "QtCore/qmutex.h"
-#include "QtCore/qthreadstorage.h"
+#include "QtCore/qrandom.h"
QT_BEGIN_NAMESPACE
@@ -431,23 +431,16 @@ void QHttpPartPrivate::checkHeaderCreated() const
}
}
-Q_GLOBAL_STATIC(QThreadStorage<bool *>, seedCreatedStorage);
-
QHttpMultiPartPrivate::QHttpMultiPartPrivate() : contentType(QHttpMultiPart::MixedType), device(new QHttpMultiPartIODevice(this))
{
- if (!seedCreatedStorage()->hasLocalData()) {
- qsrand(QTime(0,0,0).msecsTo(QTime::currentTime()) ^ reinterpret_cast<quintptr>(this));
- seedCreatedStorage()->setLocalData(new bool(true));
- }
-
- boundary = QByteArray("boundary_.oOo._")
- + QByteArray::number(qrand()).toBase64()
- + QByteArray::number(qrand()).toBase64()
- + QByteArray::number(qrand()).toBase64();
+ // 24 random bytes, becomes 32 characters when encoded to Base64
+ quint32 random[6];
+ QRandomGenerator::fillRange(random);
+ boundary = "boundary_.oOo._"
+ + QByteArray::fromRawData(reinterpret_cast<char *>(random), sizeof(random)).toBase64();
// boundary must not be longer than 70 characters, see RFC 2046, section 5.1.1
- if (boundary.count() > 70)
- boundary = boundary.left(70);
+ Q_ASSERT(boundary.count() <= 70);
}
qint64 QHttpMultiPartIODevice::size() const
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index da055de2da..ae30d3a8cf 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -644,6 +644,7 @@ QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetwor
channels[0].spdyRequestsToSend.insertMulti(request.priority(), pair);
}
+#ifndef Q_OS_WINRT
// For Happy Eyeballs the networkLayerState is set to Unknown
// untill we have started the first connection attempt. So no
// request will be started untill we know if IPv4 or IPv6
@@ -651,6 +652,13 @@ QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetwor
if (networkLayerState == Unknown || networkLayerState == HostLookupPending) {
startHostInfoLookup();
} else if ( networkLayerState == IPv4 || networkLayerState == IPv6 ) {
+#else // !Q_OS_WINRT
+ {
+ // Skip the host lookup part for winrt. Host lookup and proxy handling are done by Windows
+ // internally and networkLayerPreference is ignored on this platform. Instead of refactoring
+ // the whole approach we just pretend that everything important is known here.
+ networkLayerState = IPv4;
+#endif
// this used to be called via invokeMethod and a QueuedConnection
// It is the only place _q_startNextRequest is called directly without going
// through the event loop using a QueuedConnection.
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index e1084e0870..6b2018ef86 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -55,7 +55,6 @@
# include <private/qsslsocket_p.h>
# include <QtNetwork/qsslkey.h>
# include <QtNetwork/qsslcipher.h>
-# include <QtNetwork/qsslconfiguration.h>
#endif
#ifndef QT_NO_BEARERMANAGEMENT
@@ -176,8 +175,8 @@ void QHttpNetworkConnectionChannel::init()
if (!ignoreSslErrorsList.isEmpty())
sslSocket->ignoreSslErrors(ignoreSslErrorsList);
- if (!sslConfiguration.isNull())
- sslSocket->setSslConfiguration(sslConfiguration);
+ if (sslConfiguration.data() && !sslConfiguration->isNull())
+ sslSocket->setSslConfiguration(*sslConfiguration);
} else {
#endif // !QT_NO_SSL
if (connection->connectionType() != QHttpNetworkConnection::ConnectionTypeHTTP2)
@@ -656,7 +655,10 @@ void QHttpNetworkConnectionChannel::setSslConfiguration(const QSslConfiguration
if (socket)
static_cast<QSslSocket *>(socket)->setSslConfiguration(config);
- sslConfiguration = config;
+ if (sslConfiguration.data())
+ *sslConfiguration = config;
+ else
+ sslConfiguration.reset(new QSslConfiguration(config));
}
#endif
@@ -1088,8 +1090,15 @@ void QHttpNetworkConnectionChannel::_q_encrypted()
Q_FALLTHROUGH();
case QSslConfiguration::NextProtocolNegotiationNone: {
protocolHandler.reset(new QHttpProtocolHandler(this));
+ if (!sslConfiguration.data()) {
+ // Our own auto-tests bypass the normal initialization (done by
+ // QHttpThreadDelegate), this means in the past we'd have here
+ // the default constructed QSslConfiguration without any protocols
+ // to negotiate. Let's create it now:
+ sslConfiguration.reset(new QSslConfiguration);
+ }
- QList<QByteArray> protocols = sslConfiguration.allowedNextProtocols();
+ QList<QByteArray> protocols = sslConfiguration->allowedNextProtocols();
const int nProtocols = protocols.size();
// Clear the protocol that we failed to negotiate, so we do not try
// it again on other channels that our connection can create/open.
@@ -1099,10 +1108,10 @@ void QHttpNetworkConnectionChannel::_q_encrypted()
protocols.removeAll(QSslConfiguration::NextProtocolSpdy3_0);
if (nProtocols > protocols.size()) {
- sslConfiguration.setAllowedNextProtocols(protocols);
+ sslConfiguration->setAllowedNextProtocols(protocols);
const int channelCount = connection->d_func()->channelCount;
for (int i = 0; i < channelCount; ++i)
- connection->d_func()->channels[i].setSslConfiguration(sslConfiguration);
+ connection->d_func()->channels[i].setSslConfiguration(*sslConfiguration);
}
connection->setConnectionType(QHttpNetworkConnection::ConnectionTypeHTTP);
diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h
index 61aea9d35d..584d52ddb7 100644
--- a/src/network/access/qhttpnetworkconnectionchannel_p.h
+++ b/src/network/access/qhttpnetworkconnectionchannel_p.h
@@ -78,6 +78,8 @@
# include <QtNetwork/qtcpsocket.h>
#endif
+#include <QtCore/qscopedpointer.h>
+
QT_BEGIN_NAMESPACE
class QHttpNetworkRequest;
@@ -128,7 +130,7 @@ public:
#ifndef QT_NO_SSL
bool ignoreAllSslErrors;
QList<QSslError> ignoreSslErrorsList;
- QSslConfiguration sslConfiguration;
+ QScopedPointer<QSslConfiguration> sslConfiguration;
void ignoreSslErrors();
void ignoreSslErrors(const QList<QSslError> &errors);
void setSslConfiguration(const QSslConfiguration &config);
diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp
index 9d874b4d94..3d17664ed4 100644
--- a/src/network/access/qhttpthreaddelegate.cpp
+++ b/src/network/access/qhttpthreaddelegate.cpp
@@ -44,6 +44,7 @@
#include <QTimer>
#include <QAuthenticator>
#include <QEventLoop>
+#include <QCryptographicHash>
#include "private/qhttpnetworkreply_p.h"
#include "private/qnetworkaccesscache_p.h"
@@ -158,7 +159,10 @@ static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy)
}
if (!key.scheme().isEmpty()) {
+ const QByteArray obfuscatedPassword = QCryptographicHash::hash(proxy->password().toUtf8(),
+ QCryptographicHash::Sha1).toHex();
key.setUserName(proxy->user());
+ key.setPassword(QString::fromUtf8(obfuscatedPassword));
key.setHost(proxy->hostName());
key.setPort(proxy->port());
key.setQuery(result);
@@ -290,18 +294,21 @@ void QHttpThreadDelegate::startRequest()
: QHttpNetworkConnection::ConnectionTypeHTTP;
#ifndef QT_NO_SSL
+ if (ssl && !incomingSslConfiguration.data())
+ incomingSslConfiguration.reset(new QSslConfiguration);
+
if (httpRequest.isHTTP2Allowed() && ssl) {
QList<QByteArray> protocols;
protocols << QSslConfiguration::ALPNProtocolHTTP2
<< QSslConfiguration::NextProtocolHttp1_1;
- incomingSslConfiguration.setAllowedNextProtocols(protocols);
+ incomingSslConfiguration->setAllowedNextProtocols(protocols);
} else if (httpRequest.isSPDYAllowed() && ssl) {
connectionType = QHttpNetworkConnection::ConnectionTypeSPDY;
urlCopy.setScheme(QStringLiteral("spdy")); // to differentiate SPDY requests from HTTPS requests
QList<QByteArray> nextProtocols;
nextProtocols << QSslConfiguration::NextProtocolSpdy3_0
<< QSslConfiguration::NextProtocolHttp1_1;
- incomingSslConfiguration.setAllowedNextProtocols(nextProtocols);
+ incomingSslConfiguration->setAllowedNextProtocols(nextProtocols);
}
#endif // QT_NO_SSL
@@ -330,9 +337,8 @@ void QHttpThreadDelegate::startRequest()
#endif
#ifndef QT_NO_SSL
// Set the QSslConfiguration from this QNetworkRequest.
- if (ssl && incomingSslConfiguration != QSslConfiguration::defaultConfiguration()) {
- httpConnection->setSslConfiguration(incomingSslConfiguration);
- }
+ if (ssl)
+ httpConnection->setSslConfiguration(*incomingSslConfiguration);
#endif
#ifndef QT_NO_NETWORKPROXY
diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h
index 6d1ea11f29..2f6954aa3b 100644
--- a/src/network/access/qhttpthreaddelegate_p.h
+++ b/src/network/access/qhttpthreaddelegate_p.h
@@ -63,7 +63,7 @@
#include "qhttpnetworkrequest_p.h"
#include "qhttpnetworkconnection_p.h"
#include <QSharedPointer>
-#include "qsslconfiguration.h"
+#include <QScopedPointer>
#include "private/qnoncontiguousbytedevice_p.h"
#include "qnetworkaccessauthenticationmanager_p.h"
@@ -88,7 +88,7 @@ public:
// incoming
bool ssl;
#ifndef QT_NO_SSL
- QSslConfiguration incomingSslConfiguration;
+ QScopedPointer<QSslConfiguration> incomingSslConfiguration;
#endif
QHttpNetworkRequest httpRequest;
qint64 downloadBufferMaximumSize;
diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp
index 9c223dd32f..272dd22097 100644
--- a/src/network/access/qnetworkaccessbackend.cpp
+++ b/src/network/access/qnetworkaccessbackend.cpp
@@ -396,30 +396,8 @@ bool QNetworkAccessBackend::start()
#endif
#ifndef QT_NO_NETWORKPROXY
-#ifndef QT_NO_BEARERMANAGEMENT
- // Get the proxy settings from the network session (in the case of service networks,
- // the proxy settings change depending which AP was activated)
- QNetworkSession *session = networkSession.data();
- QNetworkConfiguration config;
- if (session) {
- QNetworkConfigurationManager configManager;
- // The active configuration tells us what IAP is in use
- QVariant v = session->sessionProperty(QLatin1String("ActiveConfiguration"));
- if (v.isValid())
- config = configManager.configurationFromIdentifier(qvariant_cast<QString>(v));
- // Fallback to using the configuration if no active configuration
- if (!config.isValid())
- config = session->configuration();
- // or unspecified configuration if that is no good either
- if (!config.isValid())
- config = QNetworkConfiguration();
- }
- reply->proxyList = manager->queryProxy(QNetworkProxyQuery(config, url()));
-#else // QT_NO_BEARERMANAGEMENT
- // Without bearer management, the proxy depends only on the url
reply->proxyList = manager->queryProxy(QNetworkProxyQuery(url()));
#endif
-#endif
// now start the request
open();
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index d57acc2f6b..eeee82a87c 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -730,9 +730,51 @@ 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 into HSTS cache.
+ Adds HTTP Strict Transport Security policies contained in \a knownHosts into HSTS cache.
\note An expired policy will remove a known host from the cache, if previously
present.
@@ -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/network/access/qnetworkreply.cpp b/src/network/access/qnetworkreply.cpp
index 8e7059de0f..cabea86521 100644
--- a/src/network/access/qnetworkreply.cpp
+++ b/src/network/access/qnetworkreply.cpp
@@ -311,9 +311,11 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
When client code handling the redirected() signal has verified the new URL,
it emits this signal to allow the redirect to go ahead. This protocol applies
to network requests whose redirects policy is set to
- QNetworkRequest::UserVerifiedRedirectsPolicy.
+ QNetworkRequest::UserVerifiedRedirectPolicy
- \sa QNetworkRequest::UserVerifiedRedirectPolicy, QNetworkAccessManager::setRedirectPolicy(), QNetworkRequest::RedirectPolicyAttribute
+ \sa QNetworkRequest::UserVerifiedRedirectPolicy,
+ QNetworkAccessManager::setRedirectPolicy(),
+ QNetworkRequest::RedirectPolicyAttribute
*/
/*!
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
index fec3b0a100..84b1ddf5ac 100644
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
@@ -180,7 +180,8 @@ QNetworkReplyHttpImpl::QNetworkReplyHttpImpl(QNetworkAccessManager* const manage
d->outgoingData = outgoingData;
d->url = request.url();
#ifndef QT_NO_SSL
- d->sslConfiguration = request.sslConfiguration();
+ if (request.url().scheme() == QLatin1String("https"))
+ d->sslConfiguration.reset(new QSslConfiguration(request.sslConfiguration()));
#endif
// FIXME Later maybe set to Unbuffered, especially if it is zerocopy or from cache?
@@ -419,7 +420,10 @@ void QNetworkReplyHttpImpl::setSslConfigurationImplementation(const QSslConfigur
void QNetworkReplyHttpImpl::sslConfigurationImplementation(QSslConfiguration &configuration) const
{
Q_D(const QNetworkReplyHttpImpl);
- configuration = d->sslConfiguration;
+ if (d->sslConfiguration.data())
+ configuration = *d->sslConfiguration;
+ else
+ configuration = request().sslConfiguration();
}
#endif
@@ -786,7 +790,7 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
delegate->ssl = ssl;
#ifndef QT_NO_SSL
if (ssl)
- delegate->incomingSslConfiguration = newHttpRequest.sslConfiguration();
+ delegate->incomingSslConfiguration.reset(new QSslConfiguration(newHttpRequest.sslConfiguration()));
#endif
// Do we use synchronous HTTP?
@@ -1411,10 +1415,13 @@ void QNetworkReplyHttpImplPrivate::replySslErrors(
*toBeIgnored = pendingIgnoreSslErrorsList;
}
-void QNetworkReplyHttpImplPrivate::replySslConfigurationChanged(const QSslConfiguration &sslConfiguration)
+void QNetworkReplyHttpImplPrivate::replySslConfigurationChanged(const QSslConfiguration &newSslConfiguration)
{
// Receiving the used SSL configuration from the HTTP thread
- this->sslConfiguration = sslConfiguration;
+ if (sslConfiguration.data())
+ *sslConfiguration = newSslConfiguration;
+ else
+ sslConfiguration.reset(new QSslConfiguration(newSslConfiguration));
}
void QNetworkReplyHttpImplPrivate::replyPreSharedKeyAuthenticationRequiredSlot(QSslPreSharedKeyAuthenticator *authenticator)
diff --git a/src/network/access/qnetworkreplyhttpimpl_p.h b/src/network/access/qnetworkreplyhttpimpl_p.h
index 9383149124..26b16e8386 100644
--- a/src/network/access/qnetworkreplyhttpimpl_p.h
+++ b/src/network/access/qnetworkreplyhttpimpl_p.h
@@ -58,6 +58,7 @@
#include "QtCore/qpointer.h"
#include "QtCore/qdatetime.h"
#include "QtCore/qsharedpointer.h"
+#include "QtCore/qscopedpointer.h"
#include "qatomic.h"
#include <QtNetwork/QNetworkCacheMetaData>
@@ -260,7 +261,7 @@ public:
#ifndef QT_NO_SSL
- QSslConfiguration sslConfiguration;
+ QScopedPointer<QSslConfiguration> sslConfiguration;
bool pendingIgnoreAllSslErrors;
QList<QSslError> pendingIgnoreSslErrorsList;
#endif
@@ -290,7 +291,7 @@ public:
#ifndef QT_NO_SSL
void replyEncrypted();
void replySslErrors(const QList<QSslError> &, bool *, QList<QSslError> *);
- void replySslConfigurationChanged(const QSslConfiguration&);
+ void replySslConfigurationChanged(const QSslConfiguration &newSslConfiguration);
void replyPreSharedKeyAuthenticationRequiredSlot(QSslPreSharedKeyAuthenticator *);
#endif
#ifndef QT_NO_NETWORKPROXY