summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
Diffstat (limited to 'src/network')
-rw-r--r--src/network/access/access.pri8
-rw-r--r--src/network/access/qhsts.cpp504
-rw-r--r--src/network/access/qhsts_p.h143
-rw-r--r--src/network/access/qhstspolicy.cpp218
-rw-r--r--src/network/access/qhstspolicy.h82
-rw-r--r--src/network/access/qhttp2protocolhandler.cpp44
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp42
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp11
-rw-r--r--src/network/access/qhttpnetworkreply.cpp8
-rw-r--r--src/network/access/qhttpnetworkreply_p.h2
-rw-r--r--src/network/access/qhttpnetworkrequest.cpp21
-rw-r--r--src/network/access/qhttpnetworkrequest_p.h6
-rw-r--r--src/network/access/qhttpthreaddelegate.cpp5
-rw-r--r--src/network/access/qhttpthreaddelegate_p.h3
-rw-r--r--src/network/access/qnetworkaccessbackend_p.h1
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp189
-rw-r--r--src/network/access/qnetworkaccessmanager.h15
-rw-r--r--src/network/access/qnetworkaccessmanager_p.h12
-rw-r--r--src/network/access/qnetworkdiskcache.cpp2
-rw-r--r--src/network/access/qnetworkreply.cpp25
-rw-r--r--src/network/access/qnetworkreply.h1
-rw-r--r--src/network/access/qnetworkreplyhttpimpl.cpp90
-rw-r--r--src/network/access/qnetworkreplyhttpimpl_p.h7
-rw-r--r--src/network/access/qnetworkrequest.cpp43
-rw-r--r--src/network/access/qnetworkrequest.h12
-rw-r--r--src/network/bearer/qnetworkconfiguration.cpp38
-rw-r--r--src/network/bearer/qnetworkconfiguration.h3
-rw-r--r--src/network/bearer/qnetworkconfiguration_p.h6
-rw-r--r--src/network/kernel/kernel.pri3
-rw-r--r--src/network/kernel/qdnslookup.cpp4
-rw-r--r--src/network/kernel/qhostaddress.cpp86
-rw-r--r--src/network/kernel/qhostaddress.h10
-rw-r--r--src/network/kernel/qhostinfo.cpp148
-rw-r--r--src/network/kernel/qhostinfo.h63
-rw-r--r--src/network/kernel/qhostinfo_p.h42
-rw-r--r--src/network/kernel/qnetworkproxy.cpp19
-rw-r--r--src/network/kernel/qnetworkproxy.h4
-rw-r--r--src/network/socket/qabstractsocket.cpp36
-rw-r--r--src/network/socket/qlocalserver.h3
-rw-r--r--src/network/socket/qnativesocketengine_p.h4
-rw-r--r--src/network/socket/qnativesocketengine_win.cpp14
-rw-r--r--src/network/socket/qnativesocketengine_winrt.cpp2
-rw-r--r--src/network/socket/qsctpserver.h2
-rw-r--r--src/network/socket/qsctpsocket.h2
-rw-r--r--src/network/socket/qsocks5socketengine.cpp11
-rw-r--r--src/network/ssl/qsslerror.cpp4
-rw-r--r--src/network/ssl/qsslsocket.cpp1
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols.cpp2
-rw-r--r--src/network/ssl/qsslsocket_winrt.cpp8
49 files changed, 1836 insertions, 173 deletions
diff --git a/src/network/access/access.pri b/src/network/access/access.pri
index 70ace3f55e..13d52ea44a 100644
--- a/src/network/access/access.pri
+++ b/src/network/access/access.pri
@@ -39,7 +39,9 @@ HEADERS += \
access/qhttpmultipart.h \
access/qhttpmultipart_p.h \
access/qnetworkfile_p.h \
- access/qhttp2protocolhandler_p.h
+ access/qhttp2protocolhandler_p.h \
+ access/qhsts_p.h \
+ access/qhstspolicy.h
SOURCES += \
access/qftp.cpp \
@@ -72,7 +74,9 @@ SOURCES += \
access/qhttpthreaddelegate.cpp \
access/qhttpmultipart.cpp \
access/qnetworkfile.cpp \
- access/qhttp2protocolhandler.cpp
+ access/qhttp2protocolhandler.cpp \
+ access/qhsts.cpp \
+ access/qhstspolicy.cpp
mac: LIBS_PRIVATE += -framework Security
diff --git a/src/network/access/qhsts.cpp b/src/network/access/qhsts.cpp
new file mode 100644
index 0000000000..5e4f75b0ed
--- /dev/null
+++ b/src/network/access/qhsts.cpp
@@ -0,0 +1,504 @@
+/****************************************************************************
+**
+** 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 "qhsts_p.h"
+
+#include "QtCore/qstringlist.h"
+
+#include "QtCore/private/qipaddress_p.h"
+
+QT_BEGIN_NAMESPACE
+
+static bool is_valid_domain_name(const QString &host)
+{
+ if (!host.size())
+ return false;
+
+ // RFC6797 8.1.1
+ // If the substring matching the host production from the Request-URI
+ // (of the message to which the host responded) syntactically matches
+ //the IP-literal or IPv4address productions from Section 3.2.2 of
+ //[RFC3986], then the UA MUST NOT note this host as a Known HSTS Host.
+ using namespace QIPAddressUtils;
+
+ IPv4Address ipv4Addr = {};
+ if (parseIp4(ipv4Addr, host.constBegin(), host.constEnd()))
+ return false;
+
+ IPv6Address ipv6Addr = {};
+ // Unlike parseIp4, parseIp6 returns nullptr if it managed to parse IPv6
+ // address successfully.
+ if (!parseIp6(ipv6Addr, host.constBegin(), host.constEnd()))
+ return false;
+
+ // TODO: for now we do not test IPvFuture address, it must be addressed
+ // by introducing parseIpFuture (actually, there is an implementation
+ // in QUrl that can be adopted/modified/moved to QIPAddressUtils).
+ return true;
+}
+
+void QHstsCache::updateFromHeaders(const QList<QPair<QByteArray, QByteArray>> &headers,
+ const QUrl &url)
+{
+ if (!url.isValid())
+ return;
+
+ QHstsHeaderParser parser;
+ if (parser.parse(headers))
+ updateKnownHost(url.host(), parser.expirationDate(), parser.includeSubDomains());
+}
+
+void QHstsCache::updateFromPolicies(const QList<QHstsPolicy> &policies)
+{
+ for (const auto &policy : policies)
+ updateKnownHost(policy.host(), policy.expiry(), policy.includesSubDomains());
+}
+
+void QHstsCache::updateKnownHost(const QUrl &url, const QDateTime &expires,
+ bool includeSubDomains)
+{
+ if (!url.isValid())
+ return;
+
+ updateKnownHost(url.host(), expires, includeSubDomains);
+}
+
+void QHstsCache::updateKnownHost(const QString &host, const QDateTime &expires,
+ bool includeSubDomains)
+{
+ if (!is_valid_domain_name(host))
+ return;
+
+ // HSTS is a per-host policy, regardless of protocol, port or any of the other
+ // details in an URL; so we only want the host part. QUrl::host handles
+ // IDNA 2003 (RFC3490) for us, as required by HSTS (RFC6797, section 10).
+ const HostName hostName(host);
+ const auto pos = knownHosts.find(hostName);
+ const QHstsPolicy newPolicy(expires, includeSubDomains, hostName.name);
+ if (pos == knownHosts.end()) {
+ // A new, previously unknown host.
+ if (newPolicy.isExpired()) {
+ // Nothing to do at all - we did not know this host previously,
+ // we do not have to - since its policy expired.
+ return;
+ }
+
+ knownHosts.insert(pos, hostName, newPolicy);
+ return;
+ }
+
+ if (newPolicy.isExpired())
+ knownHosts.erase(pos);
+ else
+ *pos = std::move(newPolicy);
+}
+
+bool QHstsCache::isKnownHost(const QUrl &url) const
+{
+ if (!url.isValid() || !is_valid_domain_name(url.host()))
+ return false;
+
+ /*
+ RFC6797, 8.2. Known HSTS Host Domain Name Matching
+
+ * Superdomain Match
+ If a label-for-label match between an entire Known HSTS Host's
+ domain name and a right-hand portion of the given domain name
+ is found, then this Known HSTS Host's domain name is a
+ superdomain match for the given domain name. There could be
+ multiple superdomain matches for a given domain name.
+ * Congruent Match
+ If a label-for-label match between a Known HSTS Host's domain
+ name and the given domain name is found -- i.e., there are no
+ further labels to compare -- then the given domain name
+ congruently matches this Known HSTS Host.
+
+ We start from the congruent match, and then chop labels and dots and
+ proceed with superdomain match. While RFC6797 recommends to start from
+ superdomain, the result is the same - some valid policy will make a host
+ known.
+ */
+
+ bool superDomainMatch = false;
+ const QString hostNameAsString(url.host());
+ HostName nameToTest(static_cast<QStringRef>(&hostNameAsString));
+ while (nameToTest.fragment.size()) {
+ auto const pos = knownHosts.find(nameToTest);
+ if (pos != knownHosts.end()) {
+ if (pos.value().isExpired())
+ knownHosts.erase(pos);
+ else if (!superDomainMatch || pos.value().includesSubDomains())
+ return true;
+ }
+
+ const int dot = nameToTest.fragment.indexOf(QLatin1Char('.'));
+ if (dot == -1)
+ break;
+
+ nameToTest.fragment = nameToTest.fragment.mid(dot + 1);
+ superDomainMatch = true;
+ }
+
+ return false;
+}
+
+void QHstsCache::clear()
+{
+ knownHosts.clear();
+}
+
+QList<QHstsPolicy> QHstsCache::policies() const
+{
+ return knownHosts.values();
+}
+
+// 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
+// be combined into a valid directive and if some weird combination of
+// valid tokens is found - we immediately stop.
+// And finally we call parseDirective again and again until some error found or
+// we have no more bytes in the header.
+
+// The following isXXX functions are based on RFC2616, 2.2 Basic Rules.
+
+static bool isCHAR(int c)
+{
+ // CHAR = <any US-ASCII character (octets 0 - 127)>
+ return c >= 0 && c <= 127;
+}
+
+static bool isCTL(int c)
+{
+ // CTL = <any US-ASCII control character
+ // (octets 0 - 31) and DEL (127)>
+ return (c >= 0 && c <= 31) || c == 127;
+}
+
+
+static bool isLWS(int c)
+{
+ // LWS = [CRLF] 1*( SP | HT )
+ //
+ // CRLF = CR LF
+ // CR = <US-ASCII CR, carriage return (13)>
+ // LF = <US-ASCII LF, linefeed (10)>
+ // SP = <US-ASCII SP, space (32)>
+ // HT = <US-ASCII HT, horizontal-tab (9)>
+ //
+ // CRLF is handled by the time we parse a header (they were replaced with
+ // spaces). We only have to deal with remaining SP|HT
+ return c == ' ' || c == '\t';
+}
+
+static bool isTEXT(char c)
+{
+ // TEXT = <any OCTET except CTLs,
+ // but including LWS>
+ return !isCTL(c) || isLWS(c);
+}
+
+static bool isSeparator(char c)
+{
+ // separators = "(" | ")" | "<" | ">" | "@"
+ // | "," | ";" | ":" | "\" | <">
+ // | "/" | "[" | "]" | "?" | "="
+ // | "{" | "}" | SP | HT
+ static const char separators[] = "()<>@,;:\\\"/[]?={}";
+ static const char *end = separators + sizeof separators - 1;
+ return isLWS(c) || std::find(separators, end, c) != end;
+}
+
+static QByteArray unescapeMaxAge(const QByteArray &value)
+{
+ if (value.size() < 2 || value[0] != '"')
+ return value;
+
+ Q_ASSERT(value[value.size() - 1] == '"');
+ return value.mid(1, value.size() - 2);
+}
+
+static bool isTOKEN(char c)
+{
+ // token = 1*<any CHAR except CTLs or separators>
+ return isCHAR(c) && !isCTL(c) && !isSeparator(c);
+}
+
+/*
+
+RFC6797, 6.1 Strict-Transport-Security HTTP Response Header Field.
+Syntax:
+
+Strict-Tranposrt-Security = "Strict-Transport-Security" ":"
+ [ directive ] *( ";" [ directive ] )
+
+directive = directive-name [ "=" directive-value ]
+directive-name = token
+directive-value = token | quoted-string
+
+RFC 2616, 2.2 Basic Rules.
+
+token = 1*<any CHAR except CTLs or separators>
+quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
+
+
+qdtext = <any TEXT except <">>
+quoted-pair = "\" CHAR
+
+*/
+
+bool QHstsHeaderParser::parse(const QList<QPair<QByteArray, QByteArray>> &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;
+ }
+ }
+ }
+
+ // In case it was set by a syntactically correct header (but without
+ // REQUIRED max-age directive):
+ subDomainsFound = false;
+
+ return false;
+}
+
+bool QHstsHeaderParser::parseSTSHeader()
+{
+ expiry = QDateTime();
+ maxAgeFound = false;
+ subDomainsFound = false;
+ maxAge = 0;
+ tokenPos = 0;
+ token.clear();
+
+ while (tokenPos < header.size()) {
+ if (!parseDirective())
+ return false;
+
+ if (token.size() && token != ";") {
+ // After a directive we can only have a ";" or no more tokens.
+ // Invalid syntax.
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool QHstsHeaderParser::parseDirective()
+{
+ // RFC 6797, 6.1:
+ //
+ // directive = directive-name [ "=" directive-value ]
+ // directive-name = token
+ // directive-value = token | quoted-string
+
+
+ // RFC 2616, 2.2:
+ //
+ // token = 1*<any CHAR except CTLs or separators>
+
+ if (!nextToken())
+ return false;
+
+ if (!token.size()) // No more data, but no error.
+ return true;
+
+ if (token == ";") // That's a weird grammar, but that's what it is.
+ return true;
+
+ if (!isTOKEN(token[0])) // Not a valid directive-name.
+ return false;
+
+ const QByteArray directiveName = token;
+ // 2. Try to read "=" or ";".
+ if (!nextToken())
+ return false;
+
+ QByteArray directiveValue;
+ if (token == ";") // No directive-value
+ return processDirective(directiveName, directiveValue);
+
+ if (token == "=") {
+ // We expect a directive-value now:
+ if (!nextToken() || !token.size())
+ return false;
+ directiveValue = token;
+ } else if (token.size()) {
+ // Invalid syntax:
+ return false;
+ }
+
+ if (!processDirective(directiveName, directiveValue))
+ return false;
+
+ // Read either ";", or 'end of header', or some invalid token.
+ return nextToken();
+}
+
+bool QHstsHeaderParser::processDirective(const QByteArray &name, const QByteArray &value)
+{
+ Q_ASSERT(name.size());
+ // RFC6797 6.1/3 Directive names are case-insensitive
+ const auto lcName = name.toLower();
+ if (lcName == "max-age") {
+ // RFC 6797, 6.1.1
+ // The syntax of the max-age directive's REQUIRED value (after
+ // quoted-string unescaping, if necessary) is defined as:
+ //
+ // max-age-value = delta-seconds
+ if (maxAgeFound) {
+ // RFC 6797, 6.1/2:
+ // All directives MUST appear only once in an STS header field.
+ return false;
+ }
+
+ const QByteArray unescapedValue = unescapeMaxAge(value);
+ if (!unescapedValue.size())
+ return false;
+
+ bool ok = false;
+ const qint64 age = unescapedValue.toLongLong(&ok);
+ if (!ok || age < 0)
+ return false;
+
+ maxAge = age;
+ maxAgeFound = true;
+ } else if (lcName == "includesubdomains") {
+ // RFC 6797, 6.1.2. The includeSubDomains Directive.
+ // The OPTIONAL "includeSubDomains" directive is a valueless directive.
+
+ if (subDomainsFound) {
+ // RFC 6797, 6.1/2:
+ // All directives MUST appear only once in an STS header field.
+ return false;
+ }
+
+ subDomainsFound = true;
+ } // else we do nothing, skip unknown directives (RFC 6797, 6.1/5)
+
+ return true;
+}
+
+bool QHstsHeaderParser::nextToken()
+{
+ // Returns true if we found a valid token or we have no more data (token is
+ // empty then).
+
+ token.clear();
+
+ // 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]))
+ ++tokenPos;
+
+ if (tokenPos == header.size())
+ return true;
+
+ const char ch = header[tokenPos];
+ if (ch == ';' || ch == '=') {
+ token.append(ch);
+ ++tokenPos;
+ return true;
+ }
+
+ // RFC 2616, 2.2.
+ //
+ // quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
+ // qdtext = <any TEXT except <">>
+ if (ch == '"') {
+ int last = tokenPos + 1;
+ while (last < header.size()) {
+ if (header[last] == '"') {
+ // The end of a quoted-string.
+ break;
+ } else if (header[last] == '\\') {
+ // quoted-pair = "\" CHAR
+ if (last + 1 < header.size() && isCHAR(header[last + 1]))
+ last += 2;
+ else
+ return false;
+ } else {
+ if (!isTEXT(header[last]))
+ return false;
+ ++last;
+ }
+ }
+
+ if (last >= header.size()) // no closing '"':
+ return false;
+
+ token = header.mid(tokenPos, last - tokenPos + 1);
+ tokenPos = last + 1;
+ return true;
+ }
+
+ // RFC 2616, 2.2:
+ //
+ // token = 1*<any CHAR except CTLs or separators>
+ if (!isTOKEN(ch))
+ return false;
+
+ int last = tokenPos + 1;
+ while (last < header.size() && isTOKEN(header[last]))
+ ++last;
+
+ token = header.mid(tokenPos, last - tokenPos);
+ tokenPos = last;
+
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qhsts_p.h b/src/network/access/qhsts_p.h
new file mode 100644
index 0000000000..5d95f39b96
--- /dev/null
+++ b/src/network/access/qhsts_p.h
@@ -0,0 +1,143 @@
+/****************************************************************************
+**
+** 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 QHSTS_P_H
+#define QHSTS_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/qhstspolicy.h>
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qglobal.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qpair.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qmap.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_AUTOTEST_EXPORT QHstsCache
+{
+public:
+
+ void updateFromHeaders(const QList<QPair<QByteArray, QByteArray>> &headers,
+ const QUrl &url);
+ void updateFromPolicies(const QList<QHstsPolicy> &hosts);
+ void updateKnownHost(const QUrl &url, const QDateTime &expires,
+ bool includeSubDomains);
+ bool isKnownHost(const QUrl &url) const;
+ void clear();
+
+ QList<QHstsPolicy> policies() const;
+
+private:
+
+ void updateKnownHost(const QString &hostName, const QDateTime &expires,
+ bool includeSubDomains);
+
+ struct HostName
+ {
+ explicit HostName(const QString &n) : name(n) { }
+ explicit HostName(const QStringRef &r) : fragment(r) { }
+
+ bool operator < (const HostName &rhs) const
+ {
+ if (fragment.size()) {
+ if (rhs.fragment.size())
+ return fragment < rhs.fragment;
+ return fragment < QStringRef(&rhs.name);
+ }
+
+ if (rhs.fragment.size())
+ return QStringRef(&name) < rhs.fragment;
+ return name < rhs.name;
+ }
+
+ // We use 'name' for a HostName object contained in our dictionary;
+ // we use 'fragment' only during lookup, when chopping the complete host
+ // name, removing subdomain names (such HostName object is 'transient', it
+ // must not outlive the original QString object.
+ QString name;
+ QStringRef fragment;
+ };
+
+ mutable QMap<HostName, QHstsPolicy> knownHosts;
+};
+
+class Q_AUTOTEST_EXPORT QHstsHeaderParser
+{
+public:
+
+ bool parse(const QList<QPair<QByteArray, QByteArray>> &headers);
+
+ QDateTime expirationDate() const { return expiry; }
+ bool includeSubDomains() const { return subDomainsFound; }
+
+private:
+
+ bool parseSTSHeader();
+ bool parseDirective();
+ bool processDirective(const QByteArray &name, const QByteArray &value);
+ bool nextToken();
+
+ QByteArray header;
+ QByteArray token;
+
+ QDateTime expiry;
+ int tokenPos = 0;
+ bool maxAgeFound = false;
+ qint64 maxAge = 0;
+ bool subDomainsFound = false;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/qhstspolicy.cpp b/src/network/access/qhstspolicy.cpp
new file mode 100644
index 0000000000..2cec587f4d
--- /dev/null
+++ b/src/network/access/qhstspolicy.cpp
@@ -0,0 +1,218 @@
+/****************************************************************************
+**
+** 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 "qhstspolicy.h"
+
+#include <QtCore/qdatetime.h>
+#include <QtCore/qstring.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QHstsPolicy
+ \brief The QHstsPolicy class specifies that a host supports HTTP Strict Transport
+ Security policy (HSTS).
+ \since 5.9
+ \ingroup network
+ \inmodule QtNetwork
+
+ HSTS policy defines a period of time during which QNetworkAccessManager
+ should only access a host in a secure fashion. HSTS policy is defined by
+ 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().
+
+ \sa QNetworkAccessManager::enableStrictTransportSecurity()
+*/
+
+class QHstsPolicyPrivate
+{
+public:
+ QUrl url;
+ QDateTime expiry;
+ bool includeSubDomains = false;
+
+ bool operator == (const QHstsPolicyPrivate &other) const
+ {
+ return url.host() == other.url.host() && expiry == other.expiry
+ && includeSubDomains == other.includeSubDomains;
+ }
+};
+
+/*!
+ Constructs an invalid (expired) policy with empty host name and subdomains
+ not included.
+*/
+QHstsPolicy::QHstsPolicy() : d(new QHstsPolicyPrivate)
+{
+}
+
+/*!
+ Constructs QHstsPolicy with \a expiry (in UTC); \a includeSubDomains parameter
+ defines if this policy must also include subdomains, \a host data is interpreted
+ according to \a mode.
+
+ \sa QUrl::setHost(), QUrl::ParsingMode
+*/
+QHstsPolicy::QHstsPolicy(const QDateTime &expiry, bool includeSubDomains, const QString &host,
+ QUrl::ParsingMode mode)
+ : d(new QHstsPolicyPrivate)
+{
+ d->url.setHost(host, mode);
+ d->expiry = expiry;
+ d->includeSubDomains = includeSubDomains;
+}
+
+/*!
+ Creates a copy of \a other object.
+*/
+QHstsPolicy::QHstsPolicy(const QHstsPolicy &other)
+ : d(new QHstsPolicyPrivate(*other.d))
+{
+}
+
+/*!
+ Destructor.
+*/
+QHstsPolicy::~QHstsPolicy()
+{
+}
+
+/*!
+ Copy-assignment operator, makes a copy of \a other.
+*/
+QHstsPolicy &QHstsPolicy::operator=(const QHstsPolicy &other)
+{
+ *d = *other.d;
+ return *this;
+}
+
+
+/*!
+ Move-assignment operator.
+*/
+QHstsPolicy &QHstsPolicy::operator=(QHstsPolicy &&other) Q_DECL_NOTHROW
+{
+ qSwap(d, other.d);
+ return *this;
+}
+
+/*!
+ Sets a host, \a host data is interpreted according to \a mode parameter.
+
+ \sa host(), QUrl::setHost(), QUrl::ParsingMode
+*/
+void QHstsPolicy::setHost(const QString &host, QUrl::ParsingMode mode)
+{
+ d->url.setHost(host, mode);
+}
+
+/*!
+ Returns a host for a given policy, formatted according to \a options.
+
+ \sa setHost(), QUrl::host(), QUrl::ComponentFormattingOptions
+*/
+QString QHstsPolicy::host(QUrl::ComponentFormattingOptions options) const
+{
+ return d->url.host(options);
+}
+
+/*!
+ Sets the expiration date for the policy (in UTC).
+
+ \sa expiry()
+*/
+void QHstsPolicy::setExpiry(const QDateTime &expiry)
+{
+ d->expiry = expiry;
+}
+
+/*!
+ Returns the expiration date for the policy (in UTC).
+
+ \sa setExpiry()
+*/
+QDateTime QHstsPolicy::expiry() const
+{
+ return d->expiry;
+}
+
+/*!
+ Includes or excludes subdomains for this policy.
+
+ \sa includeSubdomains()
+*/
+void QHstsPolicy::setIncludesSubDomains(bool include)
+{
+ d->includeSubDomains = include;
+}
+
+/*!
+ Returns \c true if this policy also includes subdomains.
+
+ \sa setIncludesSubDomains()
+ */
+bool QHstsPolicy::includesSubDomains() const
+{
+ return d->includeSubDomains;
+}
+
+/*!
+ Returns \c true if the two policies have the same host and expriration date
+ while agreeing on whether to include or exclude subdomains.
+*/
+bool QHstsPolicy::operator==(const QHstsPolicy &other) const
+{
+ return *d == *other.d;
+}
+
+/*!
+ Return \c true if this policy has a valid expiration date and this date
+ is greater than QDateTime::currentGetDateTimeUtc().
+
+ \sa setExpiry(), expiry()
+*/
+bool QHstsPolicy::isExpired() const
+{
+ return !d->expiry.isValid() || d->expiry <= QDateTime::currentDateTimeUtc();
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qhstspolicy.h b/src/network/access/qhstspolicy.h
new file mode 100644
index 0000000000..4260ac278c
--- /dev/null
+++ b/src/network/access/qhstspolicy.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** 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 QHSTSPOLICY_H
+#define QHSTSPOLICY_H
+
+#include <QtNetwork/qtnetworkglobal.h>
+
+#include <QtCore/qscopedpointer.h>
+#include <QtCore/qurl.h>
+
+QT_BEGIN_NAMESPACE
+
+class QHstsPolicyPrivate;
+class QDateTime;
+class QString;
+class Q_NETWORK_EXPORT QHstsPolicy
+{
+public:
+
+ QHstsPolicy();
+ QHstsPolicy(const QDateTime &expiry, bool includeSubDomains, const QString &host,
+ QUrl::ParsingMode mode = QUrl::DecodedMode);
+ QHstsPolicy(const QHstsPolicy &rhs);
+ QHstsPolicy &operator=(const QHstsPolicy &rhs);
+ QHstsPolicy &operator=(QHstsPolicy &&rhs) Q_DECL_NOTHROW;
+ ~QHstsPolicy();
+
+ void setHost(const QString &host, QUrl::ParsingMode mode = QUrl::DecodedMode);
+ QString host(QUrl::ComponentFormattingOptions options = QUrl::FullyDecoded) const;
+ void setExpiry(const QDateTime &expiry);
+ QDateTime expiry() const;
+ void setIncludesSubDomains(bool include);
+ bool includesSubDomains() const;
+
+ bool operator==(const QHstsPolicy &rhs) const;
+ bool isExpired() const;
+
+private:
+
+ QScopedPointer<QHstsPolicyPrivate> d;
+};
+
+QT_END_NAMESPACE
+
+#endif // QHSTSPOLICY_H
diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp
index 3fa0c18dc0..60313422da 100644
--- a/src/network/access/qhttp2protocolhandler.cpp
+++ b/src/network/access/qhttp2protocolhandler.cpp
@@ -53,6 +53,10 @@
#include <QtCore/qlist.h>
#include <QtCore/qurl.h>
+#ifndef QT_NO_NETWORKPROXY
+#include <QtNetwork/qnetworkproxy.h>
+#endif
+
#include <algorithm>
#include <vector>
@@ -61,7 +65,8 @@ QT_BEGIN_NAMESPACE
namespace
{
-HPack::HttpHeader build_headers(const QHttpNetworkRequest &request, quint32 maxHeaderListSize)
+HPack::HttpHeader build_headers(const QHttpNetworkRequest &request, quint32 maxHeaderListSize,
+ bool useProxy)
{
using namespace HPack;
@@ -73,7 +78,7 @@ HPack::HttpHeader build_headers(const QHttpNetworkRequest &request, quint32 maxH
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(false)));
+ header.push_back(HeaderField(":path", request.uri(useProxy)));
header.push_back(HeaderField(":scheme", request.url().scheme().toLatin1()));
HeaderSize size = header_size(header);
@@ -208,7 +213,7 @@ void QHttp2ProtocolHandler::_q_receiveReply()
Q_ASSERT(m_socket);
Q_ASSERT(m_channel);
- do {
+ while (!goingAway || activeStreams.size()) {
const auto result = frameReader.read(*m_socket);
switch (result) {
case FrameStatus::incompleteFrame:
@@ -264,13 +269,19 @@ void QHttp2ProtocolHandler::_q_receiveReply()
// 5.1 - ignore unknown frames.
break;
}
- } while (!goingAway || activeStreams.size());
+ }
}
bool QHttp2ProtocolHandler::sendRequest()
{
- if (goingAway)
+ if (goingAway) {
+ // Stop further calls to this method: we have received GOAWAY
+ // so we cannot create new streams.
+ m_channel->emitFinishedWithError(QNetworkReply::ProtocolUnknownError,
+ "GOAWAY received, cannot start a request");
+ m_channel->spdyRequestsToSend.clear();
return false;
+ }
if (!prefaceSent && !sendClientPreface())
return false;
@@ -397,7 +408,11 @@ bool QHttp2ProtocolHandler::sendHEADERS(Stream &stream)
frameWriter.append(quint32()); // No stream dependency in Qt.
frameWriter.append(stream.weight());
- const auto headers = build_headers(stream.request(), maxHeaderListSize);
+ bool useProxy = false;
+#ifndef QT_NO_NETWORKPROXY
+ useProxy = m_connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy;
+#endif
+ const auto headers = build_headers(stream.request(), maxHeaderListSize, useProxy);
if (!headers.size()) // nothing fits into maxHeaderListSize
return false;
@@ -756,14 +771,10 @@ void QHttp2ProtocolHandler::handleGOAWAY()
// "The last stream identifier can be set to 0 if no
// streams were processed."
lastStreamID = 1;
- }
-
- if (!(lastStreamID & 0x1)) {
+ } 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");
- }
-
- if (lastStreamID >= nextID) {
+ } else if (lastStreamID >= nextID) {
// "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."
@@ -776,6 +787,13 @@ void QHttp2ProtocolHandler::handleGOAWAY()
goingAway = true;
+ // For the requests (and streams) we did not start yet, we have to report an
+ // error.
+ m_channel->emitFinishedWithError(QNetworkReply::ProtocolUnknownError,
+ "GOAWAY received, cannot start a request");
+ // Also, prevent further calls to sendRequest:
+ m_channel->spdyRequestsToSend.clear();
+
QNetworkReply::NetworkError error = QNetworkReply::NoError;
QString message;
qt_error(errorCode, error, message);
@@ -970,7 +988,7 @@ bool QHttp2ProtocolHandler::acceptSetting(Http2::Settings identifier, quint32 ne
}
if (identifier == Settings::MAX_CONCURRENT_STREAMS_ID) {
- if (maxConcurrentStreams > maxPeerConcurrentStreams) {
+ if (newValue > maxPeerConcurrentStreams) {
connectionError(PROTOCOL_ERROR, "SETTINGS invalid number of concurrent streams");
return false;
}
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index 128f75f93b..fd3cbbe36e 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -522,17 +522,17 @@ QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socke
if (!reply->request().isFollowRedirects())
return QUrl();
- QUrl rUrl;
+ QUrl redirectUrl;
const QList<QPair<QByteArray, QByteArray> > fields = reply->header();
for (const QNetworkReply::RawHeaderPair &header : fields) {
if (header.first.toLower() == "location") {
- rUrl = QUrl::fromEncoded(header.second);
+ redirectUrl = QUrl::fromEncoded(header.second);
break;
}
}
// If the location url is invalid/empty, we emit ProtocolUnknownError
- if (!rUrl.isValid()) {
+ if (!redirectUrl.isValid()) {
emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError);
return QUrl();
}
@@ -544,24 +544,38 @@ QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socke
}
// Resolve the URL if it's relative
- if (rUrl.isRelative())
- rUrl = reply->request().url().resolved(rUrl);
+ if (redirectUrl.isRelative())
+ redirectUrl = reply->request().url().resolved(redirectUrl);
// Check redirect url protocol
- QString scheme = rUrl.scheme();
- if (scheme == QLatin1String("http") || scheme == QLatin1String("https")) {
- QString previousUrlScheme = reply->request().url().scheme();
- // Check if we're doing an unsecure redirect (https -> http)
- if (previousUrlScheme == QLatin1String("https")
- && scheme == QLatin1String("http")) {
- emitReplyError(socket, reply, QNetworkReply::InsecureRedirectError);
- return QUrl();
+ const QUrl priorUrl(reply->request().url());
+ if (redirectUrl.scheme() == QLatin1String("http") || redirectUrl.scheme() == QLatin1String("https")) {
+ switch (reply->request().redirectsPolicy()) {
+ case QNetworkRequest::NoLessSafeRedirectsPolicy:
+ // Here we could handle https->http redirects as InsecureProtocolError.
+ // However, if HSTS is enabled and redirectUrl.host() is a known STS
+ // host, then we'll replace its scheme and this won't downgrade protocol,
+ // after all. We cannot access QNAM's STS cache from here, so delegate
+ // this check to QNetworkReplyHttpImpl.
+ break;
+ case QNetworkRequest::SameOriginRedirectsPolicy:
+ if (priorUrl.host() != redirectUrl.host()
+ || priorUrl.scheme() != redirectUrl.scheme()
+ || priorUrl.port() != redirectUrl.port()) {
+ emitReplyError(socket, reply, QNetworkReply::InsecureRedirectError);
+ return QUrl();
+ }
+ break;
+ case QNetworkRequest::UserVerifiedRedirectsPolicy:
+ break;
+ default:
+ Q_ASSERT(!"Unexpected redirect policy");
}
} else {
emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError);
return QUrl();
}
- return rUrl;
+ return redirectUrl;
}
void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request)
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index 7fa19dc65b..668409a988 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -180,9 +180,7 @@ void QHttpNetworkConnectionChannel::init()
sslSocket->setSslConfiguration(sslConfiguration);
} else {
#endif // !QT_NO_SSL
- if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2)
- protocolHandler.reset(new QHttp2ProtocolHandler(this));
- else
+ if (connection->connectionType() != QHttpNetworkConnection::ConnectionTypeHTTP2)
protocolHandler.reset(new QHttpProtocolHandler(this));
#ifndef QT_NO_SSL
}
@@ -839,6 +837,9 @@ void QHttpNetworkConnectionChannel::_q_connected()
} else {
state = QHttpNetworkConnectionChannel::IdleState;
if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2) {
+ // We have to reset QHttp2ProtocolHandler's state machine, it's a new
+ // connection and the handler's state is unique per connection.
+ protocolHandler.reset(new QHttp2ProtocolHandler(this));
if (spdyRequestsToSend.count() > 0) {
// wait for data from the server first (e.g. initial window, max concurrent requests)
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
@@ -1092,6 +1093,10 @@ void QHttpNetworkConnectionChannel::_q_encrypted()
emitFinishedWithError(QNetworkReply::SslHandshakeFailedError,
"detected unknown Next Protocol Negotiation protocol");
}
+ } else if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2) {
+ // We have to reset QHttp2ProtocolHandler's state machine, it's a new
+ // connection and the handler's state is unique per connection.
+ protocolHandler.reset(new QHttp2ProtocolHandler(this));
}
if (!socket)
diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp
index 24ada3a81f..612abb9044 100644
--- a/src/network/access/qhttpnetworkreply.cpp
+++ b/src/network/access/qhttpnetworkreply.cpp
@@ -299,6 +299,11 @@ void QHttpNetworkReply::setSpdyWasUsed(bool spdy)
d_func()->spdyUsed = spdy;
}
+qint64 QHttpNetworkReply::removedContentLength() const
+{
+ return d_func()->removedContentLength;
+}
+
bool QHttpNetworkReply::isRedirecting() const
{
return d_func()->isRedirecting();
@@ -326,6 +331,7 @@ QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
currentlyReceivedDataInWindow(0),
currentlyUploadedDataInWindow(0),
totallyUploadedData(0),
+ removedContentLength(-1),
connection(0),
autoDecompress(false), responseData(), requestIsPrepared(false)
,pipeliningUsed(false), spdyUsed(false), downstreamLimited(false)
@@ -398,12 +404,12 @@ void QHttpNetworkReplyPrivate::removeAutoDecompressHeader()
end = fields.end();
while (it != end) {
if (qstricmp(name.constData(), it->first.constData()) == 0) {
+ removedContentLength = strtoull(it->second.constData(), nullptr, 0);
fields.erase(it);
break;
}
++it;
}
-
}
bool QHttpNetworkReplyPrivate::findChallenge(bool forProxy, QByteArray &challenge) const
diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h
index f3b007f594..faab03f056 100644
--- a/src/network/access/qhttpnetworkreply_p.h
+++ b/src/network/access/qhttpnetworkreply_p.h
@@ -139,6 +139,7 @@ public:
bool isPipeliningUsed() const;
bool isSpdyUsed() const;
void setSpdyWasUsed(bool spdy);
+ qint64 removedContentLength() const;
bool isRedirecting() const;
@@ -255,6 +256,7 @@ public:
qint32 currentlyReceivedDataInWindow; // only for SPDY
qint32 currentlyUploadedDataInWindow; // only for SPDY
qint64 totallyUploadedData; // only for SPDY
+ qint64 removedContentLength;
QPointer<QHttpNetworkConnection> connection;
QPointer<QHttpNetworkConnectionChannel> connectionChannel;
diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp
index 802043d847..7862d464e1 100644
--- a/src/network/access/qhttpnetworkrequest.cpp
+++ b/src/network/access/qhttpnetworkrequest.cpp
@@ -48,7 +48,8 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(QHttpNetworkRequest::Oper
QHttpNetworkRequest::Priority pri, const QUrl &newUrl)
: QHttpNetworkHeaderPrivate(newUrl), operation(op), priority(pri), uploadByteDevice(0),
autoDecompress(false), pipeliningAllowed(false), spdyAllowed(false), http2Allowed(false),
- withCredentials(true), preConnect(false), followRedirect(false), redirectCount(0)
+ withCredentials(true), preConnect(false), redirectCount(0),
+ redirectsPolicy(QNetworkRequest::ManualRedirectsPolicy)
{
}
@@ -65,8 +66,8 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest
withCredentials(other.withCredentials),
ssl(other.ssl),
preConnect(other.preConnect),
- followRedirect(other.followRedirect),
- redirectCount(other.redirectCount)
+ redirectCount(other.redirectCount),
+ redirectsPolicy(other.redirectsPolicy)
{
}
@@ -88,7 +89,8 @@ bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &ot
&& (operation != QHttpNetworkRequest::Custom || (customVerb == other.customVerb))
&& (withCredentials == other.withCredentials)
&& (ssl == other.ssl)
- && (preConnect == other.preConnect);
+ && (preConnect == other.preConnect)
+ && (redirectsPolicy == other.redirectsPolicy);
}
QByteArray QHttpNetworkRequest::methodName() const
@@ -229,12 +231,17 @@ void QHttpNetworkRequest::setPreConnect(bool preConnect)
bool QHttpNetworkRequest::isFollowRedirects() const
{
- return d->followRedirect;
+ return d->redirectsPolicy != QNetworkRequest::ManualRedirectsPolicy;
}
-void QHttpNetworkRequest::setFollowRedirects(bool followRedirect)
+void QHttpNetworkRequest::setRedirectsPolicy(QNetworkRequest::RedirectsPolicy policy)
{
- d->followRedirect = followRedirect;
+ d->redirectsPolicy = policy;
+}
+
+QNetworkRequest::RedirectsPolicy QHttpNetworkRequest::redirectsPolicy() const
+{
+ return d->redirectsPolicy;
}
int QHttpNetworkRequest::redirectCount() const
diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h
index d1abb76e28..3900e9080e 100644
--- a/src/network/access/qhttpnetworkrequest_p.h
+++ b/src/network/access/qhttpnetworkrequest_p.h
@@ -55,6 +55,7 @@
#ifndef QT_NO_HTTP
#include <private/qhttpnetworkheader_p.h>
+#include <QtNetwork/qnetworkrequest.h>
#include <qmetatype.h>
QT_BEGIN_NAMESPACE
@@ -130,7 +131,8 @@ public:
void setPreConnect(bool preConnect);
bool isFollowRedirects() const;
- void setFollowRedirects(bool followRedirect);
+ void setRedirectsPolicy(QNetworkRequest::RedirectsPolicy policy);
+ QNetworkRequest::RedirectsPolicy redirectsPolicy() const;
int redirectCount() const;
void setRedirectCount(int count);
@@ -173,8 +175,8 @@ public:
bool withCredentials;
bool ssl;
bool preConnect;
- bool followRedirect;
int redirectCount;
+ QNetworkRequest::RedirectsPolicy redirectsPolicy;
};
diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp
index 1dca7f02fb..6e5e29d7bf 100644
--- a/src/network/access/qhttpthreaddelegate.cpp
+++ b/src/network/access/qhttpthreaddelegate.cpp
@@ -67,7 +67,7 @@ static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const
break;
case 403: // Access denied
- code = QNetworkReply::ContentOperationNotPermittedError;
+ code = QNetworkReply::ContentAccessDenied;
break;
case 404: // Not Found
@@ -234,6 +234,7 @@ QHttpThreadDelegate::QHttpThreadDelegate(QObject *parent) :
, isPipeliningUsed(false)
, isSpdyUsed(false)
, incomingContentLength(-1)
+ , removedContentLength(-1)
, incomingErrorCode(QNetworkReply::NoError)
, downloadBuffer()
, httpConnection(0)
@@ -623,6 +624,7 @@ void QHttpThreadDelegate::headerChangedSlot()
incomingReasonPhrase = httpReply->reasonPhrase();
isPipeliningUsed = httpReply->isPipeliningUsed();
incomingContentLength = httpReply->contentLength();
+ removedContentLength = httpReply->removedContentLength();
isSpdyUsed = httpReply->isSpdyUsed();
emit downloadMetaData(incomingHeaders,
@@ -631,6 +633,7 @@ void QHttpThreadDelegate::headerChangedSlot()
isPipeliningUsed,
downloadBuffer,
incomingContentLength,
+ removedContentLength,
isSpdyUsed);
}
diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h
index 64c58cf648..6d1ea11f29 100644
--- a/src/network/access/qhttpthreaddelegate_p.h
+++ b/src/network/access/qhttpthreaddelegate_p.h
@@ -112,6 +112,7 @@ public:
bool isPipeliningUsed;
bool isSpdyUsed;
qint64 incomingContentLength;
+ qint64 removedContentLength;
QNetworkReply::NetworkError incomingErrorCode;
QString incomingErrorDetail;
#ifndef QT_NO_BEARERMANAGEMENT
@@ -141,7 +142,7 @@ signals:
void preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *);
#endif
void downloadMetaData(const QList<QPair<QByteArray,QByteArray> > &, int, const QString &, bool,
- QSharedPointer<char>, qint64, bool);
+ QSharedPointer<char>, qint64, qint64, bool);
void downloadProgress(qint64, qint64);
void downloadData(const QByteArray &);
void error(QNetworkReply::NetworkError, const QString &);
diff --git a/src/network/access/qnetworkaccessbackend_p.h b/src/network/access/qnetworkaccessbackend_p.h
index 7f39c942a3..4b5422ce29 100644
--- a/src/network/access/qnetworkaccessbackend_p.h
+++ b/src/network/access/qnetworkaccessbackend_p.h
@@ -63,7 +63,6 @@ class QNetworkProxyQuery;
class QNetworkRequest;
class QStringList;
class QUrl;
-class QUrlInfo;
class QSslConfiguration;
class QNetworkAccessManagerPrivate;
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index 6d5b2400f1..19e9ecc265 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -45,6 +45,8 @@
#include "qnetworkcookie.h"
#include "qnetworkcookiejar.h"
#include "qabstractnetworkcache.h"
+#include "qhstspolicy.h"
+#include "qhsts_p.h"
#include "QtNetwork/qnetworksession.h"
#include "QtNetwork/private/qsharednetworksession_p.h"
@@ -693,6 +695,94 @@ void QNetworkAccessManager::setCookieJar(QNetworkCookieJar *cookieJar)
}
/*!
+ \since 5.9
+
+ Enables HTTP Strict Transport Security (HSTS, RFC6797). When processing a
+ request, QNetworkAccessManager automatically replaces "http" scheme with
+ "https" and uses a secure transport if a host is a known HSTS host.
+ Port 80 if it's set explicitly is replaced by port 443.
+
+ When HSTS is enabled, for each HTTP response containing HSTS header and
+ received over a secure transport, QNetworkAccessManager will update its HSTS
+ cache, either remembering a host with a valid policy or removing a host with
+ expired/disabled HSTS policy.
+
+ \sa disableStrictTransportSecurity(), strictTransportSecurityEnabled()
+*/
+void QNetworkAccessManager::enableStrictTransportSecurity()
+{
+ Q_D(QNetworkAccessManager);
+ d->stsEnabled = true;
+}
+
+/*!
+ \since 5.9
+
+ Disables HTTP Strict Transport Security (HSTS). HSTS headers in responses would
+ be ignored, no scheme/port mapping is done.
+
+ \sa enableStrictTransportSecurity()
+*/
+void QNetworkAccessManager::disableStrictTransportSecurity()
+{
+ Q_D(QNetworkAccessManager);
+ d->stsEnabled = false;
+}
+
+/*!
+ \since 5.9
+
+ Returns true if HTTP Strict Transport Security (HSTS) was enabled. By default
+ HSTS is disabled.
+
+ \sa enableStrictTransportSecurity
+*/
+bool QNetworkAccessManager::strictTransportSecurityEnabled() const
+{
+ Q_D(const QNetworkAccessManager);
+ return d->stsEnabled;
+}
+
+/*!
+ \since 5.9
+
+ Adds HTTP Strict Transport Security policies into HSTS cache.
+
+ \note An expired policy will remove a known host from the cache, if previously
+ present.
+
+ \note While processing HTTP responses, QNetworkAccessManager can also update
+ the HSTS cache, removing or updating exitsting policies or introducing new
+ known hosts. The current implementation thus is server-driven, client code
+ can provide QNetworkAccessManager with previously known or discovered
+ policies, but this information can be overridden by "Strict-Transport-Security"
+ response headers.
+
+ \sa addStrictTransportSecurityHosts(), QHstsPolicy
+*/
+
+void QNetworkAccessManager::addStrictTransportSecurityHosts(const QList<QHstsPolicy> &knownHosts)
+{
+ Q_D(QNetworkAccessManager);
+ d->stsCache.updateFromPolicies(knownHosts);
+}
+
+/*!
+ \since 5.9
+
+ Returns the list of HTTP Strict Transport Security policies. This list can
+ differ from what was initially set via addStrictTransportSecurityHosts() if
+ HSTS cache was updated from a "Strict-Transport-Security" response header.
+
+ \sa addStrictTransportSecurityHosts(), QHstsPolicy
+*/
+QList<QHstsPolicy> QNetworkAccessManager::strictTransportSecurityHosts() const
+{
+ Q_D(const QNetworkAccessManager);
+ return d->stsCache.policies();
+}
+
+/*!
Posts a request to obtain the network headers for \a request
and returns a new QNetworkReply object which will contain such headers.
@@ -1079,6 +1169,45 @@ void QNetworkAccessManager::connectToHost(const QString &hostName, quint16 port)
}
/*!
+ \since 5.9
+
+ Sets the manager's redirects policy to be the \a policy specified. This policy
+ will affect all subsequent requests created by the manager.
+
+ Use this function to enable or disable HTTP redirects on the manager's level.
+
+ \note When creating a request QNetworkRequest::RedirectAttributePolicy has
+ the highest priority, next by priority is QNetworkRequest::FollowRedirectsAttribute.
+ Finally, the manager's policy has the lowest priority.
+
+ For backwards compatibility the default value is QNetworkRequest::ManualRedirectsPolicy.
+ This may change in the future and some type of auto-redirect policy will become
+ the default; clients relying on manual redirect handling are encouraged to set
+ this policy explicitly in their code.
+
+ \sa redirectsPolicy(), QNetworkRequest::RedirectsPolicy,
+ QNetworkRequest::FollowRedirectsAttribute
+*/
+void QNetworkAccessManager::setRedirectsPolicy(QNetworkRequest::RedirectsPolicy policy)
+{
+ Q_D(QNetworkAccessManager);
+ d->redirectsPolicy = policy;
+}
+
+/*!
+ \since 5.9
+
+ Returns the redirect policy that is used when creating new requests.
+
+ \sa setRedirectsPolicy(), QNetworkRequest::RedirectsPolicy
+*/
+QNetworkRequest::RedirectsPolicy QNetworkAccessManager::redirectsPolicy() const
+{
+ Q_D(const QNetworkAccessManager);
+ return d->redirectsPolicy;
+}
+
+/*!
\since 4.7
Sends a custom request to the server identified by the URL of \a request.
@@ -1147,9 +1276,9 @@ QNetworkReply *QNetworkAccessManager::sendCustomRequest(const QNetworkRequest &r
/*!
Returns a new QNetworkReply object to handle the operation \a op
- and request \a req. The device \a outgoingData is always 0 for Get and
- Head requests, but is the value passed to post() and put() in
- those operations (the QByteArray variants will pass a QBuffer
+ and request \a originalReq. The device \a outgoingData is always 0
+ for Get and Head requests, but is the value passed to post() and
+ put() in those operations (the QByteArray variants will pass a QBuffer
object).
The default implementation calls QNetworkCookieJar::cookiesForUrl()
@@ -1159,11 +1288,20 @@ QNetworkReply *QNetworkAccessManager::sendCustomRequest(const QNetworkRequest &r
The returned object must be in an open state.
*/
QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Operation op,
- const QNetworkRequest &req,
+ const QNetworkRequest &originalReq,
QIODevice *outgoingData)
{
Q_D(QNetworkAccessManager);
+ QNetworkRequest req(originalReq);
+ if (req.attribute(QNetworkRequest::RedirectsPolicyAttribute).isNull()
+ && req.attribute(QNetworkRequest::FollowRedirectsAttribute).isNull()) {
+ // We only apply the general manager's policy if:
+ // - RedirectsPolicyAttribute is not set already on request and
+ // - no FollowRedirectsAttribute is set.
+ req.setAttribute(QNetworkRequest::RedirectsPolicyAttribute, redirectsPolicy());
+ }
+
bool isLocalFile = req.url().isLocalFile();
QString scheme = req.url().scheme();
@@ -1251,6 +1389,24 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
|| scheme == QLatin1String("https") || scheme == QLatin1String("preconnect-https")
#endif
) {
+#ifndef QT_NO_SSL
+ if (strictTransportSecurityEnabled() && d->stsCache.isKnownHost(request.url())) {
+ QUrl stsUrl(request.url());
+ // RFC6797, 8.3:
+ // The UA MUST replace the URI scheme with "https" [RFC2818],
+ // and if the URI contains an explicit port component of "80",
+ // then the UA MUST convert the port component to be "443", or
+ // if the URI contains an explicit port component that is not
+ // equal to "80", the port component value MUST be preserved;
+ // otherwise,
+ // if the URI does not contain an explicit port component, the UA
+ // MUST NOT add one.
+ if (stsUrl.port() == 80)
+ stsUrl.setPort(443);
+ stsUrl.setScheme(QLatin1String("https"));
+ request.setUrl(stsUrl);
+ }
+#endif
QNetworkReplyHttpImpl *reply = new QNetworkReplyHttpImpl(this, request, op, outgoingData);
#ifndef QT_NO_BEARERMANAGEMENT
connect(this, SIGNAL(networkSessionConnected()),
@@ -1352,10 +1508,26 @@ QStringList QNetworkAccessManager::supportedSchemesImplementation() const
This function is useful for doing auto tests.
+ \sa clearConnectionCache()
*/
void QNetworkAccessManager::clearAccessCache()
{
- QNetworkAccessManagerPrivate::clearCache(this);
+ QNetworkAccessManagerPrivate::clearAuthenticationCache(this);
+ QNetworkAccessManagerPrivate::clearConnectionCache(this);
+}
+
+/*!
+ \since 5.9
+
+ Flushes the internal cache of network connections.
+ In contrast to clearAccessCache() the authentication data
+ is preserved.
+
+ \sa clearAccessCache()
+*/
+void QNetworkAccessManager::clearConnectionCache()
+{
+ QNetworkAccessManagerPrivate::clearConnectionCache(this);
}
void QNetworkAccessManagerPrivate::_q_replyFinished()
@@ -1552,11 +1724,14 @@ QList<QNetworkProxy> QNetworkAccessManagerPrivate::queryProxy(const QNetworkProx
}
#endif
-void QNetworkAccessManagerPrivate::clearCache(QNetworkAccessManager *manager)
+void QNetworkAccessManagerPrivate::clearAuthenticationCache(QNetworkAccessManager *manager)
{
- manager->d_func()->objectCache.clear();
manager->d_func()->authenticationManager->clearCache();
+}
+void QNetworkAccessManagerPrivate::clearConnectionCache(QNetworkAccessManager *manager)
+{
+ manager->d_func()->objectCache.clear();
manager->d_func()->destroyThread();
}
diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h
index 4b8c4ddf0e..52769627f3 100644
--- a/src/network/access/qnetworkaccessmanager.h
+++ b/src/network/access/qnetworkaccessmanager.h
@@ -41,6 +41,7 @@
#define QNETWORKACCESSMANAGER_H
#include <QtNetwork/qtnetworkglobal.h>
+#include <QtNetwork/qnetworkrequest.h>
#include <QtCore/QObject>
#ifndef QT_NO_SSL
#include <QtNetwork/QSslConfiguration>
@@ -49,7 +50,6 @@
QT_BEGIN_NAMESPACE
-
class QIODevice;
class QAbstractNetworkCache;
class QAuthenticator;
@@ -57,11 +57,11 @@ class QByteArray;
template<typename T> class QList;
class QNetworkCookie;
class QNetworkCookieJar;
-class QNetworkRequest;
class QNetworkReply;
class QNetworkProxy;
class QNetworkProxyFactory;
class QSslError;
+class QHstsPolicy;
#ifndef QT_NO_BEARERMANAGEMENT
class QNetworkConfiguration;
#endif
@@ -106,6 +106,8 @@ public:
void clearAccessCache();
+ void clearConnectionCache();
+
#ifndef QT_NO_NETWORKPROXY
QNetworkProxy proxy() const;
void setProxy(const QNetworkProxy &proxy);
@@ -119,6 +121,12 @@ public:
QNetworkCookieJar *cookieJar() const;
void setCookieJar(QNetworkCookieJar *cookieJar);
+ void enableStrictTransportSecurity();
+ void disableStrictTransportSecurity();
+ bool strictTransportSecurityEnabled() const;
+ void addStrictTransportSecurityHosts(const QList<QHstsPolicy> &knownHosts);
+ QList<QHstsPolicy> strictTransportSecurityHosts() const;
+
QNetworkReply *head(const QNetworkRequest &request);
QNetworkReply *get(const QNetworkRequest &request);
QNetworkReply *post(const QNetworkRequest &request, QIODevice *data);
@@ -147,6 +155,9 @@ public:
#endif
void connectToHost(const QString &hostName, quint16 port = 80);
+ void setRedirectsPolicy(QNetworkRequest::RedirectsPolicy policy);
+ QNetworkRequest::RedirectsPolicy redirectsPolicy() const;
+
Q_SIGNALS:
#ifndef QT_NO_NETWORKPROXY
void proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator);
diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h
index bb4641ab8b..cafc93871a 100644
--- a/src/network/access/qnetworkaccessmanager_p.h
+++ b/src/network/access/qnetworkaccessmanager_p.h
@@ -55,6 +55,8 @@
#include "qnetworkaccessmanager.h"
#include "qnetworkaccesscache_p.h"
#include "qnetworkaccessbackend_p.h"
+#include "qnetworkrequest.h"
+#include "qhsts_p.h"
#include "private/qobject_p.h"
#include "QtNetwork/qnetworkproxy.h"
#include "QtNetwork/qnetworksession.h"
@@ -91,6 +93,7 @@ public:
#endif
cookieJarCreated(false),
defaultAccessControl(true),
+ redirectsPolicy(QNetworkRequest::ManualRedirectsPolicy),
authenticationManager(QSharedPointer<QNetworkAccessAuthenticationManager>::create())
{
#ifndef QT_NO_BEARERMANAGEMENT
@@ -193,6 +196,7 @@ public:
bool cookieJarCreated;
bool defaultAccessControl;
+ QNetworkRequest::RedirectsPolicy redirectsPolicy;
// The cache with authorization data:
QSharedPointer<QNetworkAccessAuthenticationManager> authenticationManager;
@@ -202,7 +206,13 @@ public:
QNetworkAccessCache objectCache;
static inline QNetworkAccessCache *getObjectCache(QNetworkAccessBackend *backend)
{ return &backend->manager->objectCache; }
- Q_AUTOTEST_EXPORT static void clearCache(QNetworkAccessManager *manager);
+
+ Q_AUTOTEST_EXPORT static void clearAuthenticationCache(QNetworkAccessManager *manager);
+ Q_AUTOTEST_EXPORT static void clearConnectionCache(QNetworkAccessManager *manager);
+
+ QHstsCache stsCache;
+ bool stsEnabled = false;
+
#ifndef QT_NO_BEARERMANAGEMENT
Q_AUTOTEST_EXPORT static const QWeakPointer<const QNetworkSession> getNetworkSession(const QNetworkAccessManager *manager);
#endif
diff --git a/src/network/access/qnetworkdiskcache.cpp b/src/network/access/qnetworkdiskcache.cpp
index ce3b773c64..d72791c1f0 100644
--- a/src/network/access/qnetworkdiskcache.cpp
+++ b/src/network/access/qnetworkdiskcache.cpp
@@ -606,7 +606,7 @@ QString QNetworkDiskCachePrivate::uniqueFileName(const QUrl &url)
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(cleanUrl.toEncoded());
// convert sha1 to base36 form and return first 8 bytes for use as string
- QByteArray id = QByteArray::number(*(qlonglong*)hash.result().data(), 36).left(8);
+ const QByteArray id = QByteArray::number(*(qlonglong*)hash.result().constData(), 36).left(8);
// generates <one-char subdir>/<8-char filname.d>
uint code = (uint)id.at(id.length()-1) % 16;
QString pathFragment = QString::number(code, 16) + QLatin1Char('/')
diff --git a/src/network/access/qnetworkreply.cpp b/src/network/access/qnetworkreply.cpp
index 3f17b68e79..ca27e66791 100644
--- a/src/network/access/qnetworkreply.cpp
+++ b/src/network/access/qnetworkreply.cpp
@@ -166,7 +166,7 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
any credentials offered (if any)
\value ContentAccessDenied the access to the remote
- content was denied (similar to HTTP error 401)
+ content was denied (similar to HTTP error 403)
\value ContentOperationNotPermittedError the operation requested
on the remote content is not permitted
@@ -305,6 +305,20 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
*/
/*!
+ \fn void QNetworkReply::redirectAllowed()
+ \since 5.9
+
+ 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.
+
+ \sa QNetworkRequest::UserVerifiedRedirectsPolicy
+ QNetworkAccessManager::setRedirectsPolicy(),
+ QNetworkRequest::RedirectsPolicyAttribute
+*/
+
+/*!
\fn void QNetworkReply::metaDataChanged()
\omit FIXME: Update name? \endomit
@@ -718,7 +732,11 @@ void QNetworkReply::setSslConfiguration(const QSslConfiguration &config)
You can clear the list of errors you want to ignore by calling this
function with an empty list.
- \sa sslConfiguration(), sslErrors(), QSslSocket::ignoreSslErrors()
+ \note If HTTP Strict Transport Security is enabled for QNetworkAccessManager,
+ this function has no effect.
+
+ \sa sslConfiguration(), sslErrors(), QSslSocket::ignoreSslErrors(),
+ QNetworkAccessManager::enableStrictTransportSecurity()
*/
void QNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
{
@@ -785,6 +803,9 @@ void QNetworkReply::ignoreSslErrorsImplementation(const QList<QSslError> &)
sslErrors() signal, which indicates which errors were
found.
+ \note If HTTP Strict Transport Security is enabled for QNetworkAccessManager,
+ this function has no effect.
+
\sa sslConfiguration(), sslErrors(), QSslSocket::ignoreSslErrors()
*/
void QNetworkReply::ignoreSslErrors()
diff --git a/src/network/access/qnetworkreply.h b/src/network/access/qnetworkreply.h
index 1419db8597..d858e07d84 100644
--- a/src/network/access/qnetworkreply.h
+++ b/src/network/access/qnetworkreply.h
@@ -163,6 +163,7 @@ Q_SIGNALS:
void preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *authenticator);
#endif
void redirected(const QUrl &url);
+ void redirectAllowed();
void uploadProgress(qint64 bytesSent, qint64 bytesTotal);
void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
index 6f5e68d9c2..ece08acc6b 100644
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
@@ -52,6 +52,7 @@
#include "QtCore/qelapsedtimer.h"
#include "QtNetwork/qsslconfiguration.h"
#include "qhttpthreaddelegate_p.h"
+#include "qhsts_p.h"
#include "qthread.h"
#include "QtCore/qcoreapplication.h"
@@ -384,6 +385,12 @@ void QNetworkReplyHttpImpl::ignoreSslErrors()
{
Q_D(QNetworkReplyHttpImpl);
+ if (d->managerPrivate && d->managerPrivate->stsEnabled
+ && d->managerPrivate->stsCache.isKnownHost(url())) {
+ // We cannot ignore any Security Transport-related errors for this host.
+ return;
+ }
+
d->pendingIgnoreAllSslErrors = true;
}
@@ -391,6 +398,12 @@ void QNetworkReplyHttpImpl::ignoreSslErrorsImplementation(const QList<QSslError>
{
Q_D(QNetworkReplyHttpImpl);
+ if (d->managerPrivate && d->managerPrivate->stsEnabled
+ && d->managerPrivate->stsCache.isKnownHost(url())) {
+ // We cannot ignore any Security Transport-related errors for this host.
+ return;
+ }
+
// the pending list is set if QNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
// is called before QNetworkAccessManager::get() (or post(), etc.)
d->pendingIgnoreSslErrorsList = errors;
@@ -655,8 +668,14 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
}
#endif
- if (newHttpRequest.attribute(QNetworkRequest::FollowRedirectsAttribute).toBool())
- httpRequest.setFollowRedirects(true);
+ auto redirectsPolicy = QNetworkRequest::ManualRedirectsPolicy;
+ const QVariant value = newHttpRequest.attribute(QNetworkRequest::RedirectsPolicyAttribute);
+ if (value.isValid())
+ redirectsPolicy = value.value<QNetworkRequest::RedirectsPolicy>();
+ else if (newHttpRequest.attribute(QNetworkRequest::FollowRedirectsAttribute).toBool())
+ redirectsPolicy = QNetworkRequest::NoLessSafeRedirectsPolicy;
+
+ httpRequest.setRedirectsPolicy(redirectsPolicy);
httpRequest.setPriority(convert(newHttpRequest.priority()));
@@ -803,10 +822,11 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
Qt::QueuedConnection);
QObject::connect(delegate, SIGNAL(downloadMetaData(QList<QPair<QByteArray,QByteArray> >,
int, QString, bool,
- QSharedPointer<char>, qint64, bool)),
+ QSharedPointer<char>, qint64, qint64,
+ bool)),
q, SLOT(replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,
int, QString, bool,
- QSharedPointer<char>, qint64, bool)),
+ QSharedPointer<char>, qint64, qint64, bool)),
Qt::QueuedConnection);
QObject::connect(delegate, SIGNAL(downloadProgress(qint64,qint64)),
q, SLOT(replyDownloadProgressSlot(qint64,qint64)),
@@ -817,12 +837,16 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
QObject::connect(delegate, SIGNAL(redirected(QUrl,int,int)),
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)),
Qt::QueuedConnection);
#endif
- // Those need to report back, therefire BlockingQueuedConnection
+ // Those need to report back, therefore BlockingQueuedConnection
QObject::connect(delegate, SIGNAL(authenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
q, SLOT(httpAuthenticationRequired(QHttpNetworkRequest,QAuthenticator*)),
Qt::BlockingQueuedConnection);
@@ -911,6 +935,7 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
delegate->isPipeliningUsed,
QSharedPointer<char>(),
delegate->incomingContentLength,
+ delegate->removedContentLength,
delegate->isSpdyUsed);
replyDownloadData(delegate->synchronousDownloadData);
httpError(delegate->incomingErrorCode, delegate->incomingErrorDetail);
@@ -922,6 +947,7 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
delegate->isPipeliningUsed,
QSharedPointer<char>(),
delegate->incomingContentLength,
+ delegate->removedContentLength,
delegate->isSpdyUsed);
replyDownloadData(delegate->synchronousDownloadData);
}
@@ -1109,22 +1135,53 @@ void QNetworkReplyHttpImplPrivate::onRedirected(const QUrl &redirectUrl, int htt
if (isFinished)
return;
+ const QString schemeBefore(url.scheme());
if (httpRequest.isFollowRedirects()) // update the reply's url as it could've changed
url = redirectUrl;
- QNetworkRequest redirectRequest = createRedirectRequest(originalRequest, redirectUrl, maxRedirectsRemaining);
+ if (managerPrivate->stsEnabled && managerPrivate->stsCache.isKnownHost(url)) {
+ // RFC6797, 8.3:
+ // The UA MUST replace the URI scheme with "https" [RFC2818],
+ // and if the URI contains an explicit port component of "80",
+ // then the UA MUST convert the port component to be "443", or
+ // if the URI contains an explicit port component that is not
+ // equal to "80", the port component value MUST be preserved;
+ // otherwise, if the URI does not contain an explicit port
+ // component, the UA MUST NOT add one.
+ url.setScheme(QLatin1String("https"));
+ if (url.port() == 80)
+ url.setPort(443);
+ }
+
+ const bool isLessSafe = schemeBefore == QLatin1String("https")
+ && url.scheme() == QLatin1String("http");
+ if (httpRequest.redirectsPolicy() == QNetworkRequest::NoLessSafeRedirectsPolicy
+ && isLessSafe) {
+ error(QNetworkReply::InsecureRedirectError,
+ QCoreApplication::translate("QHttp", "Insecure redirect"));
+ return;
+ }
+
+ redirectRequest = createRedirectRequest(originalRequest, url, maxRedirectsRemaining);
operation = getRedirectOperation(operation, httpStatus);
+ if (httpRequest.redirectsPolicy() != QNetworkRequest::UserVerifiedRedirectsPolicy)
+ followRedirect();
+
+ emit q->redirected(url);
+}
+
+void QNetworkReplyHttpImplPrivate::followRedirect()
+{
+ Q_Q(QNetworkReplyHttpImpl);
+
cookedHeaders.clear();
if (managerPrivate->thread)
managerPrivate->thread->disconnect();
- // Recurse
QMetaObject::invokeMethod(q, "start", Qt::QueuedConnection,
Q_ARG(QNetworkRequest, redirectRequest));
-
- emit q->redirected(redirectUrl);
}
void QNetworkReplyHttpImplPrivate::checkForRedirect(const int statusCode)
@@ -1149,7 +1206,9 @@ void QNetworkReplyHttpImplPrivate::checkForRedirect(const int statusCode)
void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QList<QPair<QByteArray,QByteArray> > &hm,
int sc, const QString &rp, bool pu,
QSharedPointer<char> db,
- qint64 contentLength, bool spdyWasUsed)
+ qint64 contentLength,
+ qint64 removedContentLength,
+ bool spdyWasUsed)
{
Q_Q(QNetworkReplyHttpImpl);
Q_UNUSED(contentLength);
@@ -1157,6 +1216,15 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QList<QPair<QByte
statusCode = sc;
reasonPhrase = rp;
+#ifndef QT_NO_SSL
+ // We parse this header only if we're using secure transport:
+ //
+ // RFC6797, 8.1
+ // If an HTTP response is received over insecure transport, the UA MUST
+ // ignore any present STS header field(s).
+ if (url.scheme() == QLatin1String("https") && managerPrivate->stsEnabled)
+ managerPrivate->stsCache.updateFromHeaders(hm, url);
+#endif
// Download buffer
if (!db.isNull()) {
downloadBufferPointer = db;
@@ -1195,6 +1263,8 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QList<QPair<QByte
q->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, statusCode);
q->setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, reasonPhrase);
+ if (removedContentLength != -1)
+ q->setAttribute(QNetworkRequest::OriginalContentLengthAttribute, removedContentLength);
// is it a redirection?
if (!isHttpRedirectResponse())
diff --git a/src/network/access/qnetworkreplyhttpimpl_p.h b/src/network/access/qnetworkreplyhttpimpl_p.h
index 868fa617b6..9383149124 100644
--- a/src/network/access/qnetworkreplyhttpimpl_p.h
+++ b/src/network/access/qnetworkreplyhttpimpl_p.h
@@ -114,7 +114,7 @@ public:
Q_PRIVATE_SLOT(d_func(), void replyFinished())
Q_PRIVATE_SLOT(d_func(), void replyDownloadMetaData(QList<QPair<QByteArray,QByteArray> >,
int, QString, bool, QSharedPointer<char>,
- qint64, bool))
+ qint64, qint64, bool))
Q_PRIVATE_SLOT(d_func(), void replyDownloadProgressSlot(qint64,qint64))
Q_PRIVATE_SLOT(d_func(), void httpAuthenticationRequired(const QHttpNetworkRequest &, QAuthenticator *))
Q_PRIVATE_SLOT(d_func(), void httpError(QNetworkReply::NetworkError, const QString &))
@@ -136,6 +136,7 @@ public:
Q_PRIVATE_SLOT(d_func(), void _q_cacheSaveDeviceAboutToClose())
Q_PRIVATE_SLOT(d_func(), void _q_metaDataChanged())
Q_PRIVATE_SLOT(d_func(), void onRedirected(const QUrl &, int, int))
+ Q_PRIVATE_SLOT(d_func(), void followRedirect())
#ifndef QT_NO_SSL
protected:
@@ -212,6 +213,7 @@ public:
QSharedPointer<QRingBuffer> outgoingDataBuffer;
void emitReplyUploadProgress(qint64 bytesSent, qint64 bytesTotal); // dup?
void onRedirected(const QUrl &redirectUrl, int httpStatus, int maxRedirectsRemainig);
+ void followRedirect();
qint64 bytesUploaded;
@@ -263,6 +265,7 @@ public:
QList<QSslError> pendingIgnoreSslErrorsList;
#endif
+ QNetworkRequest redirectRequest;
bool loadFromCacheIfAllowed(QHttpNetworkRequest &httpRequest);
void invalidateCache();
@@ -280,7 +283,7 @@ public:
void replyDownloadData(QByteArray);
void replyFinished();
void replyDownloadMetaData(const QList<QPair<QByteArray,QByteArray> > &, int, const QString &,
- bool, QSharedPointer<char>, qint64, bool);
+ bool, QSharedPointer<char>, qint64, qint64, bool);
void replyDownloadProgressSlot(qint64,qint64);
void httpAuthenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *auth);
void httpError(QNetworkReply::NetworkError error, const QString &errorString);
diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp
index 29362b81e2..169695fa27 100644
--- a/src/network/access/qnetworkrequest.cpp
+++ b/src/network/access/qnetworkrequest.cpp
@@ -282,6 +282,19 @@ QT_BEGIN_NAMESPACE
that is redirecting from "https" to "http" protocol, are not allowed.
(This value was introduced in 5.6.)
+ \value OriginalContentLengthAttribute
+ Replies only, type QMetaType::Int
+ Holds the original content-length attribute before being invalidated and
+ removed from the header when the data is compressed and the request was
+ marked to be decompressed automatically.
+ (This value was introduced in 5.9.)
+
+ \value RedirectsPolicyAttribute
+ Requests only, type: QMetaType::Int, should be one of the
+ QNetworkRequest::RedirectsPolicy values (default: ManualRedirectsPolicy).
+ This attribute obsoletes FollowRedirectsAttribute.
+ (This value was introduced in 5.9.)
+
\value User
Special type. Additional information can be passed in
QVariants with types ranging from User to UserMax. The default
@@ -329,6 +342,36 @@ QT_BEGIN_NAMESPACE
\value Manual indicates behaviour has been manually overridden.
*/
+/*!
+ \enum QNetworkRequest::RedirectsPolicy
+ \since 5.9
+
+ Indicates whether the Network Access API should automatically follow a
+ HTTP redirect response or not.
+
+ \value ManualRedirectsPolicy Default value: not following any redirects.
+
+ \value NoLessSafeRedirectsPolicy Only "http"->"http", "http" -> "https"
+ or "https" -> "https" redirects are allowed.
+ Equivalent to setting the old FollowRedirectsAttribute
+ to true
+
+ \value SameOriginRedirectsPolicy Require the same protocol, host and port.
+ Note, http://example.com and http://example.com:80
+ will fail with this policy (implicit/explicit ports
+ are considered to be a mismatch).
+
+ \value UserVerifiedRedirectsPolicy Client decides whether to follow each
+ redirect by handling the redirected()
+ signal, emitting redirectAllowed() on
+ the QNetworkReply object to allow
+ the redirect or aborting/finishing it to
+ reject the redirect. This can be used,
+ for example, to ask the user whether to
+ accept the redirect, or to decide
+ based on some app-specific configuration.
+*/
+
class QNetworkRequestPrivate: public QSharedData, public QNetworkHeadersPrivate
{
public:
diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h
index ad8f5bddd9..06c895af5f 100644
--- a/src/network/access/qnetworkrequest.h
+++ b/src/network/access/qnetworkrequest.h
@@ -48,7 +48,6 @@
QT_BEGIN_NAMESPACE
-
class QSslConfiguration;
class QNetworkRequestPrivate;
@@ -91,6 +90,8 @@ public:
FollowRedirectsAttribute,
HTTP2AllowedAttribute,
HTTP2WasUsedAttribute,
+ OriginalContentLengthAttribute,
+ RedirectsPolicyAttribute,
User = 1000,
UserMax = 32767
@@ -112,6 +113,14 @@ public:
LowPriority = 5
};
+ enum RedirectsPolicy {
+ ManualRedirectsPolicy,
+ NoLessSafeRedirectsPolicy,
+ SameOriginRedirectsPolicy,
+ UserVerifiedRedirectsPolicy
+ };
+
+
explicit QNetworkRequest(const QUrl &url = QUrl());
QNetworkRequest(const QNetworkRequest &other);
~QNetworkRequest();
@@ -168,5 +177,6 @@ Q_DECLARE_SHARED(QNetworkRequest)
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QNetworkRequest)
+Q_DECLARE_METATYPE(QNetworkRequest::RedirectsPolicy)
#endif
diff --git a/src/network/bearer/qnetworkconfiguration.cpp b/src/network/bearer/qnetworkconfiguration.cpp
index 533a27357c..f1619ab7c0 100644
--- a/src/network/bearer/qnetworkconfiguration.cpp
+++ b/src/network/bearer/qnetworkconfiguration.cpp
@@ -326,6 +326,44 @@ bool QNetworkConfiguration::isValid() const
}
/*!
+ \since 5.9
+
+ Returns the connect timeout of this configuration.
+
+ \sa setConnectTimeout
+*/
+int QNetworkConfiguration::connectTimeout() const
+{
+ if (!d)
+ return QNetworkConfigurationPrivate::DefaultTimeout;
+ QMutexLocker locker(&d->mutex);
+ return d->timeout;
+}
+
+/*!
+ \since 5.9
+
+ Sets the connect timeout of this configuration to \a timeout.
+ This allows control of the timeout used by \c QAbstractSocket
+ to establish a connection.
+
+ \warning This will have no effect if the bearer plugin doesn't have
+ the CanStartAndStopInterfaces capability.
+
+ Returns true if succeeded.
+
+ \sa connectTimeout
+*/
+bool QNetworkConfiguration::setConnectTimeout(int timeout)
+{
+ if (!d)
+ return false;
+ QMutexLocker locker(&d->mutex);
+ d->timeout = timeout;
+ return true;
+}
+
+/*!
Returns the current state of the configuration.
*/
QNetworkConfiguration::StateFlags QNetworkConfiguration::state() const
diff --git a/src/network/bearer/qnetworkconfiguration.h b/src/network/bearer/qnetworkconfiguration.h
index 208f9f4692..e7b74034fc 100644
--- a/src/network/bearer/qnetworkconfiguration.h
+++ b/src/network/bearer/qnetworkconfiguration.h
@@ -120,6 +120,9 @@ public:
QString name() const;
bool isValid() const;
+ int connectTimeout() const;
+ bool setConnectTimeout(int timeout);
+
private:
friend class QNetworkConfigurationPrivate;
friend class QNetworkConfigurationManager;
diff --git a/src/network/bearer/qnetworkconfiguration_p.h b/src/network/bearer/qnetworkconfiguration_p.h
index 12d9676b59..2fdb490ea0 100644
--- a/src/network/bearer/qnetworkconfiguration_p.h
+++ b/src/network/bearer/qnetworkconfiguration_p.h
@@ -69,7 +69,8 @@ public:
type(QNetworkConfiguration::Invalid),
purpose(QNetworkConfiguration::UnknownPurpose),
bearerType(QNetworkConfiguration::BearerUnknown),
- isValid(false), roamingSupported(false)
+ isValid(false), roamingSupported(false),
+ timeout(DefaultTimeout)
{}
virtual ~QNetworkConfigurationPrivate()
{
@@ -91,6 +92,9 @@ public:
bool isValid;
bool roamingSupported;
+ int timeout;
+
+ static Q_CONSTEXPR int DefaultTimeout = 30000;
private:
Q_DISABLE_COPY(QNetworkConfigurationPrivate)
diff --git a/src/network/kernel/kernel.pri b/src/network/kernel/kernel.pri
index 005f000c25..a80b2d387e 100644
--- a/src/network/kernel/kernel.pri
+++ b/src/network/kernel/kernel.pri
@@ -56,8 +56,7 @@ win32: {
mac {
LIBS_PRIVATE += -framework CoreFoundation
- !uikit: LIBS_PRIVATE += -framework CoreServices
- !if(watchos:CONFIG(device, simulator|device)): LIBS_PRIVATE += -framework SystemConfiguration
+ !uikit: LIBS_PRIVATE += -framework CoreServices -framework SystemConfiguration
}
osx:SOURCES += kernel/qnetworkproxy_mac.cpp
diff --git a/src/network/kernel/qdnslookup.cpp b/src/network/kernel/qdnslookup.cpp
index 02df00a590..6203ba37b3 100644
--- a/src/network/kernel/qdnslookup.cpp
+++ b/src/network/kernel/qdnslookup.cpp
@@ -58,7 +58,7 @@ static bool qt_qdnsmailexchangerecord_less_than(const QDnsMailExchangeRecord &r1
return r1.preference() < r2.preference();
}
-/*!
+/*
Sorts a list of QDnsMailExchangeRecord objects according to RFC 5321.
*/
@@ -100,7 +100,7 @@ static bool qt_qdnsservicerecord_less_than(const QDnsServiceRecord &r1, const QD
&& r1.weight() == 0 && r2.weight() > 0);
}
-/*!
+/*
Sorts a list of QDnsServiceRecord objects according to RFC 2782.
*/
diff --git a/src/network/kernel/qhostaddress.cpp b/src/network/kernel/qhostaddress.cpp
index fc753204a9..b8c0584a62 100644
--- a/src/network/kernel/qhostaddress.cpp
+++ b/src/network/kernel/qhostaddress.cpp
@@ -63,12 +63,6 @@
QT_BEGIN_NAMESPACE
-#define QT_ENSURE_PARSED(a) \
- do { \
- if (!(a)->d->isParsed) \
- (a)->d->parse(); \
- } while (0)
-
#ifdef Q_OS_WIN
// sockaddr_in6 size changed between old and new SDK
// Only the new version is the correct one, so always
@@ -100,7 +94,7 @@ typedef struct {
#endif
-class QHostAddressPrivate
+class QHostAddressPrivate : public QSharedData
{
public:
QHostAddressPrivate();
@@ -109,10 +103,9 @@ public:
void setAddress(const quint8 *a_);
void setAddress(const Q_IPV6ADDR &a_);
- bool parse();
+ bool parse(const QString &ipString);
void clear();
- QString ipString;
QString scopeId;
union {
@@ -122,13 +115,12 @@ public:
};
quint32 a; // IPv4 address
qint8 protocol;
- bool isParsed;
friend class QHostAddress;
};
QHostAddressPrivate::QHostAddressPrivate()
- : a(0), protocol(QAbstractSocket::UnknownNetworkLayerProtocol), isParsed(true)
+ : a(0), protocol(QAbstractSocket::UnknownNetworkLayerProtocol)
{
memset(&a6, 0, sizeof(a6));
}
@@ -137,7 +129,6 @@ void QHostAddressPrivate::setAddress(quint32 a_)
{
a = a_;
protocol = QAbstractSocket::IPv4Protocol;
- isParsed = true;
//create mapped address, except for a_ == 0 (any)
a6_64.c[0] = 0;
@@ -187,7 +178,6 @@ static bool convertToIpv4(quint32& a, const Q_IPV6ADDR &a6, const QHostAddress::
void QHostAddressPrivate::setAddress(const quint8 *a_)
{
protocol = QAbstractSocket::IPv6Protocol;
- isParsed = true;
memcpy(a6.c, a_, sizeof(a6));
a = 0;
convertToIpv4(a, a6, (QHostAddress::ConvertV4MappedToIPv4
@@ -201,10 +191,10 @@ void QHostAddressPrivate::setAddress(const Q_IPV6ADDR &a_)
static bool parseIp6(const QString &address, QIPAddressUtils::IPv6Address &addr, QString *scopeId)
{
- QString tmp = address;
+ QStringRef tmp(&address);
int scopeIdPos = tmp.lastIndexOf(QLatin1Char('%'));
if (scopeIdPos != -1) {
- *scopeId = tmp.mid(scopeIdPos + 1);
+ *scopeId = tmp.mid(scopeIdPos + 1).toString();
tmp.chop(tmp.size() - scopeIdPos);
} else {
scopeId->clear();
@@ -212,9 +202,8 @@ static bool parseIp6(const QString &address, QIPAddressUtils::IPv6Address &addr,
return QIPAddressUtils::parseIp6(addr, tmp.constBegin(), tmp.constEnd()) == 0;
}
-Q_NEVER_INLINE bool QHostAddressPrivate::parse()
+bool QHostAddressPrivate::parse(const QString &ipString)
{
- isParsed = true;
protocol = QAbstractSocket::UnknownNetworkLayerProtocol;
QString a = ipString.simplified();
if (a.isEmpty())
@@ -242,13 +231,13 @@ void QHostAddressPrivate::clear()
{
a = 0;
protocol = QAbstractSocket::UnknownNetworkLayerProtocol;
- isParsed = true;
memset(&a6, 0, sizeof(a6));
}
bool QNetmaskAddress::setAddress(const QString &address)
{
+ d.detach();
length = -1;
QHostAddress other;
return other.setAddress(address) && setAddress(other);
@@ -256,6 +245,8 @@ bool QNetmaskAddress::setAddress(const QString &address)
bool QNetmaskAddress::setAddress(const QHostAddress &address)
{
+ d.detach();
+
static const quint8 zeroes[16] = { 0 };
union {
quint32 v4;
@@ -349,6 +340,7 @@ int QNetmaskAddress::prefixLength() const
void QNetmaskAddress::setPrefixLength(QAbstractSocket::NetworkLayerProtocol proto, int newLength)
{
+ d.detach();
length = newLength;
if (length < 0 || length > (proto == QAbstractSocket::IPv4Protocol ? 32 :
proto == QAbstractSocket::IPv6Protocol ? 128 : -1)) {
@@ -377,6 +369,7 @@ void QNetmaskAddress::setPrefixLength(QAbstractSocket::NetworkLayerProtocol prot
\class QHostAddress
\brief The QHostAddress class provides an IP address.
\ingroup network
+ \ingroup shared
\inmodule QtNetwork
This class holds an IPv4 or IPv6 address in a platform- and
@@ -484,8 +477,7 @@ QHostAddress::QHostAddress(const Q_IPV6ADDR &ip6Addr)
QHostAddress::QHostAddress(const QString &address)
: d(new QHostAddressPrivate)
{
- d->ipString = address;
- d->isParsed = false;
+ d->parse(address);
}
/*!
@@ -513,7 +505,7 @@ QHostAddress::QHostAddress(const struct sockaddr *sockaddr)
Constructs a copy of the given \a address.
*/
QHostAddress::QHostAddress(const QHostAddress &address)
- : d(new QHostAddressPrivate(*address.d.data()))
+ : d(address.d)
{
}
@@ -582,7 +574,7 @@ QHostAddress::~QHostAddress()
*/
QHostAddress &QHostAddress::operator=(const QHostAddress &address)
{
- *d.data() = *address.d.data();
+ d = address.d;
return *this;
}
@@ -633,6 +625,7 @@ QHostAddress &QHostAddress::operator=(SpecialAddress address)
*/
void QHostAddress::clear()
{
+ d.detach();
d->clear();
}
@@ -641,6 +634,7 @@ void QHostAddress::clear()
*/
void QHostAddress::setAddress(quint32 ip4Addr)
{
+ d.detach();
d->setAddress(ip4Addr);
}
@@ -654,6 +648,7 @@ void QHostAddress::setAddress(quint32 ip4Addr)
*/
void QHostAddress::setAddress(quint8 *ip6Addr)
{
+ d.detach();
d->setAddress(ip6Addr);
}
@@ -668,6 +663,7 @@ void QHostAddress::setAddress(quint8 *ip6Addr)
*/
void QHostAddress::setAddress(const quint8 *ip6Addr)
{
+ d.detach();
d->setAddress(ip6Addr);
}
@@ -678,6 +674,7 @@ void QHostAddress::setAddress(const quint8 *ip6Addr)
*/
void QHostAddress::setAddress(const Q_IPV6ADDR &ip6Addr)
{
+ d.detach();
d->setAddress(ip6Addr);
}
@@ -691,8 +688,8 @@ void QHostAddress::setAddress(const Q_IPV6ADDR &ip6Addr)
*/
bool QHostAddress::setAddress(const QString &address)
{
- d->ipString = address;
- return d->parse();
+ d.detach();
+ return d->parse(address);
}
/*!
@@ -705,6 +702,7 @@ bool QHostAddress::setAddress(const QString &address)
*/
void QHostAddress::setAddress(const struct sockaddr *sockaddr)
{
+ d.detach();
#ifndef Q_OS_WINRT
clear();
if (sockaddr->sa_family == AF_INET)
@@ -752,7 +750,6 @@ quint32 QHostAddress::toIPv4Address() const
*/
quint32 QHostAddress::toIPv4Address(bool *ok) const
{
- QT_ENSURE_PARSED(this);
quint32 dummy;
if (ok)
*ok = d->protocol == QAbstractSocket::IPv4Protocol || d->protocol == QAbstractSocket::AnyIPProtocol
@@ -767,7 +764,6 @@ quint32 QHostAddress::toIPv4Address(bool *ok) const
*/
QAbstractSocket::NetworkLayerProtocol QHostAddress::protocol() const
{
- QT_ENSURE_PARSED(this);
return QAbstractSocket::NetworkLayerProtocol(d->protocol);
}
@@ -787,7 +783,6 @@ QAbstractSocket::NetworkLayerProtocol QHostAddress::protocol() const
*/
Q_IPV6ADDR QHostAddress::toIPv6Address() const
{
- QT_ENSURE_PARSED(this);
return d->a6;
}
@@ -803,7 +798,6 @@ Q_IPV6ADDR QHostAddress::toIPv6Address() const
*/
QString QHostAddress::toString() const
{
- QT_ENSURE_PARSED(this);
QString s;
if (d->protocol == QAbstractSocket::IPv4Protocol
|| d->protocol == QAbstractSocket::AnyIPProtocol) {
@@ -855,7 +849,6 @@ QString QHostAddress::toString() const
*/
QString QHostAddress::scopeId() const
{
- QT_ENSURE_PARSED(this);
return (d->protocol == QAbstractSocket::IPv6Protocol) ? d->scopeId : QString();
}
@@ -873,7 +866,7 @@ QString QHostAddress::scopeId() const
*/
void QHostAddress::setScopeId(const QString &id)
{
- QT_ENSURE_PARSED(this);
+ d.detach();
if (d->protocol == QAbstractSocket::IPv6Protocol)
d->scopeId = id;
}
@@ -886,7 +879,7 @@ void QHostAddress::setScopeId(const QString &id)
*/
bool QHostAddress::operator==(const QHostAddress &other) const
{
- return isEqual(other, StrictConversion);
+ return d == other.d || isEqual(other, StrictConversion);
}
/*!
@@ -903,8 +896,8 @@ bool QHostAddress::operator==(const QHostAddress &other) const
*/
bool QHostAddress::isEqual(const QHostAddress &other, ConversionMode mode) const
{
- QT_ENSURE_PARSED(this);
- QT_ENSURE_PARSED(&other);
+ if (d == other.d)
+ return true;
if (d->protocol == QAbstractSocket::IPv4Protocol) {
switch (other.d->protocol) {
@@ -956,7 +949,6 @@ bool QHostAddress::isEqual(const QHostAddress &other, ConversionMode mode) const
*/
bool QHostAddress::operator ==(SpecialAddress other) const
{
- QT_ENSURE_PARSED(this);
quint32 ip4 = INADDR_ANY;
switch (other) {
case Null:
@@ -996,7 +988,6 @@ bool QHostAddress::operator ==(SpecialAddress other) const
*/
bool QHostAddress::isNull() const
{
- QT_ENSURE_PARSED(this);
return d->protocol == QAbstractSocket::UnknownNetworkLayerProtocol;
}
@@ -1021,7 +1012,6 @@ bool QHostAddress::isNull() const
*/
bool QHostAddress::isInSubnet(const QHostAddress &subnet, int netmask) const
{
- QT_ENSURE_PARSED(this);
if (subnet.protocol() != d->protocol || netmask < 0)
return false;
@@ -1120,7 +1110,7 @@ QPair<QHostAddress, int> QHostAddress::parseSubnet(const QString &subnet)
return invalid;
int slash = subnet.indexOf(QLatin1Char('/'));
- QString netStr = subnet;
+ QStringRef netStr(&subnet);
if (slash != -1)
netStr.truncate(slash);
@@ -1151,7 +1141,7 @@ QPair<QHostAddress, int> QHostAddress::parseSubnet(const QString &subnet)
netmask = 128;
QHostAddress net;
- if (!net.setAddress(netStr))
+ if (!net.setAddress(netStr.toString()))
return invalid; // failed to parse the IP
clearBits(net.d->a6.c, netmask, 128);
@@ -1162,7 +1152,7 @@ QPair<QHostAddress, int> QHostAddress::parseSubnet(const QString &subnet)
return invalid; // invalid netmask
// parse the address manually
- auto parts = netStr.splitRef(QLatin1Char('.'));
+ auto parts = netStr.split(QLatin1Char('.'));
if (parts.isEmpty() || parts.count() > 4)
return invalid; // invalid IPv4 address
@@ -1204,7 +1194,6 @@ QPair<QHostAddress, int> QHostAddress::parseSubnet(const QString &subnet)
*/
bool QHostAddress::isLoopback() const
{
- QT_ENSURE_PARSED(this);
if ((d->a & 0xFF000000) == 0x7F000000)
return true; // v4 range (including IPv6 wrapped IPv4 addresses)
if (d->protocol == QAbstractSocket::IPv6Protocol) {
@@ -1230,7 +1219,6 @@ bool QHostAddress::isLoopback() const
*/
bool QHostAddress::isMulticast() const
{
- QT_ENSURE_PARSED(this);
if ((d->a & 0xF0000000) == 0xE0000000)
return true; // 224.0.0.0-239.255.255.255 (including v4-mapped IPv6 addresses)
if (d->protocol == QAbstractSocket::IPv6Protocol)
@@ -1256,10 +1244,8 @@ QDebug operator<<(QDebug d, const QHostAddress &address)
\relates QHostAddress
Returns a hash of the host address \a key, using \a seed to seed the calculation.
*/
-uint qHash(const QHostAddress &key, uint seed)
+uint qHash(const QHostAddress &key, uint seed) Q_DECL_NOTHROW
{
- // both lines might throw
- QT_ENSURE_PARSED(&key);
return qHashBits(key.d->a6.c, 16, seed);
}
@@ -1272,6 +1258,18 @@ uint qHash(const QHostAddress &key, uint seed)
\sa isEqual()
*/
+
+/*!
+ \relates QHostAddress
+ \since 5.9
+ \fn operator!=(QHostAddress::SpecialAddress lhs, const QHostAddress &rhs)
+
+ Returns \c false if special address \a lhs is the same as host address \a rhs;
+ otherwise returns \c true.
+
+ \sa isEqual()
+*/
+
#ifndef QT_NO_DATASTREAM
/*! \relates QHostAddress
diff --git a/src/network/kernel/qhostaddress.h b/src/network/kernel/qhostaddress.h
index 10fe33f6fa..fdbdbfc72c 100644
--- a/src/network/kernel/qhostaddress.h
+++ b/src/network/kernel/qhostaddress.h
@@ -44,7 +44,7 @@
#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qpair.h>
#include <QtCore/qstring.h>
-#include <QtCore/qscopedpointer.h>
+#include <QtCore/qshareddata.h>
#include <QtNetwork/qabstractsocket.h>
struct sockaddr;
@@ -66,7 +66,7 @@ typedef QIPv6Address Q_IPV6ADDR;
class QHostAddress;
// qHash is a friend, but we can't use default arguments for friends (ยง8.3.6.4)
-Q_NETWORK_EXPORT uint qHash(const QHostAddress &key, uint seed = 0);
+Q_NETWORK_EXPORT uint qHash(const QHostAddress &key, uint seed = 0) Q_DECL_NOTHROW;
class Q_NETWORK_EXPORT QHostAddress
{
@@ -152,15 +152,17 @@ public:
static QPair<QHostAddress, int> parseSubnet(const QString &subnet);
- friend Q_NETWORK_EXPORT uint qHash(const QHostAddress &key, uint seed);
+ friend Q_NETWORK_EXPORT uint qHash(const QHostAddress &key, uint seed) Q_DECL_NOTHROW;
protected:
- QScopedPointer<QHostAddressPrivate> d;
+ QExplicitlySharedDataPointer<QHostAddressPrivate> d;
};
Q_DECLARE_OPERATORS_FOR_FLAGS(QHostAddress::ConversionMode)
Q_DECLARE_SHARED_NOT_MOVABLE_UNTIL_QT6(QHostAddress)
inline bool operator ==(QHostAddress::SpecialAddress address1, const QHostAddress &address2)
{ return address2 == address1; }
+inline bool operator!=(QHostAddress::SpecialAddress lhs, const QHostAddress &rhs)
+{ return rhs != lhs; }
#ifndef QT_NO_DEBUG_STREAM
Q_NETWORK_EXPORT QDebug operator<<(QDebug, const QHostAddress &);
diff --git a/src/network/kernel/qhostinfo.cpp b/src/network/kernel/qhostinfo.cpp
index 88df65dbcb..193c990d8c 100644
--- a/src/network/kernel/qhostinfo.cpp
+++ b/src/network/kernel/qhostinfo.cpp
@@ -95,6 +95,38 @@ std::pair<OutputIt1, OutputIt2> separate_if(InputIt first, InputIt last, OutputI
}
return std::make_pair(dest1, dest2);
}
+
+int get_signal_index()
+{
+ static auto senderMetaObject = &QHostInfoResult::staticMetaObject;
+ static 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);
+}
+
+void emit_results_ready(const QHostInfo &hostInfo, const QObject *receiver,
+ QtPrivate::QSlotObjectBase *slotObj)
+{
+ static const int signal_index = get_signal_index();
+ auto result = new QHostInfoResult(receiver, slotObj);
+ Q_CHECK_PTR(result);
+ const int nargs = 2;
+ auto types = reinterpret_cast<int *>(malloc(nargs * sizeof(int)));
+ Q_CHECK_PTR(types);
+ types[0] = QMetaType::type("void");
+ types[1] = QMetaType::type("QHostInfo");
+ auto args = reinterpret_cast<void **>(malloc(nargs * sizeof(void *)));
+ Q_CHECK_PTR(args);
+ args[0] = 0;
+ args[1] = QMetaType::create(types[1], &hostInfo);
+ Q_CHECK_PTR(args[1]);
+ auto metaCallEvent = new QMetaCallEvent(slotObj, nullptr, signal_index, nargs, types, args);
+ Q_CHECK_PTR(metaCallEvent);
+ qApp->postEvent(result, metaCallEvent);
+}
+
}
/*!
@@ -243,6 +275,67 @@ int QHostInfo::lookupHost(const QString &name, QObject *receiver,
}
/*!
+ \fn int QHostInfo::lookupHost(const QString &name, const QObject *receiver, PointerToMemberFunction function)
+
+ \since 5.9
+
+ \overload
+
+ Looks up the IP address(es) associated with host name \a name, and
+ returns an ID for the lookup. When the result of the lookup is
+ ready, the slot or signal \a function in \a receiver is called with
+ a QHostInfo argument. The QHostInfo object can then be inspected
+ to get the results of the lookup.
+
+ \note There is no guarantee on the order the signals will be emitted
+ if you start multiple requests with lookupHost().
+
+ \sa abortHostLookup(), addresses(), error(), fromName()
+*/
+
+/*!
+ \fn int QHostInfo::lookupHost(const QString &name, Functor functor)
+
+ \since 5.9
+
+ \overload
+
+ Looks up the IP address(es) associated with host name \a name, and
+ returns an ID for the lookup. When the result of the lookup is
+ ready, the \a functor is called with a QHostInfo argument. The
+ QHostInfo object can then be inspected to get the results of the
+ lookup.
+ \note There is no guarantee on the order the signals will be emitted
+ if you start multiple requests with lookupHost().
+
+ \sa abortHostLookup(), addresses(), error(), fromName()
+*/
+
+/*!
+ \fn int QHostInfo::lookupHost(const QString &name, const QObject *context, Functor functor)
+
+ \since 5.9
+
+ \overload
+
+ Looks up the IP address(es) associated with host name \a name, and
+ returns an ID for the lookup. When the result of the lookup is
+ ready, the \a functor is called with a QHostInfo argument. The
+ QHostInfo object can then be inspected to get the results of the
+ lookup.
+
+ If \a context is destroyed before the lookup completes, the
+ \a functor will not be called. The \a functor will be run in the
+ thread of \a context. The context's thread must have a running Qt
+ event loop.
+
+ \note There is no guarantee on the order the signals will be emitted
+ if you start multiple requests with lookupHost().
+
+ \sa abortHostLookup(), addresses(), error(), fromName()
+*/
+
+/*!
Aborts the host lookup with the ID \a id, as returned by lookupHost().
\sa lookupHost(), lookupId()
@@ -487,11 +580,66 @@ QString QHostInfo::localHostName()
\sa hostName()
*/
+int QHostInfo::lookupHostImpl(const QString &name,
+ const QObject *receiver,
+ QtPrivate::QSlotObjectBase *slotObj)
+{
+#if defined QHOSTINFO_DEBUG
+ qDebug("QHostInfo::lookupHost(\"%s\", %p, %p)",
+ name.toLatin1().constData(), receiver, slotObj);
+#endif
+
+ if (!QAbstractEventDispatcher::instance(QThread::currentThread())) {
+ qWarning("QHostInfo::lookupHost() called with no event dispatcher");
+ return -1;
+ }
+
+ qRegisterMetaType<QHostInfo>();
+
+ int id = theIdCounter.fetchAndAddRelaxed(1); // generate unique ID
+
+ if (Q_UNLIKELY(name.isEmpty())) {
+ QHostInfo hostInfo(id);
+ hostInfo.setError(QHostInfo::HostNotFound);
+ hostInfo.setErrorString(QCoreApplication::translate("QHostInfo", "No host name given"));
+ emit_results_ready(hostInfo, receiver, slotObj);
+ return id;
+ }
+
+ QHostInfoLookupManager *manager = theHostInfoLookupManager();
+
+ if (Q_LIKELY(manager)) {
+ // the application is still alive
+ if (manager->cache.isEnabled()) {
+ // check cache first
+ bool valid = false;
+ QHostInfo info = manager->cache.get(name, &valid);
+ if (valid) {
+ info.setLookupId(id);
+ emit_results_ready(info, receiver, slotObj);
+ return id;
+ }
+ }
+
+ // cache is not enabled or it was not in the cache, do normal lookup
+ QHostInfoRunnable* runnable = new QHostInfoRunnable(name, id, receiver, slotObj);
+ manager->scheduleLookup(runnable);
+ }
+ return id;
+}
+
QHostInfoRunnable::QHostInfoRunnable(const QString &hn, int i) : toBeLookedUp(hn), id(i)
{
setAutoDelete(true);
}
+QHostInfoRunnable::QHostInfoRunnable(const QString &hn, int i, const QObject *receiver,
+ QtPrivate::QSlotObjectBase *slotObj) :
+ toBeLookedUp(hn), id(i), resultEmitter(receiver, slotObj)
+{
+ setAutoDelete(true);
+}
+
// the QHostInfoLookupManager will at some point call this via a QThreadPool
void QHostInfoRunnable::run()
{
diff --git a/src/network/kernel/qhostinfo.h b/src/network/kernel/qhostinfo.h
index 9b4a4853d9..4484d718bd 100644
--- a/src/network/kernel/qhostinfo.h
+++ b/src/network/kernel/qhostinfo.h
@@ -87,8 +87,71 @@ public:
static QString localHostName();
static QString localDomainName();
+#ifdef Q_QDOC
+ template<typename PointerToMemberFunction>
+ static int QHostInfo::lookupHost(const QString &name, const QObject *receiver,
+ PointerToMemberFunction function);
+ template<typename Functor>
+ static int QHostInfo::lookupHost(const QString &name, Functor functor);
+ template<typename Functor>
+ static int QHostInfo::lookupHost(const QString &name, const QObject *context, Functor functor);
+#else
+ // lookupHost to a QObject slot
+ template <typename Func>
+ static inline int lookupHost(const QString &name,
+ const typename QtPrivate::FunctionPointer<Func>::Object *receiver,
+ Func slot)
+ {
+ typedef QtPrivate::FunctionPointer<Func> SlotType;
+
+ typedef QtPrivate::FunctionPointer<void (*)(QHostInfo)> SignalType;
+ Q_STATIC_ASSERT_X(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount),
+ "The slot requires more arguments than the signal provides.");
+ Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments<typename SignalType::Arguments,
+ typename SlotType::Arguments>::value),
+ "Signal and slot arguments are not compatible.");
+ Q_STATIC_ASSERT_X((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);
+ }
+
+ // 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, 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)
+ {
+ typedef QtPrivate::FunctionPointer<Func1> SlotType;
+
+ Q_STATIC_ASSERT_X(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>(slot);
+ return lookupHostImpl(name, context, slotObj);
+ }
+#endif // Q_QDOC
+
private:
QScopedPointer<QHostInfoPrivate> d;
+
+ static int lookupHostImpl(const QString &name,
+ const QObject *receiver,
+ QtPrivate::QSlotObjectBase *slotObj);
};
QT_END_NAMESPACE
diff --git a/src/network/kernel/qhostinfo_p.h b/src/network/kernel/qhostinfo_p.h
index ba342bf533..dd46818a19 100644
--- a/src/network/kernel/qhostinfo_p.h
+++ b/src/network/kernel/qhostinfo_p.h
@@ -54,6 +54,7 @@
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "QtCore/qcoreapplication.h"
#include "private/qcoreapplication_p.h"
+#include "private/qmetaobject_p.h"
#include "QtNetwork/qhostinfo.h"
#include "QtCore/qmutex.h"
#include "QtCore/qwaitcondition.h"
@@ -77,10 +78,47 @@ QT_BEGIN_NAMESPACE
class QHostInfoResult : public QObject
{
Q_OBJECT
+
+ QPointer<const QObject> receiver = nullptr;
+ QtPrivate::QSlotObjectBase *slotObj = nullptr;
+
+public:
+ QHostInfoResult() = default;
+ QHostInfoResult(const QObject *receiver, QtPrivate::QSlotObjectBase *slotObj) :
+ receiver(receiver),
+ slotObj(slotObj)
+ {
+ connect(QCoreApplication::instance(), &QCoreApplication::aboutToQuit, this,
+ &QObject::deleteLater);
+ if (slotObj && receiver)
+ moveToThread(receiver->thread());
+ }
+
public Q_SLOTS:
inline void emitResultsReady(const QHostInfo &info)
{
- emit resultsReady(info);
+ if (slotObj) {
+ QHostInfo copy = info;
+ void *args[2] = { 0, reinterpret_cast<void *>(&copy) };
+ slotObj->call(const_cast<QObject*>(receiver.data()), args);
+ slotObj->destroyIfLastRef();
+ } else {
+ emit resultsReady(info);
+ }
+ }
+
+protected:
+ bool event(QEvent *event)
+ {
+ if (event->type() == QEvent::MetaCall) {
+ auto metaCallEvent = static_cast<QMetaCallEvent *>(event);
+ auto args = metaCallEvent->args();
+ auto hostInfo = reinterpret_cast<QHostInfo *>(args[1]);
+ emitResultsReady(*hostInfo);
+ deleteLater();
+ return true;
+ }
+ return QObject::event(event);
}
Q_SIGNALS:
@@ -154,6 +192,8 @@ class QHostInfoRunnable : public QRunnable
{
public:
QHostInfoRunnable(const QString &hn, int i);
+ QHostInfoRunnable(const QString &hn, int i, const QObject *receiver,
+ QtPrivate::QSlotObjectBase *slotObj);
void run() Q_DECL_OVERRIDE;
QString toBeLookedUp;
diff --git a/src/network/kernel/qnetworkproxy.cpp b/src/network/kernel/qnetworkproxy.cpp
index 0be8a7f79e..11e8fa6264 100644
--- a/src/network/kernel/qnetworkproxy.cpp
+++ b/src/network/kernel/qnetworkproxy.cpp
@@ -1636,10 +1636,6 @@ QList<QNetworkProxy> QNetworkProxyFactory::proxyForQuery(const QNetworkProxyQuer
}
#ifndef QT_NO_DEBUG_STREAM
-/*!
- \since 5.0
- Outputs a QNetworkProxy details to a debug stream
-*/
QDebug operator<<(QDebug debug, const QNetworkProxy &proxy)
{
QDebugStateSaver saver(debug);
@@ -1688,6 +1684,21 @@ QDebug operator<<(QDebug debug, const QNetworkProxy &proxy)
debug << '[' << scaps.join(QLatin1Char(' ')) << ']';
return debug;
}
+
+QDebug operator<<(QDebug debug, const QNetworkProxyQuery &proxyQuery)
+{
+ QDebugStateSaver saver(debug);
+ debug.resetFormat().nospace()
+ << "ProxyQuery("
+ << "type: " << proxyQuery.queryType()
+ << ", protocol: " << proxyQuery.protocolTag()
+ << ", peerPort: " << proxyQuery.peerPort()
+ << ", peerHostName: " << proxyQuery.peerHostName()
+ << ", localPort: " << proxyQuery.localPort()
+ << ", url: " << proxyQuery.url()
+ << ')';
+ return debug;
+}
#endif
QT_END_NAMESPACE
diff --git a/src/network/kernel/qnetworkproxy.h b/src/network/kernel/qnetworkproxy.h
index 8fcb7e33cf..8699c313e9 100644
--- a/src/network/kernel/qnetworkproxy.h
+++ b/src/network/kernel/qnetworkproxy.h
@@ -56,6 +56,8 @@ class QNetworkConfiguration;
class QNetworkProxyQueryPrivate;
class Q_NETWORK_EXPORT QNetworkProxyQuery
{
+ Q_GADGET
+
public:
enum QueryType {
TcpSocket,
@@ -65,6 +67,7 @@ public:
UrlRequest,
SctpServer
};
+ Q_ENUM(QueryType)
QNetworkProxyQuery();
explicit QNetworkProxyQuery(const QUrl &requestUrl, QueryType queryType = UrlRequest);
@@ -222,6 +225,7 @@ public:
#ifndef QT_NO_DEBUG_STREAM
Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QNetworkProxy &proxy);
+Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QNetworkProxyQuery &proxyQuery);
#endif
QT_END_NAMESPACE
diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp
index 57c40194a3..7ecbf35489 100644
--- a/src/network/socket/qabstractsocket.cpp
+++ b/src/network/socket/qabstractsocket.cpp
@@ -491,7 +491,6 @@
#ifndef QABSTRACTSOCKET_BUFFERSIZE
#define QABSTRACTSOCKET_BUFFERSIZE 32768
#endif
-#define QT_CONNECT_TIMEOUT 30000
#define QT_TRANSFER_TIMEOUT 120000
QT_BEGIN_NAMESPACE
@@ -1142,7 +1141,15 @@ void QAbstractSocketPrivate::_q_connectToNextAddress()
q, SLOT(_q_abortConnectionAttempt()),
Qt::DirectConnection);
}
- connectTimer->start(QT_CONNECT_TIMEOUT);
+ int connectTimeout = QNetworkConfigurationPrivate::DefaultTimeout;
+#ifndef QT_NO_BEARERMANAGEMENT
+ QSharedPointer<QNetworkSession> networkSession = qvariant_cast< QSharedPointer<QNetworkSession> >(q->property("_q_networksession"));
+ if (networkSession) {
+ QNetworkConfiguration networkConfiguration = networkSession->configuration();
+ connectTimeout = networkConfiguration.connectTimeout();
+ }
+#endif
+ connectTimer->start(connectTimeout);
}
// Wait for a write notification that will eventually call
@@ -1279,11 +1286,11 @@ bool QAbstractSocketPrivate::readFromSocket()
}
if (!socketEngine->isValid()) {
- setErrorAndEmit(socketEngine->error(), socketEngine->errorString());
#if defined(QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocketPrivate::readFromSocket() read failed: %s",
- q->errorString().toLatin1().constData());
+ socketEngine->errorString().toLatin1().constData());
#endif
+ setErrorAndEmit(socketEngine->error(), socketEngine->errorString());
resetSocketLayer();
return false;
}
@@ -2100,6 +2107,10 @@ bool QAbstractSocket::waitForConnected(int msecs)
QElapsedTimer stopWatch;
stopWatch.start();
+#ifndef QT_NO_BEARERMANAGEMENT
+ QSharedPointer<QNetworkSession> networkSession = qvariant_cast< QSharedPointer<QNetworkSession> >(property("_q_networksession"));
+#endif
+
if (d->state == HostLookupState) {
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::waitForConnected(%i) doing host name lookup", msecs);
@@ -2107,10 +2118,7 @@ bool QAbstractSocket::waitForConnected(int msecs)
QHostInfo::abortHostLookup(d->hostLookupId);
d->hostLookupId = -1;
#ifndef QT_NO_BEARERMANAGEMENT
- QSharedPointer<QNetworkSession> networkSession;
- QVariant v(property("_q_networksession"));
- if (v.isValid()) {
- networkSession = qvariant_cast< QSharedPointer<QNetworkSession> >(v);
+ if (networkSession) {
d->_q_startConnecting(QHostInfoPrivate::fromName(d->hostName, networkSession));
} else
#endif
@@ -2128,14 +2136,21 @@ bool QAbstractSocket::waitForConnected(int msecs)
if (state() == UnconnectedState)
return false; // connect not im progress anymore!
+ int connectTimeout = QNetworkConfigurationPrivate::DefaultTimeout;
+#ifndef QT_NO_BEARERMANAGEMENT
+ if (networkSession) {
+ QNetworkConfiguration networkConfiguration = networkSession->configuration();
+ connectTimeout = networkConfiguration.connectTimeout();
+ }
+#endif
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 > QT_CONNECT_TIMEOUT)
- timeout = QT_CONNECT_TIMEOUT;
+ if (msecs != -1 && timeout > connectTimeout)
+ timeout = connectTimeout;
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::waitForConnected(%i) waiting %.2f secs for connection attempt #%i",
msecs, timeout / 1000.0, attempt++);
@@ -2448,7 +2463,6 @@ bool QAbstractSocket::atEnd() const
\sa write(), waitForBytesWritten()
*/
-// Note! docs copied to QSslSocket::flush()
bool QAbstractSocket::flush()
{
return d_func()->flush();
diff --git a/src/network/socket/qlocalserver.h b/src/network/socket/qlocalserver.h
index 786885b6cd..52c533141f 100644
--- a/src/network/socket/qlocalserver.h
+++ b/src/network/socket/qlocalserver.h
@@ -56,7 +56,6 @@ class Q_NETWORK_EXPORT QLocalServer : public QObject
Q_OBJECT
Q_DECLARE_PRIVATE(QLocalServer)
Q_PROPERTY(SocketOptions socketOptions READ socketOptions WRITE setSocketOptions)
- Q_FLAGS(SocketOption SocketOptions)
Q_SIGNALS:
void newConnection();
@@ -69,7 +68,9 @@ public:
OtherAccessOption = 0x4,
WorldAccessOption = 0x7
};
+ Q_FLAG(SocketOption)
Q_DECLARE_FLAGS(SocketOptions, SocketOption)
+ Q_FLAG(SocketOptions)
explicit QLocalServer(QObject *parent = Q_NULLPTR);
~QLocalServer();
diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h
index 46c7ae5c55..4d1d8e1eb1 100644
--- a/src/network/socket/qnativesocketengine_p.h
+++ b/src/network/socket/qnativesocketengine_p.h
@@ -108,9 +108,9 @@ union qt_sockaddr {
namespace {
namespace SetSALen {
- template <typename T> void set(T *sa, typename QtPrivate::QEnableIf<(&T::sa_len, true), QT_SOCKLEN_T>::Type len)
+ 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 *sin6, typename QtPrivate::QEnableIf<(&T::sin6_len, true), QT_SOCKLEN_T>::Type 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 *, ...) {}
}
diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp
index 5a9641a9fe..80147b47ba 100644
--- a/src/network/socket/qnativesocketengine_win.cpp
+++ b/src/network/socket/qnativesocketengine_win.cpp
@@ -51,6 +51,7 @@
#include <qdebug.h>
#include <qdatetime.h>
#include <qnetworkinterface.h>
+#include <qoperatingsystemversion.h>
//#define QNATIVESOCKETENGINE_DEBUG
#if defined(QNATIVESOCKETENGINE_DEBUG)
@@ -334,11 +335,9 @@ bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType soc
return false;
}
- QSysInfo::WinVersion osver = QSysInfo::windowsVersion();
-
//Windows XP and 2003 support IPv6 but not dual stack sockets
int protocol = (socketProtocol == QAbstractSocket::IPv6Protocol
- || (socketProtocol == QAbstractSocket::AnyIPProtocol && osver >= QSysInfo::WV_6_0)) ? AF_INET6 : AF_INET;
+ || (socketProtocol == QAbstractSocket::AnyIPProtocol)) ? AF_INET6 : AF_INET;
int type = (socketType == QAbstractSocket::UdpSocket) ? SOCK_DGRAM : SOCK_STREAM;
// MSDN KB179942 states that on winnt 4 WSA_FLAG_OVERLAPPED is needed if socket is to be non blocking
@@ -350,14 +349,11 @@ bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType soc
#define WSA_FLAG_NO_HANDLE_INHERIT 0x80
#endif
- SOCKET socket = INVALID_SOCKET;
- // Windows 7 or later, try the new API
- if ((osver & QSysInfo::WV_NT_based) >= QSysInfo::WV_6_1)
- socket = ::WSASocket(protocol, type, 0, NULL, 0, WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);
+ SOCKET socket = ::WSASocket(protocol, type, 0, NULL, 0, WSA_FLAG_NO_HANDLE_INHERIT | WSA_FLAG_OVERLAPPED);
// previous call fails if the windows 7 service pack 1 or hot fix isn't installed.
- // Try the old API if the new one failed on Windows 7, or always on earlier versions
- if (socket == INVALID_SOCKET && ((osver & QSysInfo::WV_NT_based) <= QSysInfo::WV_6_1)) {
+ // Try the old API if the new one failed on Windows 7
+ if (socket == INVALID_SOCKET && QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8) {
socket = ::WSASocket(protocol, type, 0, NULL, 0, WSA_FLAG_OVERLAPPED);
#ifdef HANDLE_FLAG_INHERIT
if (socket != INVALID_SOCKET) {
diff --git a/src/network/socket/qnativesocketengine_winrt.cpp b/src/network/socket/qnativesocketengine_winrt.cpp
index 8b36406c67..7b20b47aa9 100644
--- a/src/network/socket/qnativesocketengine_winrt.cpp
+++ b/src/network/socket/qnativesocketengine_winrt.cpp
@@ -779,7 +779,6 @@ void QNativeSocketEngine::close()
}
}
-#if _MSC_VER >= 1900
if (d->socketType == QAbstractSocket::TcpSocket) {
hr = QEventDispatcherWinRT::runOnXamlThread([d]() {
HRESULT hr;
@@ -803,7 +802,6 @@ void QNativeSocketEngine::close()
});
Q_ASSERT_SUCCEEDED(hr);
}
-#endif // _MSC_VER >= 1900
if (d->socketDescriptor != -1) {
ComPtr<IClosable> socket;
diff --git a/src/network/socket/qsctpserver.h b/src/network/socket/qsctpserver.h
index f39257485d..1afdab28a0 100644
--- a/src/network/socket/qsctpserver.h
+++ b/src/network/socket/qsctpserver.h
@@ -45,7 +45,7 @@
QT_BEGIN_NAMESPACE
-#ifndef QT_NO_SCTP
+#if !defined(QT_NO_SCTP) || defined(Q_CLANG_QDOC)
class QSctpServerPrivate;
class QSctpSocket;
diff --git a/src/network/socket/qsctpsocket.h b/src/network/socket/qsctpsocket.h
index 3e5a545c4b..9bed1890ff 100644
--- a/src/network/socket/qsctpsocket.h
+++ b/src/network/socket/qsctpsocket.h
@@ -45,7 +45,7 @@
QT_BEGIN_NAMESPACE
-#ifndef QT_NO_SCTP
+#if !defined(QT_NO_SCTP) || defined(Q_CLANG_QDOC)
class QSctpSocketPrivate;
diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp
index a1a8e4649d..89261ce789 100644
--- a/src/network/socket/qsocks5socketengine.cpp
+++ b/src/network/socket/qsocks5socketengine.cpp
@@ -54,6 +54,7 @@
#include "qurl.h"
#include "qauthenticator.h"
#include "private/qiodevice_p.h"
+#include "private/qringbuffer_p.h"
#include <qendian.h>
#include <qnetworkinterface.h>
@@ -280,7 +281,7 @@ struct QSocks5Data
struct QSocks5ConnectData : public QSocks5Data
{
- QByteArray readBuffer;
+ QRingBuffer readBuffer;
};
struct QSocks5BindData : public QSocks5Data
@@ -1193,7 +1194,7 @@ void QSocks5SocketEnginePrivate::_q_controlSocketReadNotification()
}
if (buf.size()) {
QSOCKS5_DEBUG << dump(buf);
- connectData->readBuffer += buf;
+ connectData->readBuffer.append(buf);
emitReadNotification();
}
break;
@@ -1513,7 +1514,7 @@ qint64 QSocks5SocketEngine::read(char *data, qint64 maxlen)
Q_D(QSocks5SocketEngine);
QSOCKS5_Q_DEBUG << "read( , maxlen = " << maxlen << ')';
if (d->mode == QSocks5SocketEnginePrivate::ConnectMode) {
- if (d->connectData->readBuffer.size() == 0) {
+ if (d->connectData->readBuffer.isEmpty()) {
if (d->data->controlSocket->state() == QAbstractSocket::UnconnectedState) {
//imitate remote closed
close();
@@ -1525,9 +1526,7 @@ qint64 QSocks5SocketEngine::read(char *data, qint64 maxlen)
return 0; // nothing to be read
}
}
- qint64 copy = qMin<qint64>(d->connectData->readBuffer.size(), maxlen);
- memcpy(data, d->connectData->readBuffer.constData(), copy);
- d->connectData->readBuffer.remove(0, copy);
+ const qint64 copy = d->connectData->readBuffer.read(data, maxlen);
QSOCKS5_DEBUG << "read" << dump(QByteArray(data, copy));
return copy;
#ifndef QT_NO_UDPSOCKET
diff --git a/src/network/ssl/qsslerror.cpp b/src/network/ssl/qsslerror.cpp
index c779651959..3f79d1a037 100644
--- a/src/network/ssl/qsslerror.cpp
+++ b/src/network/ssl/qsslerror.cpp
@@ -105,13 +105,13 @@ public:
QSslCertificate certificate;
};
+// RVCT compiler in debug build does not like about default values in const-
+// So as an workaround we define all constructor overloads here explicitly
/*!
Constructs a QSslError object with no error and default certificate.
*/
-// RVCT compiler in debug build does not like about default values in const-
-// So as an workaround we define all constructor overloads here explicitly
QSslError::QSslError()
: d(new QSslErrorPrivate)
{
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index 2fc779b257..bb0d949684 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -834,7 +834,6 @@ bool QSslSocket::atEnd() const
\sa write(), waitForBytesWritten()
*/
-// Note! docs copied from QAbstractSocket::flush()
bool QSslSocket::flush()
{
return d_func()->flush();
diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp
index 88fed69d3a..2a0d746fde 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols.cpp
+++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp
@@ -154,7 +154,7 @@ DEFINEFUNC(int, BN_num_bits, const BIGNUM *a, a, return 0, return)
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
DEFINEFUNC2(int, BN_is_word, BIGNUM *a, a, BN_ULONG w, w, return 0, return)
#endif
-DEFINEFUNC2(BN_ULONG, BN_mod_word, const BIGNUM *a, a, BN_ULONG w, w, return -1, return)
+DEFINEFUNC2(BN_ULONG, BN_mod_word, const BIGNUM *a, a, BN_ULONG w, w, return static_cast<BN_ULONG>(-1), return)
#ifndef OPENSSL_NO_EC
DEFINEFUNC(const EC_GROUP*, EC_KEY_get0_group, const EC_KEY* k, k, return 0, return)
DEFINEFUNC(int, EC_GROUP_get_degree, const EC_GROUP* g, g, return 0, return)
diff --git a/src/network/ssl/qsslsocket_winrt.cpp b/src/network/ssl/qsslsocket_winrt.cpp
index f5dc9fcdcd..ca65f8a015 100644
--- a/src/network/ssl/qsslsocket_winrt.cpp
+++ b/src/network/ssl/qsslsocket_winrt.cpp
@@ -181,13 +181,7 @@ long QSslSocketPrivate::sslLibraryVersionNumber()
QString QSslSocketPrivate::sslLibraryVersionString()
{
- switch (QSysInfo::windowsVersion()) {
- case QSysInfo::WV_WINDOWS8_1:
- return QStringLiteral("Windows Runtime 8.1 SSL library");
- default:
- break;
- }
- return QStringLiteral("Windows Runtime SSL library");
+ return QStringLiteral("Windows Runtime, ") + QSysInfo::prettyProductName();
}
long QSslSocketPrivate::sslLibraryBuildVersionNumber()