summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
Diffstat (limited to 'src/network')
-rw-r--r--src/network/access/access.pri9
-rw-r--r--src/network/access/http2/bitstreams.cpp336
-rw-r--r--src/network/access/http2/bitstreams_p.h185
-rw-r--r--src/network/access/http2/hpack.cpp551
-rw-r--r--src/network/access/http2/hpack_p.h155
-rw-r--r--src/network/access/http2/hpacktable.cpp533
-rw-r--r--src/network/access/http2/hpacktable_p.h237
-rw-r--r--src/network/access/http2/http2.pri17
-rw-r--r--src/network/access/http2/http2frames.cpp495
-rw-r--r--src/network/access/http2/http2frames_p.h185
-rw-r--r--src/network/access/http2/http2protocol.cpp156
-rw-r--r--src/network/access/http2/http2protocol_p.h175
-rw-r--r--src/network/access/http2/http2streams.cpp104
-rw-r--r--src/network/access/http2/http2streams_p.h102
-rw-r--r--src/network/access/http2/huffman.cpp573
-rw-r--r--src/network/access/http2/huffman_p.h182
-rw-r--r--src/network/access/qabstractnetworkcache.h1
-rw-r--r--src/network/access/qabstractnetworkcache_p.h1
-rw-r--r--src/network/access/qabstractprotocolhandler_p.h4
-rw-r--r--src/network/access/qftp.cpp2
-rw-r--r--src/network/access/qftp_p.h7
-rw-r--r--src/network/access/qhttp2protocolhandler.cpp1218
-rw-r--r--src/network/access/qhttp2protocolhandler_p.h208
-rw-r--r--src/network/access/qhttpmultipart.h1
-rw-r--r--src/network/access/qhttpmultipart_p.h1
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp18
-rw-r--r--src/network/access/qhttpnetworkconnection_p.h21
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp78
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel_p.h9
-rw-r--r--src/network/access/qhttpnetworkheader_p.h3
-rw-r--r--src/network/access/qhttpnetworkreply.cpp5
-rw-r--r--src/network/access/qhttpnetworkreply_p.h7
-rw-r--r--src/network/access/qhttpnetworkrequest.cpp14
-rw-r--r--src/network/access/qhttpnetworkrequest_p.h7
-rw-r--r--src/network/access/qhttpprotocolhandler.cpp7
-rw-r--r--src/network/access/qhttpprotocolhandler_p.h1
-rw-r--r--src/network/access/qhttpthreaddelegate.cpp11
-rw-r--r--src/network/access/qhttpthreaddelegate_p.h1
-rw-r--r--src/network/access/qnetworkaccessauthenticationmanager_p.h1
-rw-r--r--src/network/access/qnetworkaccessbackend_p.h1
-rw-r--r--src/network/access/qnetworkaccesscache_p.h1
-rw-r--r--src/network/access/qnetworkaccesscachebackend_p.h1
-rw-r--r--src/network/access/qnetworkaccessdebugpipebackend_p.h1
-rw-r--r--src/network/access/qnetworkaccessfilebackend_p.h1
-rw-r--r--src/network/access/qnetworkaccessftpbackend_p.h1
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp81
-rw-r--r--src/network/access/qnetworkaccessmanager.h4
-rw-r--r--src/network/access/qnetworkaccessmanager_p.h8
-rw-r--r--src/network/access/qnetworkcookie.cpp4
-rw-r--r--src/network/access/qnetworkcookie.h1
-rw-r--r--src/network/access/qnetworkcookie_p.h1
-rw-r--r--src/network/access/qnetworkcookiejar.h1
-rw-r--r--src/network/access/qnetworkcookiejar_p.h1
-rw-r--r--src/network/access/qnetworkdiskcache.cpp2
-rw-r--r--src/network/access/qnetworkdiskcache.h1
-rw-r--r--src/network/access/qnetworkdiskcache_p.h1
-rw-r--r--src/network/access/qnetworkfile.cpp92
-rw-r--r--src/network/access/qnetworkfile_p.h80
-rw-r--r--src/network/access/qnetworkreply.h1
-rw-r--r--src/network/access/qnetworkreply_p.h1
-rw-r--r--src/network/access/qnetworkreplydataimpl_p.h1
-rw-r--r--src/network/access/qnetworkreplyfileimpl.cpp150
-rw-r--r--src/network/access/qnetworkreplyfileimpl_p.h13
-rw-r--r--src/network/access/qnetworkreplyhttpimpl.cpp48
-rw-r--r--src/network/access/qnetworkreplyhttpimpl_p.h1
-rw-r--r--src/network/access/qnetworkreplyimpl_p.h1
-rw-r--r--src/network/access/qnetworkrequest.cpp8
-rw-r--r--src/network/access/qnetworkrequest.h3
-rw-r--r--src/network/access/qnetworkrequest_p.h1
-rw-r--r--src/network/access/qspdyprotocolhandler.cpp8
-rw-r--r--src/network/access/qspdyprotocolhandler_p.h1
-rw-r--r--src/network/bearer/qbearerengine_p.h1
-rw-r--r--src/network/bearer/qbearerplugin_p.h1
-rw-r--r--src/network/bearer/qnetworkconfigmanager.h1
-rw-r--r--src/network/bearer/qnetworkconfigmanager_p.h1
-rw-r--r--src/network/bearer/qnetworkconfiguration.h2
-rw-r--r--src/network/bearer/qnetworkconfiguration_p.h1
-rw-r--r--src/network/bearer/qnetworksession.h1
-rw-r--r--src/network/bearer/qnetworksession_p.h1
-rw-r--r--src/network/bearer/qsharednetworksession_p.h1
-rw-r--r--src/network/configure.json274
-rw-r--r--src/network/configure.pri12
-rw-r--r--src/network/doc/snippets/code/src_network_socket_qsctpsocket.cpp53
-rw-r--r--src/network/doc/snippets/code/src_network_socket_qudpsocket.cpp9
-rw-r--r--src/network/kernel/kernel.pri21
-rw-r--r--src/network/kernel/qauthenticator.cpp14
-rw-r--r--src/network/kernel/qauthenticator.h1
-rw-r--r--src/network/kernel/qauthenticator_p.h1
-rw-r--r--src/network/kernel/qdnslookup.h1
-rw-r--r--src/network/kernel/qdnslookup_p.h1
-rw-r--r--src/network/kernel/qhostaddress.cpp122
-rw-r--r--src/network/kernel/qhostaddress.h13
-rw-r--r--src/network/kernel/qhostaddress_p.h1
-rw-r--r--src/network/kernel/qhostinfo.h1
-rw-r--r--src/network/kernel/qhostinfo_p.h1
-rw-r--r--src/network/kernel/qhostinfo_win.cpp11
-rw-r--r--src/network/kernel/qnetworkdatagram.cpp535
-rw-r--r--src/network/kernel/qnetworkdatagram.h123
-rw-r--r--src/network/kernel/qnetworkdatagram_p.h23
-rw-r--r--src/network/kernel/qnetworkinterface.cpp4
-rw-r--r--src/network/kernel/qnetworkinterface.h1
-rw-r--r--src/network/kernel/qnetworkinterface_p.h7
-rw-r--r--src/network/kernel/qnetworkinterface_unix.cpp2
-rw-r--r--src/network/kernel/qnetworkinterface_win.cpp8
-rw-r--r--src/network/kernel/qnetworkproxy.cpp37
-rw-r--r--src/network/kernel/qnetworkproxy.h9
-rw-r--r--src/network/kernel/qnetworkproxy_generic.cpp2
-rw-r--r--src/network/kernel/qnetworkproxy_mac.cpp131
-rw-r--r--src/network/kernel/qnetworkproxy_p.h9
-rw-r--r--src/network/kernel/qnetworkproxy_win.cpp30
-rw-r--r--src/network/kernel/qtnetworkglobal.h (renamed from src/network/kernel/qnetworkfunctions_wince.h)63
-rw-r--r--src/network/kernel/qtnetworkglobal_p.h58
-rw-r--r--src/network/kernel/qurlinfo_p.h1
-rw-r--r--src/network/network.pro1
-rw-r--r--src/network/socket/qabstractsocket.cpp157
-rw-r--r--src/network/socket/qabstractsocket.h2
-rw-r--r--src/network/socket/qabstractsocket_p.h12
-rw-r--r--src/network/socket/qabstractsocketengine_p.h12
-rw-r--r--src/network/socket/qhttpsocketengine.cpp22
-rw-r--r--src/network/socket/qhttpsocketengine_p.h7
-rw-r--r--src/network/socket/qlocalserver.h1
-rw-r--r--src/network/socket/qlocalserver_p.h2
-rw-r--r--src/network/socket/qlocalsocket.h1
-rw-r--r--src/network/socket/qlocalsocket_p.h2
-rw-r--r--src/network/socket/qnativesocketengine.cpp126
-rw-r--r--src/network/socket/qnativesocketengine_p.h28
-rw-r--r--src/network/socket/qnativesocketengine_unix.cpp182
-rw-r--r--src/network/socket/qnativesocketengine_win.cpp59
-rw-r--r--src/network/socket/qnativesocketengine_winrt.cpp18
-rw-r--r--src/network/socket/qnativesocketengine_winrt_p.h2
-rw-r--r--src/network/socket/qnet_unix_p.h1
-rw-r--r--src/network/socket/qsctpserver.cpp250
-rw-r--r--src/network/socket/qsctpserver.h77
-rw-r--r--src/network/socket/qsctpserver_p.h76
-rw-r--r--src/network/socket/qsctpsocket.cpp547
-rw-r--r--src/network/socket/qsctpsocket.h82
-rw-r--r--src/network/socket/qsctpsocket_p.h90
-rw-r--r--src/network/socket/qsocks5socketengine.cpp66
-rw-r--r--src/network/socket/qsocks5socketengine_p.h7
-rw-r--r--src/network/socket/qtcpserver.cpp39
-rw-r--r--src/network/socket/qtcpserver.h4
-rw-r--r--src/network/socket/qtcpserver_p.h9
-rw-r--r--src/network/socket/qtcpsocket.h1
-rw-r--r--src/network/socket/qtcpsocket_p.h7
-rw-r--r--src/network/socket/qudpsocket.cpp126
-rw-r--r--src/network/socket/qudpsocket.h5
-rw-r--r--src/network/socket/socket.pri29
-rw-r--r--src/network/ssl/qasn1element_p.h7
-rw-r--r--src/network/ssl/qssl.h2
-rw-r--r--src/network/ssl/qssl_p.h1
-rw-r--r--src/network/ssl/qsslcertificate.cpp2
-rw-r--r--src/network/ssl/qsslcertificate.h1
-rw-r--r--src/network/ssl/qsslcertificate_p.h7
-rw-r--r--src/network/ssl/qsslcertificateextension.h1
-rw-r--r--src/network/ssl/qsslcertificateextension_p.h1
-rw-r--r--src/network/ssl/qsslcipher.h1
-rw-r--r--src/network/ssl/qsslcipher_p.h1
-rw-r--r--src/network/ssl/qsslconfiguration.cpp78
-rw-r--r--src/network/ssl/qsslconfiguration.h9
-rw-r--r--src/network/ssl/qsslconfiguration_p.h8
-rw-r--r--src/network/ssl/qsslcontext_openssl.cpp61
-rw-r--r--src/network/ssl/qsslcontext_openssl_p.h1
-rw-r--r--src/network/ssl/qssldiffiehellmanparameters.cpp324
-rw-r--r--src/network/ssl/qssldiffiehellmanparameters.h118
-rw-r--r--src/network/ssl/qssldiffiehellmanparameters_dummy.cpp57
-rw-r--r--src/network/ssl/qssldiffiehellmanparameters_openssl.cpp164
-rw-r--r--src/network/ssl/qssldiffiehellmanparameters_p.h78
-rw-r--r--src/network/ssl/qsslellipticcurve.h2
-rw-r--r--src/network/ssl/qsslerror.h1
-rw-r--r--src/network/ssl/qsslkey.h1
-rw-r--r--src/network/ssl/qsslkey_p.cpp16
-rw-r--r--src/network/ssl/qsslkey_p.h4
-rw-r--r--src/network/ssl/qsslpresharedkeyauthenticator.h2
-rw-r--r--src/network/ssl/qsslpresharedkeyauthenticator_p.h1
-rw-r--r--src/network/ssl/qsslsocket.cpp28
-rw-r--r--src/network/ssl/qsslsocket.h3
-rw-r--r--src/network/ssl/qsslsocket_mac.cpp187
-rw-r--r--src/network/ssl/qsslsocket_mac_p.h1
-rw-r--r--src/network/ssl/qsslsocket_openssl.cpp73
-rw-r--r--src/network/ssl/qsslsocket_openssl_android.cpp1
-rw-r--r--src/network/ssl/qsslsocket_openssl_p.h8
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols.cpp39
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols_p.h54
-rw-r--r--src/network/ssl/qsslsocket_p.h12
-rw-r--r--src/network/ssl/qsslsocket_winrt.cpp4
-rw-r--r--src/network/ssl/qsslsocket_winrt_p.h1
-rw-r--r--src/network/ssl/ssl.pri64
187 files changed, 10212 insertions, 992 deletions
diff --git a/src/network/access/access.pri b/src/network/access/access.pri
index 38e9c25269..70ace3f55e 100644
--- a/src/network/access/access.pri
+++ b/src/network/access/access.pri
@@ -37,7 +37,9 @@ HEADERS += \
access/qnetworkdiskcache.h \
access/qhttpthreaddelegate_p.h \
access/qhttpmultipart.h \
- access/qhttpmultipart_p.h
+ access/qhttpmultipart_p.h \
+ access/qnetworkfile_p.h \
+ access/qhttp2protocolhandler_p.h
SOURCES += \
access/qftp.cpp \
@@ -68,8 +70,11 @@ SOURCES += \
access/qabstractnetworkcache.cpp \
access/qnetworkdiskcache.cpp \
access/qhttpthreaddelegate.cpp \
- access/qhttpmultipart.cpp
+ access/qhttpmultipart.cpp \
+ access/qnetworkfile.cpp \
+ access/qhttp2protocolhandler.cpp
mac: LIBS_PRIVATE += -framework Security
include($$PWD/../../3rdparty/zlib_dependency.pri)
+include($$PWD/http2/http2.pri)
diff --git a/src/network/access/http2/bitstreams.cpp b/src/network/access/http2/bitstreams.cpp
new file mode 100644
index 0000000000..d22c7cd4ec
--- /dev/null
+++ b/src/network/access/http2/bitstreams.cpp
@@ -0,0 +1,336 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 "bitstreams_p.h"
+#include "huffman_p.h"
+
+#include <QtCore/qbytearray.h>
+
+#include <limits>
+
+QT_BEGIN_NAMESPACE
+
+static_assert(std::numeric_limits<uchar>::digits == 8, "octets expected");
+
+namespace HPack
+{
+
+BitOStream::BitOStream(std::vector<uchar> &b)
+ : buffer(b),
+ // All data 'packed' before:
+ bitsSet(8 * quint64(b.size()))
+{
+}
+
+void BitOStream::writeBits(uchar bits, quint8 bitLength)
+{
+ Q_ASSERT(bitLength <= 8);
+
+ quint8 count = bitsSet % 8; // bits used in buffer.back(), but 0 means 8
+ bits <<= 8 - bitLength; // at top of byte, lower bits clear
+ if (count) { // we have a part-used byte; fill it some more:
+ buffer.back() |= bits >> count;
+ count = 8 - count;
+ } // count bits have been consumed (and 0 now means 0)
+ if (bitLength > count)
+ buffer.push_back(bits << count);
+
+ bitsSet += bitLength;
+}
+
+void BitOStream::write(quint32 src)
+{
+ const quint8 prefixLen = 8 - bitsSet % 8;
+ const quint32 fullPrefix = (1 << prefixLen) - 1;
+
+ // https://http2.github.io/http2-spec/compression.html#low-level.representation,
+ // 5.1
+ if (src < fullPrefix) {
+ writeBits(uchar(src), prefixLen);
+ } else {
+ writeBits(uchar(fullPrefix), prefixLen);
+ // We're on the byte boundary now,
+ // so we can just 'push_back'.
+ Q_ASSERT(!(bitsSet % 8));
+ src -= fullPrefix;
+ while (src >= 128) {
+ buffer.push_back(uchar(src % 128 + 128));
+ src /= 128;
+ bitsSet += 8;
+ }
+ buffer.push_back(src);
+ bitsSet += 8;
+ }
+}
+
+void BitOStream::write(const QByteArray &src, bool compressed)
+{
+ quint32 byteLen = src.size();
+ if (compressed && byteLen) {
+ const auto bitLen = huffman_encoded_bit_length(src);
+ Q_ASSERT(bitLen && std::numeric_limits<quint32>::max() >= (bitLen + 7) / 8);
+ byteLen = (bitLen + 7) / 8;
+ writeBits(uchar(1), 1); // bit set - compressed
+ } else {
+ writeBits(uchar(0), 1); // no compression.
+ }
+
+ write(byteLen);
+
+ if (compressed) {
+ huffman_encode_string(src, *this);
+ } else {
+ bitsSet += quint64(src.size()) * 8;
+ buffer.insert(buffer.end(), src.begin(), src.end());
+ }
+}
+
+quint64 BitOStream::bitLength() const
+{
+ return bitsSet;
+}
+
+quint64 BitOStream::byteLength() const
+{
+ return buffer.size();
+}
+
+const uchar *BitOStream::begin() const
+{
+ return &buffer[0];
+}
+
+const uchar *BitOStream::end() const
+{
+ return &buffer[0] + buffer.size();
+}
+
+void BitOStream::clear()
+{
+ buffer.clear();
+ bitsSet = 0;
+}
+
+BitIStream::BitIStream()
+ : first(),
+ last(),
+ offset(),
+ streamError(Error::NoError)
+{
+}
+
+BitIStream::BitIStream(const uchar *begin, const uchar *end)
+ : first(begin),
+ last(end),
+ offset(),
+ streamError(Error::NoError)
+{
+}
+
+quint64 BitIStream::bitLength() const
+{
+ return quint64(last - first) * 8;
+}
+
+bool BitIStream::hasMoreBits() const
+{
+ return offset < bitLength();
+}
+
+bool BitIStream::skipBits(quint64 nBits)
+{
+ if (nBits > bitLength() || bitLength() - nBits < offset)
+ return false;
+
+ offset += nBits;
+ return true;
+}
+
+bool BitIStream::rewindOffset(quint64 nBits)
+{
+ if (nBits > offset)
+ return false;
+
+ offset -= nBits;
+ return true;
+}
+
+bool BitIStream::read(quint32 *dstPtr)
+{
+ Q_ASSERT(dstPtr);
+ quint32 &dst = *dstPtr;
+
+ // 5.1 Integer Representation
+ //
+ // Integers are used to represent name indexes, header field indexes, or string lengths.
+ // An integer representation can start anywhere within an octet.
+ // To allow for optimized processing, an integer representation always finishes at the end of an octet.
+ // An integer is represented in two parts: a prefix that fills the current octet and an optional
+ // list of octets that are used if the integer value does not fit within the prefix.
+ // The number of bits of the prefix (called N) is a parameter of the integer representation.
+ // If the integer value is small enough, i.e., strictly less than 2N-1, it is compressed within the N-bit prefix.
+ // ...
+ // The prefix size, N, is always between 1 and 8 bits. An integer
+ // starting at an octet boundary will have an 8-bit prefix.
+
+ // Technically, such integers can be of any size, but as we do not have arbitrary-long integers,
+ // everything that does not fit into 'dst' we consider as an error (after all, try to allocate a string
+ // of such size and ... hehehe - send it as a part of a header!
+
+ // This function updates the offset _only_ if the read was successful.
+ if (offset >= bitLength()) {
+ setError(Error::NotEnoughData);
+ return false;
+ }
+
+ setError(Error::NoError);
+
+ const quint32 prefixLen = 8 - offset % 8;
+ const quint32 fullPrefix = (1 << prefixLen) - 1;
+
+ const uchar prefix = uchar(first[offset / 8] & fullPrefix);
+ if (prefix < fullPrefix) {
+ // The number fitted into the prefix bits.
+ dst = prefix;
+ offset += prefixLen;
+ return true;
+ }
+
+ quint32 newOffset = offset + prefixLen;
+ // We have a list of bytes representing an integer ...
+ quint64 val = prefix;
+ quint32 octetPower = 0;
+
+ while (true) {
+ if (newOffset >= bitLength()) {
+ setError(Error::NotEnoughData);
+ return false;
+ }
+
+ const uchar octet = first[newOffset / 8];
+
+ if (octetPower == 28 && octet > 15) {
+ qCritical("integer is too big");
+ setError(Error::InvalidInteger);
+ return false;
+ }
+
+ val += quint32(octet & 0x7f) << octetPower;
+ newOffset += 8;
+
+ if (!(octet & 0x80)) {
+ // The most significant bit of each octet is used
+ // as a continuation flag: its value is set to 1
+ // except for the last octet in the list.
+ break;
+ }
+
+ octetPower += 7;
+ }
+
+ dst = val;
+ offset = newOffset;
+ Q_ASSERT(!(offset % 8));
+
+ return true;
+}
+
+bool BitIStream::read(QByteArray *dstPtr)
+{
+ Q_ASSERT(dstPtr);
+ QByteArray &dst = *dstPtr;
+ //5.2 String Literal Representation
+ //
+ // Header field names and header field values can be represented as string literals.
+ // A string literal is compressed as a sequence of octets, either by directly encoding
+ // the string literal's octets or by using a Huffman code.
+
+ // We update the offset _only_ if the read was successful.
+
+ const quint64 oldOffset = offset;
+ uchar compressed = 0;
+ if (peekBits(offset, 1, &compressed) != 1 || !skipBits(1)) {
+ setError(Error::NotEnoughData);
+ return false;
+ }
+
+ setError(Error::NoError);
+
+ quint32 len = 0;
+ if (read(&len)) {
+ Q_ASSERT(!(offset % 8));
+ if (len <= (bitLength() - offset) / 8) { // We have enough data to read a string ...
+ if (!compressed) {
+ // Now good news, integer always ends on a byte boundary.
+ // We can read 'len' bytes without any bit magic.
+ const char *src = reinterpret_cast<const char *>(first + offset / 8);
+ dst = QByteArray(src, len);
+ offset += quint64(len) * 8;
+ return true;
+ }
+
+ BitIStream slice(first + offset / 8, first + offset / 8 + len);
+ if (huffman_decode_string(slice, &dst)) {
+ offset += quint64(len) * 8;
+ return true;
+ }
+
+ setError(Error::CompressionError);
+ } else {
+ setError(Error::NotEnoughData);
+ }
+ } // else the exact reason was set by read(quint32).
+
+ offset = oldOffset;
+ return false;
+}
+
+BitIStream::Error BitIStream::error() const
+{
+ return streamError;
+}
+
+void BitIStream::setError(Error newState)
+{
+ streamError = newState;
+}
+
+} // namespace HPack
+
+QT_END_NAMESPACE
diff --git a/src/network/access/http2/bitstreams_p.h b/src/network/access/http2/bitstreams_p.h
new file mode 100644
index 0000000000..9eba319dc2
--- /dev/null
+++ b/src/network/access/http2/bitstreams_p.h
@@ -0,0 +1,185 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 BITSTREAMS_P_H
+#define BITSTREAMS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qdebug.h>
+
+#include <type_traits>
+#include <algorithm>
+#include <vector>
+
+QT_BEGIN_NAMESPACE
+
+class QByteArray;
+
+namespace HPack
+{
+
+// BitOStream works with an external buffer,
+// for example, HEADERS frame.
+class Q_AUTOTEST_EXPORT BitOStream
+{
+public:
+ BitOStream(std::vector<uchar> &buffer);
+
+ // Write 'bitLength' bits from the least significant
+ // bits in 'bits' to bitstream:
+ void writeBits(uchar bits, quint8 bitLength);
+ // HPACK data format, we support:
+ // * 32-bit integers
+ // * strings
+ void write(quint32 src);
+ void write(const QByteArray &src, bool compressed);
+
+ quint64 bitLength() const;
+ quint64 byteLength() const;
+ const uchar *begin() const;
+ const uchar *end() const;
+
+ void clear();
+
+private:
+ Q_DISABLE_COPY(BitOStream);
+
+ std::vector<uchar> &buffer;
+ quint64 bitsSet;
+};
+
+class Q_AUTOTEST_EXPORT BitIStream
+{
+public:
+ // Error is set by 'read' functions.
+ // 'peek' does not set the error,
+ // since it just peeks some bits
+ // without the notion of wrong/right.
+ // 'read' functions only change 'streamOffset'
+ // on success.
+ enum class Error
+ {
+ NoError,
+ NotEnoughData,
+ CompressionError,
+ InvalidInteger
+ };
+
+ BitIStream();
+ BitIStream(const uchar *f, const uchar *l);
+
+ quint64 bitLength() const;
+ bool hasMoreBits() const;
+
+ // peekBits tries to read 'length' bits from the bitstream into
+ // 'dst' ('length' must be <= sizeof(dst) * 8), packing them
+ // starting from the most significant bit of the most significant
+ // byte. It's a template so that we can use it with different
+ // integer types. Returns the number of bits actually read.
+ // Does not change stream's offset.
+
+ template<class T>
+ quint64 peekBits(quint64 from, quint64 length, T *dstPtr) const
+ {
+ static_assert(std::is_unsigned<T>::value, "peekBits: unsigned integer type expected");
+
+ Q_ASSERT(dstPtr);
+ Q_ASSERT(length <= sizeof(T) * 8);
+
+ if (from >= bitLength() || !length)
+ return 0;
+
+ T &dst = *dstPtr;
+ dst = T();
+ length = std::min(length, bitLength() - from);
+
+ const uchar *srcByte = first + from / 8;
+ auto bitsToRead = length + from % 8;
+
+ while (bitsToRead > 8) {
+ dst = (dst << 8) | *srcByte;
+ bitsToRead -= 8;
+ ++srcByte;
+ }
+
+ dst <<= bitsToRead;
+ dst |= *srcByte >> (8 - bitsToRead);
+ dst <<= sizeof(T) * 8 - length;
+
+ return length;
+ }
+
+ quint64 streamOffset() const
+ {
+ return offset;
+ }
+
+ bool skipBits(quint64 nBits);
+ bool rewindOffset(quint64 nBits);
+
+ bool read(quint32 *dstPtr);
+ bool read(QByteArray *dstPtr);
+
+ Error error() const;
+
+private:
+ void setError(Error newState);
+
+ const uchar *first;
+ const uchar *last;
+ quint64 offset;
+ Error streamError;
+};
+
+} // namespace HPack
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/http2/hpack.cpp b/src/network/access/http2/hpack.cpp
new file mode 100644
index 0000000000..95e6f9051b
--- /dev/null
+++ b/src/network/access/http2/hpack.cpp
@@ -0,0 +1,551 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 "bitstreams_p.h"
+#include "hpack_p.h"
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qdebug.h>
+
+#include <limits>
+
+QT_BEGIN_NAMESPACE
+
+namespace HPack
+{
+
+HeaderSize header_size(const HttpHeader &header)
+{
+ HeaderSize size(true, 0);
+ for (const HeaderField &field : header) {
+ HeaderSize delta = entry_size(field);
+ if (!delta.first)
+ return HeaderSize();
+ if (std::numeric_limits<quint32>::max() - size.second < delta.second)
+ return HeaderSize();
+ size.second += delta.second;
+ }
+
+ return size;
+}
+
+struct BitPattern
+{
+ BitPattern()
+ : value(),
+ bitLength()
+ {
+ }
+
+ BitPattern(uchar v, uchar len)
+ : value(v),
+ bitLength(len)
+ {
+ }
+
+ uchar value;
+ uchar bitLength;
+};
+
+bool operator == (const BitPattern &lhs, const BitPattern &rhs)
+{
+ return lhs.bitLength == rhs.bitLength && lhs.value == rhs.value;
+}
+
+namespace
+{
+
+using StreamError = BitIStream::Error;
+
+// There are several bit patterns to distinguish header fields:
+// 1 - indexed
+// 01 - literal with incremented indexing
+// 0000 - literal without indexing
+// 0001 - literal, never indexing
+// 001 - dynamic table size update.
+
+// It's always 1 or 0 actually, but the number of bits to extract
+// from the input stream - differs.
+const BitPattern Indexed(1, 1);
+const BitPattern LiteralIncrementalIndexing(1, 2);
+const BitPattern LiteralNoIndexing(0, 4);
+const BitPattern LiteralNeverIndexing(1, 4);
+const BitPattern SizeUpdate(1, 3);
+
+bool is_literal_field(const BitPattern &pattern)
+{
+ return pattern == LiteralIncrementalIndexing
+ || pattern == LiteralNoIndexing
+ || pattern == LiteralNeverIndexing;
+}
+
+void write_bit_pattern(const BitPattern &pattern, BitOStream &outputStream)
+{
+ outputStream.writeBits(pattern.value, pattern.bitLength);
+}
+
+bool read_bit_pattern(const BitPattern &pattern, BitIStream &inputStream)
+{
+ uchar chunk = 0;
+
+ const quint32 bitsRead = inputStream.peekBits(inputStream.streamOffset(),
+ pattern.bitLength, &chunk);
+ if (bitsRead != pattern.bitLength)
+ return false;
+
+ // Since peekBits packs in the most significant bits, shift it!
+ chunk >>= (8 - bitsRead);
+ if (chunk != pattern.value)
+ return false;
+
+ inputStream.skipBits(pattern.bitLength);
+
+ return true;
+}
+
+bool is_request_pseudo_header(const QByteArray &name)
+{
+ return name == ":method" || name == ":scheme" ||
+ name == ":authority" || name == ":path";
+}
+
+} // unnamed namespace
+
+Encoder::Encoder(quint32 size, bool compress)
+ : lookupTable(size, true /*encoder needs search index*/),
+ compressStrings(compress)
+{
+}
+
+quint32 Encoder::dynamicTableSize() const
+{
+ return lookupTable.dynamicDataSize();
+}
+
+bool Encoder::encodeRequest(BitOStream &outputStream, const HttpHeader &header)
+{
+ if (!header.size()) {
+ qDebug("empty header");
+ return false;
+ }
+
+ if (!encodeRequestPseudoHeaders(outputStream, header))
+ return false;
+
+ for (const auto &field : header) {
+ if (is_request_pseudo_header(field.name))
+ continue;
+
+ if (!encodeHeaderField(outputStream, field))
+ return false;
+ }
+
+ return true;
+}
+
+bool Encoder::encodeResponse(BitOStream &outputStream, const HttpHeader &header)
+{
+ if (!header.size()) {
+ qDebug("empty header");
+ return false;
+ }
+
+ if (!encodeResponsePseudoHeaders(outputStream, header))
+ return false;
+
+ for (const auto &field : header) {
+ if (field.name == ":status")
+ continue;
+
+ if (!encodeHeaderField(outputStream, field))
+ return false;
+ }
+
+ return true;
+}
+
+bool Encoder::encodeSizeUpdate(BitOStream &outputStream, quint32 newSize)
+{
+ if (!lookupTable.updateDynamicTableSize(newSize)) {
+ qDebug("failed to update own table size");
+ return false;
+ }
+
+ write_bit_pattern(SizeUpdate, outputStream);
+ outputStream.write(newSize);
+
+ return true;
+}
+
+void Encoder::setMaxDynamicTableSize(quint32 size)
+{
+ // Up to a caller (HTTP2 protocol handler)
+ // to validate this size first.
+ lookupTable.setMaxDynamicTableSize(size);
+}
+
+bool Encoder::encodeRequestPseudoHeaders(BitOStream &outputStream,
+ const HttpHeader &header)
+{
+ // The following pseudo-header fields are defined for HTTP/2 requests:
+ // - The :method pseudo-header field includes the HTTP method
+ // - The :scheme pseudo-header field includes the scheme portion of the target URI
+ // - The :authority pseudo-header field includes the authority portion of the target URI
+ // - The :path pseudo-header field includes the path and query parts of the target URI
+
+ // All HTTP/2 requests MUST include exactly one valid value for the :method,
+ // :scheme, and :path pseudo-header fields, unless it is a CONNECT request
+ // (Section 8.3). An HTTP request that omits mandatory pseudo-header fields
+ // is malformed (Section 8.1.2.6).
+
+ using size_type = decltype(header.size());
+
+ bool methodFound = false;
+ const char *headerName[] = {":authority", ":scheme", ":path"};
+ const size_type nHeaders = sizeof headerName / sizeof headerName[0];
+ bool headerFound[nHeaders] = {};
+
+ for (const auto &field : header) {
+ if (field.name == ":status") {
+ qCritical("invalid pseudo-header (:status) in a request");
+ return false;
+ }
+
+ if (field.name == ":method") {
+ if (methodFound) {
+ qCritical("only one :method pseudo-header is allowed");
+ return false;
+ }
+
+ if (!encodeMethod(outputStream, field))
+ return false;
+ methodFound = true;
+ } else if (field.name == "cookie") {
+ // "crumbs" ...
+ } else {
+ for (size_type j = 0; j < nHeaders; ++j) {
+ if (field.name == headerName[j]) {
+ if (headerFound[j]) {
+ qCritical() << "only one" << headerName[j] << "pseudo-header is allowed";
+ return false;
+ }
+ if (!encodeHeaderField(outputStream, field))
+ return false;
+ headerFound[j] = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!methodFound) {
+ qCritical("mandatory :method pseudo-header not found");
+ return false;
+ }
+
+ // 1: don't demand headerFound[0], as :authority isn't mandatory.
+ for (size_type i = 1; i < nHeaders; ++i) {
+ if (!headerFound[i]) {
+ qCritical() << "mandatory" << headerName[i]
+ << "pseudo-header not found";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool Encoder::encodeHeaderField(BitOStream &outputStream, const HeaderField &field)
+{
+ // TODO: at the moment we never use LiteralNo/Never Indexing ...
+
+ // Here we try:
+ // 1. indexed
+ // 2. literal indexed with indexed name/literal value
+ // 3. literal indexed with literal name/literal value
+ if (const auto index = lookupTable.indexOf(field.name, field.value))
+ return encodeIndexedField(outputStream, index);
+
+ if (const auto index = lookupTable.indexOf(field.name)) {
+ return encodeLiteralField(outputStream, LiteralIncrementalIndexing,
+ index, field.value, compressStrings);
+ }
+
+ return encodeLiteralField(outputStream, LiteralIncrementalIndexing,
+ field.name, field.value, compressStrings);
+}
+
+bool Encoder::encodeMethod(BitOStream &outputStream, const HeaderField &field)
+{
+ Q_ASSERT(field.name == ":method");
+ quint32 index = lookupTable.indexOf(field.name, field.value);
+ if (index)
+ return encodeIndexedField(outputStream, index);
+
+ index = lookupTable.indexOf(field.name);
+ Q_ASSERT(index); // ":method" is always in the static table ...
+ return encodeLiteralField(outputStream, LiteralIncrementalIndexing,
+ index, field.value, compressStrings);
+}
+
+bool Encoder::encodeResponsePseudoHeaders(BitOStream &outputStream, const HttpHeader &header)
+{
+ bool statusFound = false;
+ for (const auto &field : header) {
+ if (is_request_pseudo_header(field.name)) {
+ qCritical() << "invalid pseudo-header" << field.name << "in http response";
+ return false;
+ }
+
+ if (field.name == ":status") {
+ if (statusFound) {
+ qDebug("only one :status pseudo-header is allowed");
+ return false;
+ }
+ if (!encodeHeaderField(outputStream, field))
+ return false;
+ statusFound = true;
+ } else if (field.name == "cookie") {
+ // "crumbs"..
+ }
+ }
+
+ if (!statusFound)
+ qCritical("mandatory :status pseudo-header not found");
+
+ return statusFound;
+}
+
+bool Encoder::encodeIndexedField(BitOStream &outputStream, quint32 index) const
+{
+ Q_ASSERT(lookupTable.indexIsValid(index));
+
+ write_bit_pattern(Indexed, outputStream);
+ outputStream.write(index);
+
+ return true;
+}
+
+bool Encoder::encodeLiteralField(BitOStream &outputStream, const BitPattern &fieldType,
+ const QByteArray &name, const QByteArray &value,
+ bool withCompression)
+{
+ Q_ASSERT(is_literal_field(fieldType));
+ // According to HPACK, the bit pattern is
+ // 01 | 000000 (integer 0 that fits into 6-bit prefix),
+ // since integers always end on byte boundary,
+ // this also implies that we always start at bit offset == 0.
+ if (outputStream.bitLength() % 8) {
+ qCritical("invalid bit offset");
+ return false;
+ }
+
+ if (fieldType == LiteralIncrementalIndexing) {
+ if (!lookupTable.prependField(name, value))
+ qDebug("failed to prepend a new field");
+ }
+
+ write_bit_pattern(fieldType, outputStream);
+
+ outputStream.write(0);
+ outputStream.write(name, withCompression);
+ outputStream.write(value, withCompression);
+
+ return true;
+}
+
+bool Encoder::encodeLiteralField(BitOStream &outputStream, const BitPattern &fieldType,
+ quint32 nameIndex, const QByteArray &value,
+ bool withCompression)
+{
+ Q_ASSERT(is_literal_field(fieldType));
+
+ QByteArray name;
+ const bool found = lookupTable.fieldName(nameIndex, &name);
+ Q_UNUSED(found) Q_ASSERT(found);
+
+ if (fieldType == LiteralIncrementalIndexing) {
+ if (!lookupTable.prependField(name, value))
+ qDebug("failed to prepend a new field");
+ }
+
+ write_bit_pattern(fieldType, outputStream);
+ outputStream.write(nameIndex);
+ outputStream.write(value, withCompression);
+
+ return true;
+}
+
+Decoder::Decoder(quint32 size)
+ : lookupTable{size, false /* we do not need search index ... */}
+{
+}
+
+bool Decoder::decodeHeaderFields(BitIStream &inputStream)
+{
+ header.clear();
+ while (true) {
+ if (read_bit_pattern(Indexed, inputStream)) {
+ if (!decodeIndexedField(inputStream))
+ return false;
+ } else if (read_bit_pattern(LiteralIncrementalIndexing, inputStream)) {
+ if (!decodeLiteralField(LiteralIncrementalIndexing, inputStream))
+ return false;
+ } else if (read_bit_pattern(LiteralNoIndexing, inputStream)) {
+ if (!decodeLiteralField(LiteralNoIndexing, inputStream))
+ return false;
+ } else if (read_bit_pattern(LiteralNeverIndexing, inputStream)) {
+ if (!decodeLiteralField(LiteralNeverIndexing, inputStream))
+ return false;
+ } else if (read_bit_pattern(SizeUpdate, inputStream)) {
+ if (!decodeSizeUpdate(inputStream))
+ return false;
+ } else {
+ return inputStream.bitLength() == inputStream.streamOffset();
+ }
+ }
+
+ return false;
+}
+
+quint32 Decoder::dynamicTableSize() const
+{
+ return lookupTable.dynamicDataSize();
+}
+
+void Decoder::setMaxDynamicTableSize(quint32 size)
+{
+ // Up to a caller (HTTP2 protocol handler)
+ // to validate this size first.
+ lookupTable.setMaxDynamicTableSize(size);
+}
+
+bool Decoder::decodeIndexedField(BitIStream &inputStream)
+{
+ quint32 index = 0;
+ if (inputStream.read(&index)) {
+ if (!index) {
+ // "The index value of 0 is not used.
+ // It MUST be treated as a decoding
+ // error if found in an indexed header
+ // field representation."
+ return false;
+ }
+
+ QByteArray name, value;
+ if (lookupTable.field(index, &name, &value))
+ return processDecodedField(Indexed, name, value);
+ } else {
+ handleStreamError(inputStream);
+ }
+
+ return false;
+}
+
+bool Decoder::decodeSizeUpdate(BitIStream &inputStream)
+{
+ // For now, just read and skip bits.
+ quint32 maxSize = 0;
+ if (inputStream.read(&maxSize)) {
+ if (!lookupTable.updateDynamicTableSize(maxSize))
+ return false;
+
+ return true;
+ }
+
+ handleStreamError(inputStream);
+ return false;
+}
+
+bool Decoder::decodeLiteralField(const BitPattern &fieldType, BitIStream &inputStream)
+{
+ // https://http2.github.io/http2-spec/compression.html
+ // 6.2.1, 6.2.2, 6.2.3
+ // Format for all 'literal' is similar,
+ // the difference - is how we update/not our lookup table.
+ quint32 index = 0;
+ if (inputStream.read(&index)) {
+ QByteArray name;
+ if (!index) {
+ // Read a string.
+ if (!inputStream.read(&name)) {
+ handleStreamError(inputStream);
+ return false;
+ }
+ } else {
+ if (!lookupTable.fieldName(index, &name))
+ return false;
+ }
+
+ QByteArray value;
+ if (inputStream.read(&value))
+ return processDecodedField(fieldType, name, value);
+ }
+
+ handleStreamError(inputStream);
+
+ return false;
+}
+
+bool Decoder::processDecodedField(const BitPattern &fieldType,
+ const QByteArray &name,
+ const QByteArray &value)
+{
+ if (fieldType == LiteralIncrementalIndexing) {
+ if (!lookupTable.prependField(name, value))
+ return false;
+ }
+
+ header.push_back(HeaderField(name, value));
+ return true;
+}
+
+void Decoder::handleStreamError(BitIStream &inputStream)
+{
+ const auto errorCode(inputStream.error());
+ if (errorCode == StreamError::NoError)
+ return;
+
+ // For now error handling not needed here,
+ // HTTP2 layer will end with session error/COMPRESSION_ERROR.
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/http2/hpack_p.h b/src/network/access/http2/hpack_p.h
new file mode 100644
index 0000000000..6a1d30d87b
--- /dev/null
+++ b/src/network/access/http2/hpack_p.h
@@ -0,0 +1,155 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 HPACK_P_H
+#define HPACK_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "hpacktable_p.h"
+
+#include <QtCore/qglobal.h>
+
+#include <vector>
+
+QT_BEGIN_NAMESPACE
+
+class QByteArray;
+
+namespace HPack
+{
+
+using HttpHeader = std::vector<HeaderField>;
+HeaderSize header_size(const HttpHeader &header);
+
+class Q_AUTOTEST_EXPORT Encoder
+{
+public:
+ Encoder(quint32 maxTableSize, bool compressStrings);
+
+ quint32 dynamicTableSize() const;
+
+ bool encodeRequest(class BitOStream &outputStream,
+ const HttpHeader &header);
+ bool encodeResponse(BitOStream &outputStream,
+ const HttpHeader &header);
+
+ bool encodeSizeUpdate(BitOStream &outputStream,
+ quint32 newSize);
+
+ void setMaxDynamicTableSize(quint32 size);
+
+private:
+ bool encodeRequestPseudoHeaders(BitOStream &outputStream,
+ const HttpHeader &header);
+ bool encodeHeaderField(BitOStream &outputStream,
+ const HeaderField &field);
+ bool encodeMethod(BitOStream &outputStream,
+ const HeaderField &field);
+
+ bool encodeResponsePseudoHeaders(BitOStream &outputStream,
+ const HttpHeader &header);
+
+ bool encodeIndexedField(BitOStream &outputStream, quint32 index) const;
+
+
+ bool encodeLiteralField(BitOStream &outputStream,
+ const struct BitPattern &fieldType,
+ quint32 nameIndex,
+ const QByteArray &value,
+ bool withCompression);
+
+ bool encodeLiteralField(BitOStream &outputStream,
+ const BitPattern &fieldType,
+ const QByteArray &name,
+ const QByteArray &value,
+ bool withCompression);
+
+ FieldLookupTable lookupTable;
+ bool compressStrings;
+};
+
+class Q_AUTOTEST_EXPORT Decoder
+{
+public:
+ Decoder(quint32 maxTableSize);
+
+ bool decodeHeaderFields(class BitIStream &inputStream);
+
+ const HttpHeader &decodedHeader() const
+ {
+ return header;
+ }
+
+ quint32 dynamicTableSize() const;
+
+ void setMaxDynamicTableSize(quint32 size);
+
+private:
+
+ bool decodeIndexedField(BitIStream &inputStream);
+ bool decodeSizeUpdate(BitIStream &inputStream);
+ bool decodeLiteralField(const BitPattern &fieldType,
+ BitIStream &inputStream);
+
+ bool processDecodedField(const BitPattern &fieldType,
+ const QByteArray &name,
+ const QByteArray &value);
+
+ void handleStreamError(BitIStream &inputStream);
+
+ HttpHeader header;
+ FieldLookupTable lookupTable;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
+
diff --git a/src/network/access/http2/hpacktable.cpp b/src/network/access/http2/hpacktable.cpp
new file mode 100644
index 0000000000..db9574e2bc
--- /dev/null
+++ b/src/network/access/http2/hpacktable.cpp
@@ -0,0 +1,533 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 "hpacktable_p.h"
+
+#include <QtCore/qdebug.h>
+
+#include <algorithm>
+#include <cstring>
+#include <limits>
+
+
+QT_BEGIN_NAMESPACE
+
+namespace HPack
+{
+
+HeaderSize entry_size(const QByteArray &name, const QByteArray &value)
+{
+ // 32 comes from HPACK:
+ // "4.1 Calculating Table Size
+ // Note: The additional 32 octets account for an estimated overhead associated
+ // with an entry. For example, an entry structure using two 64-bit pointers
+ // to reference the name and the value of the entry and two 64-bit integers
+ // for counting the number of references to the name and value would have
+ // 32 octets of overhead."
+
+ const unsigned sum = unsigned(name.size()) + value.size();
+ if (std::numeric_limits<unsigned>::max() - 32 < sum)
+ return HeaderSize();
+ if (sum + 32 > std::numeric_limits<quint32>::max())
+ return HeaderSize();
+ return HeaderSize(true, quint32(sum + 32));
+}
+
+namespace
+{
+
+int compare(const QByteArray &lhs, const QByteArray &rhs)
+{
+ if (const int minLen = std::min(lhs.size(), rhs.size())) {
+ // We use memcmp, since strings in headers are allowed
+ // to contain '\0'.
+ const int cmp = std::memcmp(lhs.constData(), rhs.constData(), minLen);
+ if (cmp)
+ return cmp;
+ }
+
+ return lhs.size() - rhs.size();
+}
+
+} // unnamed namespace
+
+FieldLookupTable::SearchEntry::SearchEntry()
+ : field(),
+ chunk(),
+ offset(),
+ table()
+{
+}
+
+FieldLookupTable::SearchEntry::SearchEntry(const HeaderField *f,
+ const Chunk *c,
+ quint32 o,
+ const FieldLookupTable *t)
+ : field(f),
+ chunk(c),
+ offset(o),
+ table(t)
+{
+ Q_ASSERT(field);
+}
+
+bool FieldLookupTable::SearchEntry::operator < (const SearchEntry &rhs)const
+{
+ Q_ASSERT(field);
+ Q_ASSERT(rhs.field);
+
+ int cmp = compare(field->name, rhs.field->name);
+ if (cmp)
+ return cmp < 0;
+
+ cmp = compare(field->value, rhs.field->value);
+ if (cmp)
+ return cmp < 0;
+
+ if (!chunk) // 'this' is not in the searchIndex.
+ return rhs.chunk;
+
+ if (!rhs.chunk) // not in the searchIndex.
+ return false;
+
+ Q_ASSERT(table);
+ Q_ASSERT(rhs.table == table);
+
+ const quint32 leftChunkIndex = table->indexOfChunk(chunk);
+ const quint32 rightChunkIndex = rhs.table->indexOfChunk(rhs.chunk);
+
+ // Later added - smaller is chunk index (since we push_front).
+ if (leftChunkIndex != rightChunkIndex)
+ return leftChunkIndex > rightChunkIndex;
+
+ // Later added - smaller is offset.
+ return offset > rhs.offset;
+}
+
+// This data is from HPACK's specs and it's quite
+// conveniently sorted == works with binary search as it is.
+// Later this can probably change and instead of simple
+// vector we'll just reuse FieldLookupTable.
+// TODO: it makes sense to generate this table while ...
+// configuring/building Qt (some script downloading/parsing/generating
+// would be quite handy).
+const std::vector<HeaderField> &staticTable()
+{
+ static std::vector<HeaderField> table = {
+ {":authority", ""},
+ {":method", "GET"},
+ {":method", "POST"},
+ {":path", "/"},
+ {":path", "/index.html"},
+ {":scheme", "http"},
+ {":scheme", "https"},
+ {":status", "200"},
+ {":status", "204"},
+ {":status", "206"},
+ {":status", "304"},
+ {":status", "400"},
+ {":status", "404"},
+ {":status", "500"},
+ {"accept-charset", ""},
+ {"accept-encoding", "gzip, deflate"},
+ {"accept-language", ""},
+ {"accept-ranges", ""},
+ {"accept", ""},
+ {"access-control-allow-origin", ""},
+ {"age", ""},
+ {"allow", ""},
+ {"authorization", ""},
+ {"cache-control", ""},
+ {"content-disposition", ""},
+ {"content-encoding", ""},
+ {"content-language", ""},
+ {"content-length", ""},
+ {"content-location", ""},
+ {"content-range", ""},
+ {"content-type", ""},
+ {"cookie", ""},
+ {"date", ""},
+ {"etag", ""},
+ {"expect", ""},
+ {"expires", ""},
+ {"from", ""},
+ {"host", ""},
+ {"if-match", ""},
+ {"if-modified-since", ""},
+ {"if-none-match", ""},
+ {"if-range", ""},
+ {"if-unmodified-since", ""},
+ {"last-modified", ""},
+ {"link", ""},
+ {"location", ""},
+ {"max-forwards", ""},
+ {"proxy-authenticate", ""},
+ {"proxy-authorization", ""},
+ {"range", ""},
+ {"referer", ""},
+ {"refresh", ""},
+ {"retry-after", ""},
+ {"server", ""},
+ {"set-cookie", ""},
+ {"strict-transport-security", ""},
+ {"transfer-encoding", ""},
+ {"user-agent", ""},
+ {"vary", ""},
+ {"via", ""},
+ {"www-authenticate", ""}
+ };
+
+ return table;
+}
+
+FieldLookupTable::FieldLookupTable(quint32 maxSize, bool use)
+ : maxTableSize(maxSize),
+ tableCapacity(maxSize),
+ useIndex(use),
+ nDynamic(),
+ begin(),
+ end(),
+ dataSize()
+{
+}
+
+
+bool FieldLookupTable::prependField(const QByteArray &name, const QByteArray &value)
+{
+ const auto entrySize = entry_size(name, value);
+ if (!entrySize.first)
+ return false;
+
+ if (entrySize.second > tableCapacity) {
+ clearDynamicTable();
+ return true;
+ }
+
+ while (nDynamic && tableCapacity - dataSize < entrySize.second)
+ evictEntry();
+
+ if (!begin) {
+ // Either no more space or empty table ...
+ chunks.push_front(ChunkPtr(new Chunk(ChunkSize)));
+ end += ChunkSize;
+ begin = ChunkSize;
+ }
+
+ --begin;
+
+ dataSize += entrySize.second;
+ ++nDynamic;
+
+ auto &newField = front();
+ newField.name = name;
+ newField.value = value;
+
+ if (useIndex) {
+ const auto result = searchIndex.insert(frontKey());
+ Q_UNUSED(result) Q_ASSERT(result.second);
+ }
+
+ return true;
+}
+
+void FieldLookupTable::evictEntry()
+{
+ if (!nDynamic)
+ return;
+
+ Q_ASSERT(end != begin);
+
+ if (useIndex) {
+ const auto res = searchIndex.erase(backKey());
+ Q_UNUSED(res) Q_ASSERT(res == 1);
+ }
+
+ const HeaderField &field = back();
+ const auto entrySize = entry_size(field);
+ Q_ASSERT(entrySize.first);
+ Q_ASSERT(dataSize >= entrySize.second);
+ dataSize -= entrySize.second;
+
+ --nDynamic;
+ --end;
+
+ if (end == begin) {
+ Q_ASSERT(chunks.size() == 1);
+ end = ChunkSize;
+ begin = end;
+ } else if (!(end % ChunkSize)) {
+ chunks.pop_back();
+ }
+}
+
+quint32 FieldLookupTable::numberOfEntries() const
+{
+ return quint32(staticTable().size()) + nDynamic;
+}
+
+quint32 FieldLookupTable::numberOfStaticEntries() const
+{
+ return quint32(staticTable().size());
+}
+
+quint32 FieldLookupTable::numberOfDynamicEntries() const
+{
+ return nDynamic;
+}
+
+quint32 FieldLookupTable::dynamicDataSize() const
+{
+ return dataSize;
+}
+
+void FieldLookupTable::clearDynamicTable()
+{
+ searchIndex.clear();
+ chunks.clear();
+ begin = 0;
+ end = 0;
+ nDynamic = 0;
+ dataSize = 0;
+}
+
+bool FieldLookupTable::indexIsValid(quint32 index) const
+{
+ return index && index <= staticTable().size() + nDynamic;
+}
+
+quint32 FieldLookupTable::indexOf(const QByteArray &name, const QByteArray &value)const
+{
+ // Start from the static part first:
+ const auto &table = staticTable();
+ const HeaderField field(name, value);
+ const auto staticPos = std::lower_bound(table.begin(), table.end(), field,
+ [](const HeaderField &lhs, const HeaderField &rhs) {
+ int cmp = compare(lhs.name, rhs.name);
+ if (cmp)
+ return cmp < 0;
+ return compare(lhs.value, rhs.value) < 0;
+ });
+ if (staticPos != table.end()) {
+ if (staticPos->name == name && staticPos->value == value)
+ return staticPos - table.begin() + 1;
+ }
+
+ // Now we have to lookup in our dynamic part ...
+ if (!useIndex) {
+ qCritical("lookup in dynamic table requires search index enabled");
+ return 0;
+ }
+
+ const SearchEntry key(&field, nullptr, 0, this);
+ const auto pos = searchIndex.lower_bound(key);
+ if (pos != searchIndex.end()) {
+ const HeaderField &found = *pos->field;
+ if (found.name == name && found.value == value)
+ return keyToIndex(*pos);
+ }
+
+ return 0;
+}
+
+quint32 FieldLookupTable::indexOf(const QByteArray &name) const
+{
+ // Start from the static part first:
+ const auto &table = staticTable();
+ const HeaderField field(name, QByteArray());
+ const auto staticPos = std::lower_bound(table.begin(), table.end(), field,
+ [](const HeaderField &lhs, const HeaderField &rhs) {
+ return compare(lhs.name, rhs.name) < 0;
+ });
+ if (staticPos != table.end()) {
+ if (staticPos->name == name)
+ return staticPos - table.begin() + 1;
+ }
+
+ // Now we have to lookup in our dynamic part ...
+ if (!useIndex) {
+ qCritical("lookup in dynamic table requires search index enabled");
+ return 0;
+ }
+
+ const SearchEntry key(&field, nullptr, 0, this);
+ const auto pos = searchIndex.lower_bound(key);
+ if (pos != searchIndex.end()) {
+ const HeaderField &found = *pos->field;
+ if (found.name == name)
+ return keyToIndex(*pos);
+ }
+
+ return 0;
+}
+
+bool FieldLookupTable::field(quint32 index, QByteArray *name, QByteArray *value) const
+{
+ Q_ASSERT(name);
+ Q_ASSERT(value);
+
+ if (!indexIsValid(index))
+ return false;
+
+ const auto &table = staticTable();
+ if (index - 1 < table.size()) {
+ *name = table[index - 1].name;
+ *value = table[index - 1].value;
+ return true;
+ }
+
+ index = index - 1 - quint32(table.size()) + begin;
+ const auto chunkIndex = index / ChunkSize;
+ Q_ASSERT(chunkIndex < chunks.size());
+ const auto offset = index % ChunkSize;
+ const HeaderField &found = (*chunks[chunkIndex])[offset];
+ *name = found.name;
+ *value = found.value;
+
+ return true;
+}
+
+bool FieldLookupTable::fieldName(quint32 index, QByteArray *dst) const
+{
+ Q_ASSERT(dst);
+ return field(index, dst, &dummyDst);
+}
+
+bool FieldLookupTable::fieldValue(quint32 index, QByteArray *dst) const
+{
+ Q_ASSERT(dst);
+ return field(index, &dummyDst, dst);
+}
+
+const HeaderField &FieldLookupTable::front() const
+{
+ Q_ASSERT(nDynamic && begin != end && chunks.size());
+ return (*chunks[0])[begin];
+}
+
+HeaderField &FieldLookupTable::front()
+{
+ Q_ASSERT(nDynamic && begin != end && chunks.size());
+ return (*chunks[0])[begin];
+}
+
+const HeaderField &FieldLookupTable::back() const
+{
+ Q_ASSERT(nDynamic && end && end != begin);
+
+ const quint32 absIndex = end - 1;
+ const quint32 chunkIndex = absIndex / ChunkSize;
+ Q_ASSERT(chunkIndex < chunks.size());
+ const quint32 offset = absIndex % ChunkSize;
+ return (*chunks[chunkIndex])[offset];
+}
+
+quint32 FieldLookupTable::indexOfChunk(const Chunk *chunk) const
+{
+ Q_ASSERT(chunk);
+
+ for (size_type i = 0; i < chunks.size(); ++i) {
+ if (chunks[i].get() == chunk)
+ return quint32(i);
+ }
+
+ Q_UNREACHABLE();
+ return 0;
+}
+
+quint32 FieldLookupTable::keyToIndex(const SearchEntry &key) const
+{
+ Q_ASSERT(key.chunk);
+
+ const auto chunkIndex = indexOfChunk(key.chunk);
+ const auto offset = key.offset;
+ Q_ASSERT(offset < ChunkSize);
+ Q_ASSERT(chunkIndex || offset >= begin);
+
+ return quint32(offset + chunkIndex * ChunkSize - begin + 1 + staticTable().size());
+}
+
+FieldLookupTable::SearchEntry FieldLookupTable::frontKey() const
+{
+ Q_ASSERT(chunks.size() && end != begin);
+ return SearchEntry(&front(), chunks.front().get(), begin, this);
+}
+
+FieldLookupTable::SearchEntry FieldLookupTable::backKey() const
+{
+ Q_ASSERT(chunks.size() && end != begin);
+
+ const HeaderField &field = back();
+ const quint32 absIndex = end - 1;
+ const auto offset = absIndex % ChunkSize;
+ const auto chunk = chunks[absIndex / ChunkSize].get();
+
+ return SearchEntry(&field, chunk, offset, this);
+}
+
+bool FieldLookupTable::updateDynamicTableSize(quint32 size)
+{
+ if (!size) {
+ clearDynamicTable();
+ return true;
+ }
+
+ if (size > maxTableSize)
+ return false;
+
+ tableCapacity = size;
+ while (nDynamic && dataSize > tableCapacity)
+ evictEntry();
+
+ return true;
+}
+
+void FieldLookupTable::setMaxDynamicTableSize(quint32 size)
+{
+ // This is for an external user, for example, HTTP2 protocol
+ // layer that can receive SETTINGS frame from its peer.
+ // No validity checks here, up to this external user.
+ // We update max size and capacity (this can also result in
+ // items evicted or even dynamic table completely cleared).
+ maxTableSize = size;
+ updateDynamicTableSize(size);
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/http2/hpacktable_p.h b/src/network/access/http2/hpacktable_p.h
new file mode 100644
index 0000000000..aaea89b986
--- /dev/null
+++ b/src/network/access/http2/hpacktable_p.h
@@ -0,0 +1,237 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 HPACKTABLE_P_H
+#define HPACKTABLE_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qglobal.h>
+#include <QtCore/qpair.h>
+
+#include <vector>
+#include <memory>
+#include <deque>
+#include <set>
+
+QT_BEGIN_NAMESPACE
+
+namespace HPack
+{
+
+struct Q_AUTOTEST_EXPORT HeaderField
+{
+ HeaderField()
+ {
+ }
+
+ HeaderField(const QByteArray &n, const QByteArray &v)
+ : name(n),
+ value(v)
+ {
+ }
+
+ bool operator == (const HeaderField &rhs) const
+ {
+ return name == rhs.name && value == rhs.value;
+ }
+
+ QByteArray name;
+ QByteArray value;
+};
+
+using HeaderSize = QPair<bool, quint32>;
+
+HeaderSize entry_size(const QByteArray &name, const QByteArray &value);
+
+inline HeaderSize entry_size(const HeaderField &entry)
+{
+ return entry_size(entry.name, entry.value);
+}
+
+/*
+ Lookup table consists of two parts (HPACK, 2.3):
+ the immutable static table (pre-defined by HPACK's specs)
+ and dynamic table which is updated while
+ compressing/decompressing headers.
+
+ Table must provide/implement:
+ 1. Fast random access - we read fields' indices from
+ HPACK's bit stream.
+ 2. FIFO for dynamic part - to push new items to the front
+ and evict them from the back (HPACK, 2.3.2).
+ 3. Fast lookup - encoder receives pairs of strings
+ (name|value) and it has to find an index for a pair
+ as the whole or for a name at least (if it's already
+ in either static or dynamic table).
+
+ Static table is an immutable vector.
+
+ Dynamic part is implemented in a way similar to std::deque -
+ it's a vector of pointers to chunks. Each chunk is a vector of
+ (name|value) pairs. Once allocated with a fixed size, chunk
+ never re-allocates its data, so entries' addresses do not change.
+ We add new chunks prepending them to the front of a vector,
+ in each chunk we fill (name|value) pairs starting from the back
+ of the chunk (this simplifies item eviction/FIFO).
+ Given a 'linear' index we can find a chunk number and
+ offset in this chunk - random access.
+
+ Lookup in a static part is straightforward:
+ it's an (immutable) vector, data is sorted,
+ contains no duplicates, we use binary search comparing string values.
+
+ To provide a lookup in dynamic table faster than a linear search,
+ we have an std::set of 'SearchEntries', where each entry contains:
+ - a pointer to a (name|value) pair (to compare
+ name|value strings).
+ - a pointer to a chunk containing this pair and
+ - an offset within this chunk - to calculate a
+ 'linear' index.
+
+ Entries in a table can be duplicated (HPACK, 2.3.2),
+ if we evict an entry, we must update our index removing
+ the exactly right key, thus keys in this set are sorted
+ by name|value pairs first, and then by chunk index/offset
+ (so that NewSearchEntryKey < OldSearchEntry even if strings
+ are equal).
+*/
+
+class Q_AUTOTEST_EXPORT FieldLookupTable
+{
+public:
+ enum
+ {
+ ChunkSize = 16,
+ DefaultSize = 4096 // Recommended by HTTP2.
+ };
+
+ FieldLookupTable(quint32 maxTableSize, bool useIndex);
+
+ bool prependField(const QByteArray &name, const QByteArray &value);
+ void evictEntry();
+
+ quint32 numberOfEntries() const;
+ quint32 numberOfStaticEntries() const;
+ quint32 numberOfDynamicEntries() const;
+ quint32 dynamicDataSize() const;
+ void clearDynamicTable();
+
+ bool indexIsValid(quint32 index) const;
+ quint32 indexOf(const QByteArray &name, const QByteArray &value) const;
+ quint32 indexOf(const QByteArray &name) const;
+ bool field(quint32 index, QByteArray *name, QByteArray *value) const;
+ bool fieldName(quint32 index, QByteArray *dst) const;
+ bool fieldValue(quint32 index, QByteArray *dst) const;
+
+ bool updateDynamicTableSize(quint32 size);
+ void setMaxDynamicTableSize(quint32 size);
+
+private:
+ // Table's maximum size is controlled
+ // by SETTINGS_HEADER_TABLE_SIZE (HTTP/2, 6.5.2).
+ quint32 maxTableSize;
+ // The tableCapacity is how many bytes the table
+ // can currently hold. It cannot exceed maxTableSize.
+ // It can be modified by a special message in
+ // the HPACK bit stream (HPACK, 6.3).
+ quint32 tableCapacity;
+
+ using Chunk = std::vector<HeaderField>;
+ using ChunkPtr = std::unique_ptr<Chunk>;
+ std::deque<ChunkPtr> chunks;
+ using size_type = std::deque<ChunkPtr>::size_type;
+
+ struct SearchEntry;
+ friend struct SearchEntry;
+
+ struct SearchEntry
+ {
+ SearchEntry();
+ SearchEntry(const HeaderField *f, const Chunk *c,
+ quint32 o, const FieldLookupTable *t);
+
+ const HeaderField *field;
+ const Chunk *chunk;
+ const quint32 offset;
+ const FieldLookupTable *table;
+
+ bool operator < (const SearchEntry &rhs) const;
+ };
+
+ bool useIndex;
+ std::set<SearchEntry> searchIndex;
+
+ SearchEntry frontKey() const;
+ SearchEntry backKey() const;
+
+ bool fieldAt(quint32 index, HeaderField *field) const;
+
+ const HeaderField &front() const;
+ HeaderField &front();
+ const HeaderField &back() const;
+
+ quint32 nDynamic;
+ quint32 begin;
+ quint32 end;
+ quint32 dataSize;
+
+ quint32 indexOfChunk(const Chunk *chunk) const;
+ quint32 keyToIndex(const SearchEntry &key) const;
+
+ mutable QByteArray dummyDst;
+
+ Q_DISABLE_COPY(FieldLookupTable);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/http2/http2.pri b/src/network/access/http2/http2.pri
new file mode 100644
index 0000000000..e9f30aeb4a
--- /dev/null
+++ b/src/network/access/http2/http2.pri
@@ -0,0 +1,17 @@
+HEADERS += \
+ access/http2/bitstreams_p.h \
+ access/http2/huffman_p.h \
+ access/http2/hpack_p.h \
+ access/http2/hpacktable_p.h \
+ access/http2/http2frames_p.h \
+ access/http2/http2streams_p.h \
+ access/http2/http2protocol_p.h
+
+SOURCES += \
+ access/http2/bitstreams.cpp \
+ access/http2/huffman.cpp \
+ access/http2/hpack.cpp \
+ access/http2/hpacktable.cpp \
+ access/http2/http2frames.cpp \
+ access/http2/http2streams.cpp \
+ access/http2/http2protocol.cpp
diff --git a/src/network/access/http2/http2frames.cpp b/src/network/access/http2/http2frames.cpp
new file mode 100644
index 0000000000..978bee09b1
--- /dev/null
+++ b/src/network/access/http2/http2frames.cpp
@@ -0,0 +1,495 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 "http2frames_p.h"
+
+#include <QtNetwork/qabstractsocket.h>
+
+#include <algorithm>
+#include <utility>
+
+QT_BEGIN_NAMESPACE
+
+namespace Http2
+{
+
+// HTTP/2 frames are defined by RFC7540, clauses 4 and 6.
+
+Frame::Frame()
+ : buffer(frameHeaderSize)
+{
+}
+
+FrameType Frame::type() const
+{
+ Q_ASSERT(buffer.size() >= frameHeaderSize);
+
+ if (int(buffer[3]) >= int(FrameType::LAST_FRAME_TYPE))
+ return FrameType::LAST_FRAME_TYPE;
+
+ return FrameType(buffer[3]);
+}
+
+quint32 Frame::streamID() const
+{
+ Q_ASSERT(buffer.size() >= frameHeaderSize);
+ return qFromBigEndian<quint32>(&buffer[5]);
+}
+
+FrameFlags Frame::flags() const
+{
+ Q_ASSERT(buffer.size() >= frameHeaderSize);
+ return FrameFlags(buffer[4]);
+}
+
+quint32 Frame::payloadSize() const
+{
+ Q_ASSERT(buffer.size() >= frameHeaderSize);
+ return buffer[0] << 16 | buffer[1] << 8 | buffer[2];
+}
+
+uchar Frame::padding() const
+{
+ Q_ASSERT(validateHeader() == FrameStatus::goodFrame);
+
+ if (!flags().testFlag(FrameFlag::PADDED))
+ return 0;
+
+ switch (type()) {
+ case FrameType::DATA:
+ case FrameType::PUSH_PROMISE:
+ case FrameType::HEADERS:
+ Q_ASSERT(buffer.size() > frameHeaderSize);
+ return buffer[frameHeaderSize];
+ default:
+ return 0;
+ }
+}
+
+bool Frame::priority(quint32 *streamID, uchar *weight) const
+{
+ Q_ASSERT(validatePayload() == FrameStatus::goodFrame);
+
+ if (buffer.size() <= frameHeaderSize)
+ return false;
+
+ const uchar *src = &buffer[0] + frameHeaderSize;
+ if (type() == FrameType::HEADERS && flags().testFlag(FrameFlag::PADDED))
+ ++src;
+
+ if ((type() == FrameType::HEADERS && flags().testFlag(FrameFlag::PRIORITY))
+ || type() == FrameType::PRIORITY) {
+ if (streamID)
+ *streamID = qFromBigEndian<quint32>(src);
+ if (weight)
+ *weight = src[4];
+ return true;
+ }
+
+ return false;
+}
+
+FrameStatus Frame::validateHeader() const
+{
+ // Should be called only on a frame with
+ // a complete header.
+ Q_ASSERT(buffer.size() >= frameHeaderSize);
+
+ const auto framePayloadSize = payloadSize();
+ // 4.2 Frame Size
+ if (framePayloadSize > maxPayloadSize)
+ return FrameStatus::sizeError;
+
+ switch (type()) {
+ case FrameType::SETTINGS:
+ // SETTINGS ACK can not have any payload.
+ // The payload of a SETTINGS frame consists of zero
+ // or more parameters, each consisting of an unsigned
+ // 16-bit setting identifier and an unsigned 32-bit value.
+ // Thus the payload size must be a multiple of 6.
+ if (flags().testFlag(FrameFlag::ACK) ? framePayloadSize : framePayloadSize % 6)
+ return FrameStatus::sizeError;
+ break;
+ case FrameType::PRIORITY:
+ // 6.3 PRIORITY
+ if (framePayloadSize != 5)
+ return FrameStatus::sizeError;
+ break;
+ case FrameType::PING:
+ // 6.7 PING
+ if (framePayloadSize != 8)
+ return FrameStatus::sizeError;
+ break;
+ case FrameType::GOAWAY:
+ // 6.8 GOAWAY
+ if (framePayloadSize < 8)
+ return FrameStatus::sizeError;
+ break;
+ case FrameType::RST_STREAM:
+ case FrameType::WINDOW_UPDATE:
+ // 6.4 RST_STREAM, 6.9 WINDOW_UPDATE
+ if (framePayloadSize != 4)
+ return FrameStatus::sizeError;
+ break;
+ case FrameType::PUSH_PROMISE:
+ // 6.6 PUSH_PROMISE
+ if (framePayloadSize < 4)
+ return FrameStatus::sizeError;
+ default:
+ // DATA/HEADERS/CONTINUATION will be verified
+ // when we have payload.
+ // Frames of unknown types are ignored (5.1)
+ break;
+ }
+
+ return FrameStatus::goodFrame;
+}
+
+FrameStatus Frame::validatePayload() const
+{
+ // Should be called only on a complete frame with a valid header.
+ Q_ASSERT(validateHeader() == FrameStatus::goodFrame);
+
+ // Ignored, 5.1
+ if (type() == FrameType::LAST_FRAME_TYPE)
+ return FrameStatus::goodFrame;
+
+ auto size = payloadSize();
+ Q_ASSERT(buffer.size() >= frameHeaderSize && size == buffer.size() - frameHeaderSize);
+
+ const uchar *src = size ? &buffer[0] + frameHeaderSize : nullptr;
+ const auto frameFlags = flags();
+ switch (type()) {
+ // 6.1 DATA, 6.2 HEADERS
+ case FrameType::DATA:
+ case FrameType::HEADERS:
+ if (frameFlags.testFlag(FrameFlag::PADDED)) {
+ if (!size || size < src[0])
+ return FrameStatus::sizeError;
+ size -= src[0];
+ }
+ if (type() == FrameType::HEADERS && frameFlags.testFlag(FrameFlag::PRIORITY)) {
+ if (size < 5)
+ return FrameStatus::sizeError;
+ }
+ break;
+ // 6.6 PUSH_PROMISE
+ case FrameType::PUSH_PROMISE:
+ if (frameFlags.testFlag(FrameFlag::PADDED)) {
+ if (!size || size < src[0])
+ return FrameStatus::sizeError;
+ size -= src[0];
+ }
+
+ if (size < 4)
+ return FrameStatus::sizeError;
+ break;
+ default:
+ break;
+ }
+
+ return FrameStatus::goodFrame;
+}
+
+
+quint32 Frame::dataSize() const
+{
+ Q_ASSERT(validatePayload() == FrameStatus::goodFrame);
+
+ quint32 size = payloadSize();
+ if (const uchar pad = padding()) {
+ // + 1 one for a byte with padding number itself:
+ size -= pad + 1;
+ }
+
+ if (priority())
+ size -= 5;
+
+ return size;
+}
+
+const uchar *Frame::dataBegin() const
+{
+ Q_ASSERT(validatePayload() == FrameStatus::goodFrame);
+ if (buffer.size() <= frameHeaderSize)
+ return nullptr;
+
+ const uchar *src = &buffer[0] + frameHeaderSize;
+ if (padding())
+ ++src;
+
+ if (priority())
+ src += 5;
+
+ return src;
+}
+
+FrameStatus FrameReader::read(QAbstractSocket &socket)
+{
+ if (offset < frameHeaderSize) {
+ if (!readHeader(socket))
+ return FrameStatus::incompleteFrame;
+
+ const auto status = frame.validateHeader();
+ if (status != FrameStatus::goodFrame) {
+ // No need to read any payload.
+ return status;
+ }
+
+ if (Http2PredefinedParameters::maxFrameSize < frame.payloadSize())
+ return FrameStatus::sizeError;
+
+ frame.buffer.resize(frame.payloadSize() + frameHeaderSize);
+ }
+
+ if (offset < frame.buffer.size() && !readPayload(socket))
+ return FrameStatus::incompleteFrame;
+
+ // Reset the offset, our frame can be re-used
+ // now (re-read):
+ offset = 0;
+
+ return frame.validatePayload();
+}
+
+bool FrameReader::readHeader(QAbstractSocket &socket)
+{
+ Q_ASSERT(offset < frameHeaderSize);
+
+ auto &buffer = frame.buffer;
+ if (buffer.size() < frameHeaderSize)
+ buffer.resize(frameHeaderSize);
+
+ const auto chunkSize = socket.read(reinterpret_cast<char *>(&buffer[offset]),
+ frameHeaderSize - offset);
+ if (chunkSize > 0)
+ offset += chunkSize;
+
+ return offset == frameHeaderSize;
+}
+
+bool FrameReader::readPayload(QAbstractSocket &socket)
+{
+ Q_ASSERT(offset < frame.buffer.size());
+ Q_ASSERT(frame.buffer.size() > frameHeaderSize);
+
+ auto &buffer = frame.buffer;
+ // Casts and ugliness - to deal with MSVC. Values are guaranteed to fit into quint32.
+ const auto chunkSize = socket.read(reinterpret_cast<char *>(&buffer[offset]),
+ qint64(buffer.size() - offset));
+ if (chunkSize > 0)
+ offset += quint32(chunkSize);
+
+ return offset == buffer.size();
+}
+
+FrameWriter::FrameWriter()
+{
+}
+
+FrameWriter::FrameWriter(FrameType type, FrameFlags flags, quint32 streamID)
+{
+ start(type, flags, streamID);
+}
+
+void FrameWriter::start(FrameType type, FrameFlags flags, quint32 streamID)
+{
+ auto &buffer = frame.buffer;
+
+ buffer.resize(frameHeaderSize);
+ // The first three bytes - payload size, which is 0 for now.
+ buffer[0] = 0;
+ buffer[1] = 0;
+ buffer[2] = 0;
+
+ buffer[3] = uchar(type);
+ buffer[4] = uchar(flags);
+
+ qToBigEndian(streamID, &buffer[5]);
+}
+
+void FrameWriter::setPayloadSize(quint32 size)
+{
+ auto &buffer = frame.buffer;
+
+ Q_ASSERT(buffer.size() >= frameHeaderSize);
+ Q_ASSERT(size < maxPayloadSize);
+
+ buffer[0] = size >> 16;
+ buffer[1] = size >> 8;
+ buffer[2] = size;
+}
+
+void FrameWriter::setType(FrameType type)
+{
+ Q_ASSERT(frame.buffer.size() >= frameHeaderSize);
+ frame.buffer[3] = uchar(type);
+}
+
+void FrameWriter::setFlags(FrameFlags flags)
+{
+ Q_ASSERT(frame.buffer.size() >= frameHeaderSize);
+ frame.buffer[4] = uchar(flags);
+}
+
+void FrameWriter::addFlag(FrameFlag flag)
+{
+ setFlags(frame.flags() | flag);
+}
+
+void FrameWriter::append(const uchar *begin, const uchar *end)
+{
+ Q_ASSERT(begin && end);
+ Q_ASSERT(begin < end);
+
+ frame.buffer.insert(frame.buffer.end(), begin, end);
+ updatePayloadSize();
+}
+
+void FrameWriter::updatePayloadSize()
+{
+ const quint32 size = quint32(frame.buffer.size() - frameHeaderSize);
+ Q_ASSERT(size <= maxPayloadSize);
+ setPayloadSize(size);
+}
+
+bool FrameWriter::write(QAbstractSocket &socket) const
+{
+ auto &buffer = frame.buffer;
+ Q_ASSERT(buffer.size() >= frameHeaderSize);
+ // Do some sanity check first:
+
+ Q_ASSERT(int(frame.type()) < int(FrameType::LAST_FRAME_TYPE));
+ Q_ASSERT(frame.validateHeader() == FrameStatus::goodFrame);
+
+ const auto nWritten = socket.write(reinterpret_cast<const char *>(&buffer[0]),
+ buffer.size());
+ return nWritten != -1 && size_type(nWritten) == buffer.size();
+}
+
+bool FrameWriter::writeHEADERS(QAbstractSocket &socket, quint32 sizeLimit)
+{
+ auto &buffer = frame.buffer;
+ Q_ASSERT(buffer.size() >= frameHeaderSize);
+
+ if (sizeLimit > quint32(maxPayloadSize))
+ sizeLimit = quint32(maxPayloadSize);
+
+ if (quint32(buffer.size() - frameHeaderSize) <= sizeLimit) {
+ addFlag(FrameFlag::END_HEADERS);
+ updatePayloadSize();
+ return write(socket);
+ }
+
+ // Our HPACK block does not fit into the size limit, remove
+ // END_HEADERS bit from the first frame, we'll later set
+ // it on the last CONTINUATION frame:
+ setFlags(frame.flags() & ~FrameFlags(FrameFlag::END_HEADERS));
+ // Write a frame's header (not controlled by sizeLimit) and
+ // as many bytes of payload as we can within sizeLimit,
+ // then send CONTINUATION frames, as needed.
+ setPayloadSize(sizeLimit);
+ const quint32 firstChunkSize = frameHeaderSize + sizeLimit;
+ qint64 written = socket.write(reinterpret_cast<const char *>(&buffer[0]),
+ firstChunkSize);
+
+ if (written != qint64(firstChunkSize))
+ return false;
+
+ FrameWriter continuationWriter(FrameType::CONTINUATION, FrameFlag::EMPTY, frame.streamID());
+ quint32 offset = firstChunkSize;
+
+ while (offset != buffer.size()) {
+ const auto chunkSize = std::min(sizeLimit, quint32(buffer.size() - offset));
+ if (chunkSize + offset == buffer.size())
+ continuationWriter.addFlag(FrameFlag::END_HEADERS);
+ continuationWriter.setPayloadSize(chunkSize);
+ if (!continuationWriter.write(socket))
+ return false;
+ written = socket.write(reinterpret_cast<const char *>(&buffer[offset]),
+ chunkSize);
+ if (written != qint64(chunkSize))
+ return false;
+
+ offset += chunkSize;
+ }
+
+ return true;
+}
+
+bool FrameWriter::writeDATA(QAbstractSocket &socket, quint32 sizeLimit,
+ const uchar *src, quint32 size)
+{
+ // With DATA frame(s) we always have:
+ // 1) frame's header (9 bytes)
+ // 2) a separate payload (from QNonContiguousByteDevice).
+ // We either fit within a sizeLimit, or split into several
+ // DATA frames.
+
+ Q_ASSERT(src);
+
+ if (sizeLimit > quint32(maxPayloadSize))
+ sizeLimit = quint32(maxPayloadSize);
+ // We NEVER set END_STREAM, since QHttp2ProtocolHandler works with
+ // QNonContiguousByteDevice and this 'writeDATA' is probably
+ // not the last one for a given request.
+ // This has to be done externally (sending an empty DATA frame with END_STREAM).
+ for (quint32 offset = 0; offset != size;) {
+ const auto chunkSize = std::min(size - offset, sizeLimit);
+ setPayloadSize(chunkSize);
+ // Frame's header first:
+ if (!write(socket))
+ return false;
+ // Payload (if any):
+ if (chunkSize) {
+ const auto written = socket.write(reinterpret_cast<const char*>(src + offset),
+ chunkSize);
+ if (written != qint64(chunkSize))
+ return false;
+ }
+
+ offset += chunkSize;
+ }
+
+ return true;
+}
+
+} // Namespace Http2
+
+QT_END_NAMESPACE
diff --git a/src/network/access/http2/http2frames_p.h b/src/network/access/http2/http2frames_p.h
new file mode 100644
index 0000000000..84ba9c3662
--- /dev/null
+++ b/src/network/access/http2/http2frames_p.h
@@ -0,0 +1,185 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 HTTP2FRAMES_P_H
+#define HTTP2FRAMES_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 "http2protocol_p.h"
+#include "hpack_p.h"
+
+#include <QtCore/qendian.h>
+#include <QtCore/qglobal.h>
+
+#include <algorithm>
+#include <vector>
+
+QT_BEGIN_NAMESPACE
+
+class QHttp2ProtocolHandler;
+class QAbstractSocket;
+
+namespace Http2
+{
+
+struct Q_AUTOTEST_EXPORT Frame
+{
+ Frame();
+ // Reading these values without first forming a valid frame
+ // (either reading it from a socket or building it) will result
+ // in undefined behavior:
+ FrameType type() const;
+ quint32 streamID() const;
+ FrameFlags flags() const;
+ quint32 payloadSize() const;
+ uchar padding() const;
+ // In HTTP/2 a stream's priority is specified by its weight
+ // and a stream (id) it depends on:
+ bool priority(quint32 *streamID = nullptr,
+ uchar *weight = nullptr) const;
+
+ FrameStatus validateHeader() const;
+ FrameStatus validatePayload() const;
+
+ // Number of payload bytes without padding and/or priority
+ quint32 dataSize() const;
+ // Beginning of payload without priority/padding
+ // bytes.
+ const uchar *dataBegin() const;
+
+ std::vector<uchar> buffer;
+};
+
+class Q_AUTOTEST_EXPORT FrameReader
+{
+public:
+ FrameStatus read(QAbstractSocket &socket);
+
+ Frame &inboundFrame()
+ {
+ return frame;
+ }
+private:
+ bool readHeader(QAbstractSocket &socket);
+ bool readPayload(QAbstractSocket &socket);
+
+ quint32 offset = 0;
+ Frame frame;
+};
+
+class Q_AUTOTEST_EXPORT FrameWriter
+{
+public:
+ using payload_type = std::vector<uchar>;
+ using size_type = payload_type::size_type;
+
+ FrameWriter();
+ FrameWriter(FrameType type, FrameFlags flags, quint32 streamID);
+
+ Frame &outboundFrame()
+ {
+ return frame;
+ }
+
+ // Frame 'builders':
+ void start(FrameType type, FrameFlags flags, quint32 streamID);
+ void setPayloadSize(quint32 size);
+ void setType(FrameType type);
+ void setFlags(FrameFlags flags);
+ void addFlag(FrameFlag flag);
+
+ // All append functions also update frame's payload
+ // length.
+ template<typename ValueType>
+ void append(ValueType val)
+ {
+ uchar wired[sizeof val] = {};
+ qToBigEndian(val, wired);
+ append(wired, wired + sizeof val);
+ }
+ void append(uchar val)
+ {
+ frame.buffer.push_back(val);
+ updatePayloadSize();
+ }
+ void append(Settings identifier)
+ {
+ append(quint16(identifier));
+ }
+ void append(const payload_type &payload)
+ {
+ append(&payload[0], &payload[0] + payload.size());
+ }
+
+ void append(const uchar *begin, const uchar *end);
+
+ // Write as a single frame:
+ bool write(QAbstractSocket &socket) const;
+ // Two types of frames we are sending are affected by
+ // frame size limits: HEADERS and DATA. HEADERS' payload
+ // (hpacked HTTP headers, following a frame header)
+ // is always in our 'buffer', we send the initial HEADERS
+ // frame first and then CONTINUTATION frame(s) if needed:
+ bool writeHEADERS(QAbstractSocket &socket, quint32 sizeLimit);
+ // With DATA frames the actual payload is never in our 'buffer',
+ // it's a 'readPointer' from QNonContiguousData. We split
+ // this payload as needed into DATA frames with correct
+ // payload size fitting into frame size limit:
+ bool writeDATA(QAbstractSocket &socket, quint32 sizeLimit,
+ const uchar *src, quint32 size);
+private:
+ void updatePayloadSize();
+ Frame frame;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/http2/http2protocol.cpp b/src/network/access/http2/http2protocol.cpp
new file mode 100644
index 0000000000..7f788a6f42
--- /dev/null
+++ b/src/network/access/http2/http2protocol.cpp
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 <QtCore/qstring.h>
+
+#include "http2protocol_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(QT_HTTP2, "qt.network.http2")
+
+namespace Http2
+{
+
+// 3.5 HTTP/2 Connection Preface:
+// "That is, the connection preface starts with the string
+// PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n)."
+const char Http2clientPreface[clientPrefaceLength] =
+ {0x50, 0x52, 0x49, 0x20, 0x2a, 0x20,
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x32,
+ 0x2e, 0x30, 0x0d, 0x0a, 0x0d, 0x0a,
+ 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a};
+
+
+void qt_error(quint32 errorCode, QNetworkReply::NetworkError &error,
+ QString &errorMessage)
+{
+ if (errorCode > quint32(HTTP_1_1_REQUIRED)) {
+ error = QNetworkReply::ProtocolFailure;
+ errorMessage = QLatin1String("RST_STREAM with unknown error code (%1)");
+ errorMessage = errorMessage.arg(errorCode);
+ return;
+ }
+
+ const Http2Error http2Error = Http2Error(errorCode);
+
+ switch (http2Error) {
+ case HTTP2_NO_ERROR:
+ error = QNetworkReply::NoError;
+ errorMessage.clear();
+ break;
+ case PROTOCOL_ERROR:
+ error = QNetworkReply::ProtocolFailure;
+ errorMessage = QLatin1String("HTTP/2 protocol error");
+ break;
+ case INTERNAL_ERROR:
+ error = QNetworkReply::InternalServerError;
+ errorMessage = QLatin1String("Internal server error");
+ break;
+ case FLOW_CONTROL_ERROR:
+ error = QNetworkReply::ProtocolFailure;
+ errorMessage = QLatin1String("Flow control error");
+ break;
+ case SETTINGS_TIMEOUT:
+ error = QNetworkReply::TimeoutError;
+ errorMessage = QLatin1String("SETTINGS ACK timeout error");
+ break;
+ case STREAM_CLOSED:
+ error = QNetworkReply::ProtocolFailure;
+ errorMessage = QLatin1String("Server received frame(s) on a half-closed stream");
+ break;
+ case FRAME_SIZE_ERROR:
+ error = QNetworkReply::ProtocolFailure;
+ errorMessage = QLatin1String("Server received a frame with an invalid size");
+ break;
+ case REFUSE_STREAM:
+ error = QNetworkReply::ProtocolFailure;
+ errorMessage = QLatin1String("Server refused a stream");
+ break;
+ case CANCEL:
+ error = QNetworkReply::ProtocolFailure;
+ errorMessage = QLatin1String("Stream is no longer needed");
+ break;
+ case COMPRESSION_ERROR:
+ error = QNetworkReply::ProtocolFailure;
+ errorMessage = QLatin1String("Server is unable to maintain the "
+ "header compression context for the connection");
+ break;
+ case CONNECT_ERROR:
+ // TODO: in Qt6 we'll have to add more error codes in QNetworkReply.
+ error = QNetworkReply::UnknownNetworkError;
+ errorMessage = QLatin1String("The connection established in response "
+ "to a CONNECT request was reset or abnormally closed");
+ break;
+ case ENHANCE_YOUR_CALM:
+ error = QNetworkReply::UnknownServerError;
+ errorMessage = QLatin1String("Server dislikes our behavior, excessive load detected.");
+ break;
+ case INADEQUATE_SECURITY:
+ error = QNetworkReply::ContentAccessDenied;
+ errorMessage = QLatin1String("The underlying transport has properties "
+ "that do not meet minimum security "
+ "requirements");
+ break;
+ case HTTP_1_1_REQUIRED:
+ error = QNetworkReply::ProtocolFailure;
+ errorMessage = QLatin1String("Server requires that HTTP/1.1 "
+ "be used instead of HTTP/2.");
+ }
+}
+
+QString qt_error_string(quint32 errorCode)
+{
+ QNetworkReply::NetworkError error = QNetworkReply::NoError;
+ QString message;
+ qt_error(errorCode, error, message);
+ return message;
+}
+
+QNetworkReply::NetworkError qt_error(quint32 errorCode)
+{
+ QNetworkReply::NetworkError error = QNetworkReply::NoError;
+ QString message;
+ qt_error(errorCode, error, message);
+ return error;
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/http2/http2protocol_p.h b/src/network/access/http2/http2protocol_p.h
new file mode 100644
index 0000000000..5c46949e23
--- /dev/null
+++ b/src/network/access/http2/http2protocol_p.h
@@ -0,0 +1,175 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 HTTP2PROTOCOL_P_H
+#define HTTP2PROTOCOL_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/qnetworkreply.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qglobal.h>
+
+// Different HTTP/2 constants/values as defined by RFC 7540.
+
+QT_BEGIN_NAMESPACE
+
+class QString;
+
+namespace Http2
+{
+
+enum class Settings : quint16
+{
+ HEADER_TABLE_SIZE_ID = 0x1,
+ ENABLE_PUSH_ID = 0x2,
+ MAX_CONCURRENT_STREAMS_ID = 0x3,
+ INITIAL_WINDOW_SIZE_ID = 0x4,
+ MAX_FRAME_SIZE_ID = 0x5,
+ MAX_HEADER_LIST_SIZE_ID = 0x6
+};
+
+enum class FrameType : uchar
+{
+ DATA = 0x0,
+ HEADERS = 0x1,
+ PRIORITY = 0x2,
+ RST_STREAM = 0x3,
+ SETTINGS = 0x4,
+ PUSH_PROMISE = 0x5,
+ PING = 0x6,
+ GOAWAY = 0x7,
+ WINDOW_UPDATE = 0x8,
+ CONTINUATION = 0x9,
+ // ATTENTION: enumerators must be sorted.
+ // We use LAST_FRAME_TYPE to check if
+ // frame type is known, if not - this frame
+ // must be ignored, HTTP/2 5.1).
+ LAST_FRAME_TYPE
+};
+
+enum class FrameFlag : uchar
+{
+ EMPTY = 0x0, // Valid for any frame type.
+ ACK = 0x1, // Valid for PING, SETTINGS
+ END_STREAM = 0x1, // Valid for HEADERS, DATA
+ END_HEADERS = 0x4, // Valid for PUSH_PROMISE, HEADERS,
+ PADDED = 0x8, // Valid for PUSH_PROMISE, HEADERS, DATA
+ PRIORITY = 0x20 // Valid for HEADERS,
+};
+
+Q_DECLARE_FLAGS(FrameFlags, FrameFlag)
+Q_DECLARE_OPERATORS_FOR_FLAGS(FrameFlags)
+
+enum Http2PredefinedParameters
+{
+ // Old-style enum, so we
+ // can use as Http2::frameHeaderSize for example.
+ clientPrefaceLength = 24, // HTTP/2, 3.5
+ connectionStreamID = 0, // HTTP/2, 5.1.1
+ frameHeaderSize = 9, // HTTP/2, 4.1
+
+ // It's our max frame size we send in SETTINGS frame,
+ // it's also the default one and we also use it to later
+ // validate incoming frames:
+ maxFrameSize = 16384, // HTTP/2 6.5.2
+
+ defaultSessionWindowSize = 65535, // HTTP/2 6.5.2
+ maxPayloadSize = (1 << 24) - 1, // HTTP/2 6.5.2
+ // Using 1000 (rather arbitrarily), just to
+ // impose *some* upper limit:
+ maxPeerConcurrentStreams = 1000,
+ maxConcurrentStreams = 100 // HTTP/2, 6.5.2
+};
+
+extern const Q_AUTOTEST_EXPORT char Http2clientPreface[clientPrefaceLength];
+
+enum class FrameStatus
+{
+ protocolError,
+ sizeError,
+ incompleteFrame,
+ goodFrame
+};
+
+enum Http2Error
+{
+ // Old-style enum to avoid excessive name
+ // qualification ...
+ // NB:
+ // I use the last enumerator to check
+ // that errorCode (quint32) is valid,
+ // so it needs to be the highest-numbered!
+ // HTTP/2 7:
+ HTTP2_NO_ERROR = 0x0,
+ PROTOCOL_ERROR = 0x1,
+ INTERNAL_ERROR = 0x2,
+ FLOW_CONTROL_ERROR = 0x3,
+ SETTINGS_TIMEOUT = 0x4,
+ STREAM_CLOSED = 0x5,
+ FRAME_SIZE_ERROR = 0x6,
+ REFUSE_STREAM = 0x7,
+ CANCEL = 0x8,
+ COMPRESSION_ERROR = 0x9,
+ CONNECT_ERROR = 0xa,
+ ENHANCE_YOUR_CALM = 0xb,
+ INADEQUATE_SECURITY = 0xc,
+ HTTP_1_1_REQUIRED = 0xd
+};
+
+void qt_error(quint32 errorCode, QNetworkReply::NetworkError &error, QString &errorString);
+QString qt_error_string(quint32 errorCode);
+QNetworkReply::NetworkError qt_error(quint32 errorCode);
+
+}
+
+Q_DECLARE_LOGGING_CATEGORY(QT_HTTP2)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/http2/http2streams.cpp b/src/network/access/http2/http2streams.cpp
new file mode 100644
index 0000000000..660100f5e4
--- /dev/null
+++ b/src/network/access/http2/http2streams.cpp
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 "http2streams_p.h"
+
+#include "private/qhttp2protocolhandler_p.h"
+#include "private/qhttpnetworkreply_p.h"
+
+#include <QtCore/qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Http2
+{
+
+Stream::Stream()
+{
+}
+
+Stream::Stream(const HttpMessagePair &message, quint32 id, qint32 sendSize, qint32 recvSize)
+ : httpPair(message),
+ streamID(id),
+ sendWindow(sendSize),
+ recvWindow(recvSize)
+{
+}
+
+QHttpNetworkReply *Stream::reply() const
+{
+ return httpPair.second;
+}
+
+const QHttpNetworkRequest &Stream::request() const
+{
+ return httpPair.first;
+}
+
+QHttpNetworkRequest &Stream::request()
+{
+ return httpPair.first;
+}
+
+QHttpNetworkRequest::Priority Stream::priority() const
+{
+ return httpPair.first.priority();
+}
+
+uchar Stream::weight() const
+{
+ switch (priority()) {
+ case QHttpNetworkRequest::LowPriority:
+ return 0;
+ case QHttpNetworkRequest::NormalPriority:
+ return 127;
+ case QHttpNetworkRequest::HighPriority:
+ default:
+ return 255;
+ }
+}
+
+QNonContiguousByteDevice *Stream::data() const
+{
+ return httpPair.first.uploadByteDevice();
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/http2/http2streams_p.h b/src/network/access/http2/http2streams_p.h
new file mode 100644
index 0000000000..8a825a5457
--- /dev/null
+++ b/src/network/access/http2/http2streams_p.h
@@ -0,0 +1,102 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 HTTP2STREAMS_P_H
+#define HTTP2STREAMS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qhttpnetworkconnectionchannel_p.h>
+#include <private/qhttpnetworkrequest_p.h>
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class QNonContiguousByteDevice;
+
+namespace Http2
+{
+
+struct Q_AUTOTEST_EXPORT Stream
+{
+ enum StreamState {
+ idle,
+ open,
+ halfClosedLocal,
+ halfClosedRemote,
+ closed
+ };
+
+ Stream();
+ Stream(const HttpMessagePair &message, quint32 streamID, qint32 sendSize,
+ qint32 recvSize);
+
+ QHttpNetworkReply *reply() const;
+ const QHttpNetworkRequest &request() const;
+ QHttpNetworkRequest &request();
+ QHttpNetworkRequest::Priority priority() const;
+ uchar weight() const;
+
+ QNonContiguousByteDevice *data() const;
+
+ HttpMessagePair httpPair;
+ quint32 streamID = 0;
+ // Signed as window sizes can become negative:
+ qint32 sendWindow = 65535;
+ qint32 recvWindow = 65535;
+
+ StreamState state = idle;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
+
diff --git a/src/network/access/http2/huffman.cpp b/src/network/access/http2/huffman.cpp
new file mode 100644
index 0000000000..0c1aa54dd6
--- /dev/null
+++ b/src/network/access/http2/huffman.cpp
@@ -0,0 +1,573 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 "bitstreams_p.h"
+#include "huffman_p.h"
+
+#include <QtCore/qbytearray.h>
+
+#include <algorithm>
+#include <limits>
+
+QT_BEGIN_NAMESPACE
+
+namespace HPack
+{
+
+/*
+ The static Huffman code used here was extracted from:
+ https://http2.github.io/http2-spec/compression.html#huffman.code
+
+ This code was generated from statistics obtained on a large
+ sample of HTTP headers. It is a canonical Huffman code
+ with some tweaking to ensure that no symbol has a unique
+ code length. All codes were left-aligned - for implementation
+ convenience.
+
+ Using binary trees to implement decoding would be prohibitively
+ expensive (both memory and time-wise). Instead we use a table-based
+ approach and any given code itself works as an index into such table(s).
+ We have 256 possible byte values and code lengths in
+ a range [5, 26]. This would require a huge table (and most of entries
+ would be 'wasted', since we only have to encode 256 elements).
+ Instead we use multi-level tables. The first level table
+ is using 9-bit length index; some entries in this table are 'terminal',
+ some reference the next level table(s).
+
+ For example, bytes with values 48 and 49 (ASCII codes for '0' and '1')
+ both have code length 5, Huffman codes are: 00000 and 00001. They
+ both are placed in the 'root' table,
+ the 'root' table has index length == 9:
+ [00000 | 4 remaining bits]
+ ...
+ [00001 | 4 remaining bits]
+
+ All entires with indices between these two will 'point' to value 48
+ with bitLength == 5 so that bit stream (for example) 000001010 will be
+ decoded as: 48 + "put 1010 back into bitstream".
+
+ A good description can be found here:
+ http://commandlinefanatic.com/cgi-bin/showarticle.cgi?article=art007
+ or just google "Efficient Huffman Decoding".
+ Also see comments below about 'filling holes'.
+*/
+
+namespace
+{
+
+const CodeEntry staticHuffmanCodeTable[]
+{
+ { 0, 0xffc00000ul, 13}, // 11111111|11000
+ { 1, 0xffffb000ul, 23}, // 11111111|11111111|1011000
+ { 2, 0xfffffe20ul, 28}, // 11111111|11111111|11111110|0010
+ { 3, 0xfffffe30ul, 28}, // 11111111|11111111|11111110|0011
+ { 4, 0xfffffe40ul, 28}, // 11111111|11111111|11111110|0100
+ { 5, 0xfffffe50ul, 28}, // 11111111|11111111|11111110|0101
+ { 6, 0xfffffe60ul, 28}, // 11111111|11111111|11111110|0110
+ { 7, 0xfffffe70ul, 28}, // 11111111|11111111|11111110|0111
+ { 8, 0xfffffe80ul, 28}, // 11111111|11111111|11111110|1000
+ { 9, 0xffffea00ul, 24}, // 11111111|11111111|11101010
+ { 10, 0xfffffff0ul, 30}, // 11111111|11111111|11111111|111100
+ { 11, 0xfffffe90ul, 28}, // 11111111|11111111|11111110|1001
+ { 12, 0xfffffea0ul, 28}, // 11111111|11111111|11111110|1010
+ { 13, 0xfffffff4ul, 30}, // 11111111|11111111|11111111|111101
+ { 14, 0xfffffeb0ul, 28}, // 11111111|11111111|11111110|1011
+ { 15, 0xfffffec0ul, 28}, // 11111111|11111111|11111110|1100
+ { 16, 0xfffffed0ul, 28}, // 11111111|11111111|11111110|1101
+ { 17, 0xfffffee0ul, 28}, // 11111111|11111111|11111110|1110
+ { 18, 0xfffffef0ul, 28}, // 11111111|11111111|11111110|1111
+ { 19, 0xffffff00ul, 28}, // 11111111|11111111|11111111|0000
+ { 20, 0xffffff10ul, 28}, // 11111111|11111111|11111111|0001
+ { 21, 0xffffff20ul, 28}, // 11111111|11111111|11111111|0010
+ { 22, 0xfffffff8ul, 30}, // 11111111|11111111|11111111|111110
+ { 23, 0xffffff30ul, 28}, // 11111111|11111111|11111111|0011
+ { 24, 0xffffff40ul, 28}, // 11111111|11111111|11111111|0100
+ { 25, 0xffffff50ul, 28}, // 11111111|11111111|11111111|0101
+ { 26, 0xffffff60ul, 28}, // 11111111|11111111|11111111|0110
+ { 27, 0xffffff70ul, 28}, // 11111111|11111111|11111111|0111
+ { 28, 0xffffff80ul, 28}, // 11111111|11111111|11111111|1000
+ { 29, 0xffffff90ul, 28}, // 11111111|11111111|11111111|1001
+ { 30, 0xffffffa0ul, 28}, // 11111111|11111111|11111111|1010
+ { 31, 0xffffffb0ul, 28}, // 11111111|11111111|11111111|1011
+ { 32, 0x50000000ul, 6}, // ' ' 010100
+ { 33, 0xfe000000ul, 10}, // '!' 11111110|00
+ { 34, 0xfe400000ul, 10}, // '"' 11111110|01
+ { 35, 0xffa00000ul, 12}, // '#' 11111111|1010
+ { 36, 0xffc80000ul, 13}, // '$' 11111111|11001
+ { 37, 0x54000000ul, 6}, // '%' 010101
+ { 38, 0xf8000000ul, 8}, // '&' 11111000
+ { 39, 0xff400000ul, 11}, // ''' 11111111|010
+ { 40, 0xfe800000ul, 10}, // '(' 11111110|10
+ { 41, 0xfec00000ul, 10}, // ')' 11111110|11
+ { 42, 0xf9000000ul, 8}, // '*' 11111001
+ { 43, 0xff600000ul, 11}, // '+' 11111111|011
+ { 44, 0xfa000000ul, 8}, // ',' 11111010
+ { 45, 0x58000000ul, 6}, // '-' 010110
+ { 46, 0x5c000000ul, 6}, // '.' 010111
+ { 47, 0x60000000ul, 6}, // '/' 011000
+ { 48, 0x00000000ul, 5}, // '0' 00000
+ { 49, 0x08000000ul, 5}, // '1' 00001
+ { 50, 0x10000000ul, 5}, // '2' 00010
+ { 51, 0x64000000ul, 6}, // '3' 011001
+ { 52, 0x68000000ul, 6}, // '4' 011010
+ { 53, 0x6c000000ul, 6}, // '5' 011011
+ { 54, 0x70000000ul, 6}, // '6' 011100
+ { 55, 0x74000000ul, 6}, // '7' 011101
+ { 56, 0x78000000ul, 6}, // '8' 011110
+ { 57, 0x7c000000ul, 6}, // '9' 011111
+ { 58, 0xb8000000ul, 7}, // ':' 1011100
+ { 59, 0xfb000000ul, 8}, // ';' 11111011
+ { 60, 0xfff80000ul, 15}, // '<' 11111111|1111100
+ { 61, 0x80000000ul, 6}, // '=' 100000
+ { 62, 0xffb00000ul, 12}, // '>' 11111111|1011
+ { 63, 0xff000000ul, 10}, // '?' 11111111|00
+ { 64, 0xffd00000ul, 13}, // '@' 11111111|11010
+ { 65, 0x84000000ul, 6}, // 'A' 100001
+ { 66, 0xba000000ul, 7}, // 'B' 1011101
+ { 67, 0xbc000000ul, 7}, // 'C' 1011110
+ { 68, 0xbe000000ul, 7}, // 'D' 1011111
+ { 69, 0xc0000000ul, 7}, // 'E' 1100000
+ { 70, 0xc2000000ul, 7}, // 'F' 1100001
+ { 71, 0xc4000000ul, 7}, // 'G' 1100010
+ { 72, 0xc6000000ul, 7}, // 'H' 1100011
+ { 73, 0xc8000000ul, 7}, // 'I' 1100100
+ { 74, 0xca000000ul, 7}, // 'J' 1100101
+ { 75, 0xcc000000ul, 7}, // 'K' 1100110
+ { 76, 0xce000000ul, 7}, // 'L' 1100111
+ { 77, 0xd0000000ul, 7}, // 'M' 1101000
+ { 78, 0xd2000000ul, 7}, // 'N' 1101001
+ { 79, 0xd4000000ul, 7}, // 'O' 1101010
+ { 80, 0xd6000000ul, 7}, // 'P' 1101011
+ { 81, 0xd8000000ul, 7}, // 'Q' 1101100
+ { 82, 0xda000000ul, 7}, // 'R' 1101101
+ { 83, 0xdc000000ul, 7}, // 'S' 1101110
+ { 84, 0xde000000ul, 7}, // 'T' 1101111
+ { 85, 0xe0000000ul, 7}, // 'U' 1110000
+ { 86, 0xe2000000ul, 7}, // 'V' 1110001
+ { 87, 0xe4000000ul, 7}, // 'W' 1110010
+ { 88, 0xfc000000ul, 8}, // 'X' 11111100
+ { 89, 0xe6000000ul, 7}, // 'Y' 1110011
+ { 90, 0xfd000000ul, 8}, // 'Z' 11111101
+ { 91, 0xffd80000ul, 13}, // '[' 11111111|11011
+ { 92, 0xfffe0000ul, 19}, // '\' 11111111|11111110|000
+ { 93, 0xffe00000ul, 13}, // ']' 11111111|11100
+ { 94, 0xfff00000ul, 14}, // '^' 11111111|111100
+ { 95, 0x88000000ul, 6}, // '_' 100010
+ { 96, 0xfffa0000ul, 15}, // '`' 11111111|1111101
+ { 97, 0x18000000ul, 5}, // 'a' 00011
+ { 98, 0x8c000000ul, 6}, // 'b' 100011
+ { 99, 0x20000000ul, 5}, // 'c' 00100
+ {100, 0x90000000ul, 6}, // 'd' 100100
+ {101, 0x28000000ul, 5}, // 'e' 00101
+ {102, 0x94000000ul, 6}, // 'f' 100101
+ {103, 0x98000000ul, 6}, // 'g' 100110
+ {104, 0x9c000000ul, 6}, // 'h' 100111
+ {105, 0x30000000ul, 5}, // 'i' 00110
+ {106, 0xe8000000ul, 7}, // 'j' 1110100
+ {107, 0xea000000ul, 7}, // 'k' 1110101
+ {108, 0xa0000000ul, 6}, // 'l' 101000
+ {109, 0xa4000000ul, 6}, // 'm' 101001
+ {110, 0xa8000000ul, 6}, // 'n' 101010
+ {111, 0x38000000ul, 5}, // 'o' 00111
+ {112, 0xac000000ul, 6}, // 'p' 101011
+ {113, 0xec000000ul, 7}, // 'q' 1110110
+ {114, 0xb0000000ul, 6}, // 'r' 101100
+ {115, 0x40000000ul, 5}, // 's' 01000
+ {116, 0x48000000ul, 5}, // 't' 01001
+ {117, 0xb4000000ul, 6}, // 'u' 101101
+ {118, 0xee000000ul, 7}, // 'v' 1110111
+ {119, 0xf0000000ul, 7}, // 'w' 1111000
+ {120, 0xf2000000ul, 7}, // 'x' 1111001
+ {121, 0xf4000000ul, 7}, // 'y' 1111010
+ {122, 0xf6000000ul, 7}, // 'z' 1111011
+ {123, 0xfffc0000ul, 15}, // '{' 11111111|1111110
+ {124, 0xff800000ul, 11}, // '|' 11111111|100
+ {125, 0xfff40000ul, 14}, // '}' 11111111|111101
+ {126, 0xffe80000ul, 13}, // '~' 11111111|11101
+ {127, 0xffffffc0ul, 28}, // 11111111|11111111|11111111|1100
+ {128, 0xfffe6000ul, 20}, // 11111111|11111110|0110
+ {129, 0xffff4800ul, 22}, // 11111111|11111111|010010
+ {130, 0xfffe7000ul, 20}, // 11111111|11111110|0111
+ {131, 0xfffe8000ul, 20}, // 11111111|11111110|1000
+ {132, 0xffff4c00ul, 22}, // 11111111|11111111|010011
+ {133, 0xffff5000ul, 22}, // 11111111|11111111|010100
+ {134, 0xffff5400ul, 22}, // 11111111|11111111|010101
+ {135, 0xffffb200ul, 23}, // 11111111|11111111|1011001
+ {136, 0xffff5800ul, 22}, // 11111111|11111111|010110
+ {137, 0xffffb400ul, 23}, // 11111111|11111111|1011010
+ {138, 0xffffb600ul, 23}, // 11111111|11111111|1011011
+ {139, 0xffffb800ul, 23}, // 11111111|11111111|1011100
+ {140, 0xffffba00ul, 23}, // 11111111|11111111|1011101
+ {141, 0xffffbc00ul, 23}, // 11111111|11111111|1011110
+ {142, 0xffffeb00ul, 24}, // 11111111|11111111|11101011
+ {143, 0xffffbe00ul, 23}, // 11111111|11111111|1011111
+ {144, 0xffffec00ul, 24}, // 11111111|11111111|11101100
+ {145, 0xffffed00ul, 24}, // 11111111|11111111|11101101
+ {146, 0xffff5c00ul, 22}, // 11111111|11111111|010111
+ {147, 0xffffc000ul, 23}, // 11111111|11111111|1100000
+ {148, 0xffffee00ul, 24}, // 11111111|11111111|11101110
+ {149, 0xffffc200ul, 23}, // 11111111|11111111|1100001
+ {150, 0xffffc400ul, 23}, // 11111111|11111111|1100010
+ {151, 0xffffc600ul, 23}, // 11111111|11111111|1100011
+ {152, 0xffffc800ul, 23}, // 11111111|11111111|1100100
+ {153, 0xfffee000ul, 21}, // 11111111|11111110|11100
+ {154, 0xffff6000ul, 22}, // 11111111|11111111|011000
+ {155, 0xffffca00ul, 23}, // 11111111|11111111|1100101
+ {156, 0xffff6400ul, 22}, // 11111111|11111111|011001
+ {157, 0xffffcc00ul, 23}, // 11111111|11111111|1100110
+ {158, 0xffffce00ul, 23}, // 11111111|11111111|1100111
+ {159, 0xffffef00ul, 24}, // 11111111|11111111|11101111
+ {160, 0xffff6800ul, 22}, // 11111111|11111111|011010
+ {161, 0xfffee800ul, 21}, // 11111111|11111110|11101
+ {162, 0xfffe9000ul, 20}, // 11111111|11111110|1001
+ {163, 0xffff6c00ul, 22}, // 11111111|11111111|011011
+ {164, 0xffff7000ul, 22}, // 11111111|11111111|011100
+ {165, 0xffffd000ul, 23}, // 11111111|11111111|1101000
+ {166, 0xffffd200ul, 23}, // 11111111|11111111|1101001
+ {167, 0xfffef000ul, 21}, // 11111111|11111110|11110
+ {168, 0xffffd400ul, 23}, // 11111111|11111111|1101010
+ {169, 0xffff7400ul, 22}, // 11111111|11111111|011101
+ {170, 0xffff7800ul, 22}, // 11111111|11111111|011110
+ {171, 0xfffff000ul, 24}, // 11111111|11111111|11110000
+ {172, 0xfffef800ul, 21}, // 11111111|11111110|11111
+ {173, 0xffff7c00ul, 22}, // 11111111|11111111|011111
+ {174, 0xffffd600ul, 23}, // 11111111|11111111|1101011
+ {175, 0xffffd800ul, 23}, // 11111111|11111111|1101100
+ {176, 0xffff0000ul, 21}, // 11111111|11111111|00000
+ {177, 0xffff0800ul, 21}, // 11111111|11111111|00001
+ {178, 0xffff8000ul, 22}, // 11111111|11111111|100000
+ {179, 0xffff1000ul, 21}, // 11111111|11111111|00010
+ {180, 0xffffda00ul, 23}, // 11111111|11111111|1101101
+ {181, 0xffff8400ul, 22}, // 11111111|11111111|100001
+ {182, 0xffffdc00ul, 23}, // 11111111|11111111|1101110
+ {183, 0xffffde00ul, 23}, // 11111111|11111111|1101111
+ {184, 0xfffea000ul, 20}, // 11111111|11111110|1010
+ {185, 0xffff8800ul, 22}, // 11111111|11111111|100010
+ {186, 0xffff8c00ul, 22}, // 11111111|11111111|100011
+ {187, 0xffff9000ul, 22}, // 11111111|11111111|100100
+ {188, 0xffffe000ul, 23}, // 11111111|11111111|1110000
+ {189, 0xffff9400ul, 22}, // 11111111|11111111|100101
+ {190, 0xffff9800ul, 22}, // 11111111|11111111|100110
+ {191, 0xffffe200ul, 23}, // 11111111|11111111|1110001
+ {192, 0xfffff800ul, 26}, // 11111111|11111111|11111000|00
+ {193, 0xfffff840ul, 26}, // 11111111|11111111|11111000|01
+ {194, 0xfffeb000ul, 20}, // 11111111|11111110|1011
+ {195, 0xfffe2000ul, 19}, // 11111111|11111110|001
+ {196, 0xffff9c00ul, 22}, // 11111111|11111111|100111
+ {197, 0xffffe400ul, 23}, // 11111111|11111111|1110010
+ {198, 0xffffa000ul, 22}, // 11111111|11111111|101000
+ {199, 0xfffff600ul, 25}, // 11111111|11111111|11110110|0
+ {200, 0xfffff880ul, 26}, // 11111111|11111111|11111000|10
+ {201, 0xfffff8c0ul, 26}, // 11111111|11111111|11111000|11
+ {202, 0xfffff900ul, 26}, // 11111111|11111111|11111001|00
+ {203, 0xfffffbc0ul, 27}, // 11111111|11111111|11111011|110
+ {204, 0xfffffbe0ul, 27}, // 11111111|11111111|11111011|111
+ {205, 0xfffff940ul, 26}, // 11111111|11111111|11111001|01
+ {206, 0xfffff100ul, 24}, // 11111111|11111111|11110001
+ {207, 0xfffff680ul, 25}, // 11111111|11111111|11110110|1
+ {208, 0xfffe4000ul, 19}, // 11111111|11111110|010
+ {209, 0xffff1800ul, 21}, // 11111111|11111111|00011
+ {210, 0xfffff980ul, 26}, // 11111111|11111111|11111001|10
+ {211, 0xfffffc00ul, 27}, // 11111111|11111111|11111100|000
+ {212, 0xfffffc20ul, 27}, // 11111111|11111111|11111100|001
+ {213, 0xfffff9c0ul, 26}, // 11111111|11111111|11111001|11
+ {214, 0xfffffc40ul, 27}, // 11111111|11111111|11111100|010
+ {215, 0xfffff200ul, 24}, // 11111111|11111111|11110010
+ {216, 0xffff2000ul, 21}, // 11111111|11111111|00100
+ {217, 0xffff2800ul, 21}, // 11111111|11111111|00101
+ {218, 0xfffffa00ul, 26}, // 11111111|11111111|11111010|00
+ {219, 0xfffffa40ul, 26}, // 11111111|11111111|11111010|01
+ {220, 0xffffffd0ul, 28}, // 11111111|11111111|11111111|1101
+ {221, 0xfffffc60ul, 27}, // 11111111|11111111|11111100|011
+ {222, 0xfffffc80ul, 27}, // 11111111|11111111|11111100|100
+ {223, 0xfffffca0ul, 27}, // 11111111|11111111|11111100|101
+ {224, 0xfffec000ul, 20}, // 11111111|11111110|1100
+ {225, 0xfffff300ul, 24}, // 11111111|11111111|11110011
+ {226, 0xfffed000ul, 20}, // 11111111|11111110|1101
+ {227, 0xffff3000ul, 21}, // 11111111|11111111|00110
+ {228, 0xffffa400ul, 22}, // 11111111|11111111|101001
+ {229, 0xffff3800ul, 21}, // 11111111|11111111|00111
+ {230, 0xffff4000ul, 21}, // 11111111|11111111|01000
+ {231, 0xffffe600ul, 23}, // 11111111|11111111|1110011
+ {232, 0xffffa800ul, 22}, // 11111111|11111111|101010
+ {233, 0xffffac00ul, 22}, // 11111111|11111111|101011
+ {234, 0xfffff700ul, 25}, // 11111111|11111111|11110111|0
+ {235, 0xfffff780ul, 25}, // 11111111|11111111|11110111|1
+ {236, 0xfffff400ul, 24}, // 11111111|11111111|11110100
+ {237, 0xfffff500ul, 24}, // 11111111|11111111|11110101
+ {238, 0xfffffa80ul, 26}, // 11111111|11111111|11111010|10
+ {239, 0xffffe800ul, 23}, // 11111111|11111111|1110100
+ {240, 0xfffffac0ul, 26}, // 11111111|11111111|11111010|11
+ {241, 0xfffffcc0ul, 27}, // 11111111|11111111|11111100|110
+ {242, 0xfffffb00ul, 26}, // 11111111|11111111|11111011|00
+ {243, 0xfffffb40ul, 26}, // 11111111|11111111|11111011|01
+ {244, 0xfffffce0ul, 27}, // 11111111|11111111|11111100|111
+ {245, 0xfffffd00ul, 27}, // 11111111|11111111|11111101|000
+ {246, 0xfffffd20ul, 27}, // 11111111|11111111|11111101|001
+ {247, 0xfffffd40ul, 27}, // 11111111|11111111|11111101|010
+ {248, 0xfffffd60ul, 27}, // 11111111|11111111|11111101|011
+ {249, 0xffffffe0ul, 28}, // 11111111|11111111|11111111|1110
+ {250, 0xfffffd80ul, 27}, // 11111111|11111111|11111101|100
+ {251, 0xfffffda0ul, 27}, // 11111111|11111111|11111101|101
+ {252, 0xfffffdc0ul, 27}, // 11111111|11111111|11111101|110
+ {253, 0xfffffde0ul, 27}, // 11111111|11111111|11111101|111
+ {254, 0xfffffe00ul, 27}, // 11111111|11111111|11111110|000
+ {255, 0xfffffb80ul, 26}, // 11111111|11111111|11111011|10
+ {256, 0xfffffffcul, 30} // EOS 11111111|11111111|11111111|111111
+};
+
+void write_huffman_code(BitOStream &outputStream, const CodeEntry &code)
+{
+ // Append octet by octet.
+ auto bitLength = code.bitLength;
+ const auto hc = code.huffmanCode >> (32 - bitLength);
+
+ if (bitLength > 24) {
+ outputStream.writeBits(uchar(hc >> 24), bitLength - 24);
+ bitLength = 24;
+ }
+
+ if (bitLength > 16) {
+ outputStream.writeBits(uchar(hc >> 16), bitLength - 16);
+ bitLength = 16;
+ }
+
+ if (bitLength > 8) {
+ outputStream.writeBits(uchar(hc >> 8), bitLength - 8);
+ bitLength = 8;
+ }
+
+ outputStream.writeBits(uchar(hc), bitLength);
+}
+
+}
+
+// That's from HPACK's specs - we deal with octets.
+static_assert(std::numeric_limits<uchar>::digits == 8, "octets expected");
+
+quint64 huffman_encoded_bit_length(const QByteArray &inputData)
+{
+ quint64 bitLength = 0;
+ for (int i = 0, e = inputData.size(); i < e; ++i)
+ bitLength += staticHuffmanCodeTable[int(inputData[i])].bitLength;
+
+ return bitLength;
+}
+
+void huffman_encode_string(const QByteArray &inputData, BitOStream &outputStream)
+{
+ for (int i = 0, e = inputData.size(); i < e; ++i)
+ write_huffman_code(outputStream, staticHuffmanCodeTable[int(inputData[i])]);
+
+ // Pad bits ...
+ if (outputStream.bitLength() % 8)
+ outputStream.writeBits(0xff, 8 - outputStream.bitLength() % 8);
+}
+
+bool padding_is_valid(quint32 chunk, quint32 nBits)
+{
+ Q_ASSERT(nBits);
+
+ // HPACK, 5.2: "A padding strictly longer than 7 bits MUST be
+ // treated as a decoding error."
+ if (nBits > 7)
+ return false;
+ // HPACK, 5.2:
+ // "A padding not corresponding to the most significant bits
+ // of the code for the EOS symbol MUST be treated as a decoding error."
+ return (chunk >> (32 - nBits)) == quint32((1 << nBits) - 1);
+}
+
+HuffmanDecoder::HuffmanDecoder()
+ : minCodeLength()
+{
+ const auto nCodes = sizeof staticHuffmanCodeTable / sizeof staticHuffmanCodeTable[0];
+
+ std::vector<CodeEntry> symbols(staticHuffmanCodeTable, staticHuffmanCodeTable + nCodes);
+ // Now we sort: by bit length first (in the descending order) and by the symbol
+ // value (descending). Descending order: to make sure we do not create prefix tables with
+ // short 'indexLength' first and having longer codes that do not fit into such tables later.
+ std::sort(symbols.begin(), symbols.end(), [](const CodeEntry &code1, const CodeEntry &code2) {
+ if (code1.bitLength == code2.bitLength)
+ return code1.byteValue > code2.byteValue;
+ return code1.bitLength > code2.bitLength;
+ });
+
+ minCodeLength = symbols.back().bitLength; // The shortest one, currently it's 5.
+
+ // TODO: add a verification - Huffman codes
+ // within a given bit length range also
+ // should be in descending order.
+
+ // Initialize 'prefixTables' and 'tableData'.
+ addTable(0, quint32(BitConstants::rootPrefix)); // 'root' table.
+
+ for (const auto &s : symbols) {
+ quint32 tableIndex = 0;
+ while (true) {
+ Q_ASSERT(tableIndex < prefixTables.size());
+ // Note, by value - since prefixTables will be updated in between.
+ const auto table = prefixTables[tableIndex];
+ // We skip prefixed bits (if any) and use indexed bits only:
+ const auto entryIndex = s.huffmanCode << table.prefixLength >> (32 - table.indexLength);
+ // Again, by value.
+ PrefixTableEntry entry = tableEntry(table, entryIndex);
+ // How many bits were coded by previous tables and this table:
+ const auto codedLength = table.prefixLength + table.indexLength;
+ if (codedLength < s.bitLength) {
+ // We have to add a new prefix table ... (if it's not done yet).
+ if (!entry.bitLength) {
+ entry.nextTable = addTable(codedLength, std::min<quint32>(quint32(BitConstants::childPrefix),
+ s.bitLength - codedLength));
+ entry.bitLength = s.bitLength;
+ entry.byteValue = s.byteValue;
+ setTableEntry(table, entryIndex, entry);
+ }
+ tableIndex = entry.nextTable;
+ } else {
+ // We found the slot for our code (terminal entry):
+ entry.byteValue = s.byteValue;
+ entry.bitLength = s.bitLength;
+ // Refer to our own table as 'nextTable':
+ entry.nextTable = tableIndex;
+ setTableEntry(table, entryIndex, entry);
+ break;
+ }
+ }
+ }
+
+ // Now, we have a table(s) and have to fill 'holes' to
+ // 'fix' terminal entries.
+ for (const auto &table : prefixTables) {
+ const quint32 codedLength = table.prefixLength + table.indexLength;
+ for (quint32 j = 0; j < table.size();) {
+ const PrefixTableEntry &entry = tableEntry(table, j);
+ if (entry.bitLength && entry.bitLength < codedLength) {
+ const quint32 range = 1 << (codedLength - entry.bitLength);
+ for (quint32 k = 1; k < range; ++k)
+ setTableEntry(table, j + k, entry);
+ j += range;
+ } else {
+ ++j;
+ }
+ }
+ }
+}
+
+bool HuffmanDecoder::decodeStream(BitIStream &inputStream, QByteArray &outputBuffer)
+{
+ while (true) {
+ quint32 chunk = 0;
+ const quint32 readBits = inputStream.peekBits(inputStream.streamOffset(), 32, &chunk);
+ if (!readBits)
+ return !inputStream.hasMoreBits();
+
+ if (readBits < minCodeLength) {
+ inputStream.skipBits(readBits);
+ return padding_is_valid(chunk, readBits);
+ }
+
+ quint32 tableIndex = 0;
+ const PrefixTable *table = &prefixTables[tableIndex];
+ quint32 entryIndex = chunk >> (32 - table->indexLength);
+ PrefixTableEntry entry = tableEntry(*table, entryIndex);
+
+ while (true) {
+ if (entry.nextTable == tableIndex)
+ break;
+
+ tableIndex = entry.nextTable;
+ table = &prefixTables[tableIndex];
+ entryIndex = chunk << table->prefixLength >> (32 - table->indexLength);
+ entry = tableEntry(*table, entryIndex);
+ }
+
+ if (entry.bitLength > readBits) {
+ inputStream.skipBits(readBits);
+ return padding_is_valid(chunk, readBits);
+ }
+
+ if (!entry.bitLength || entry.byteValue == 256) {
+ //EOS (256) == compression error (HPACK).
+ inputStream.skipBits(readBits);
+ return false;
+ }
+
+ outputBuffer.append(entry.byteValue);
+ inputStream.skipBits(entry.bitLength);
+ }
+
+ return false;
+}
+
+quint32 HuffmanDecoder::addTable(quint32 prefix, quint32 index)
+{
+ PrefixTable newTable{prefix, index};
+ newTable.offset = quint32(tableData.size());
+ prefixTables.push_back(newTable);
+ // Add entries for this table:
+ tableData.resize(tableData.size() + newTable.size());
+
+ return quint32(prefixTables.size() - 1);
+}
+
+PrefixTableEntry HuffmanDecoder::tableEntry(const PrefixTable &table, quint32 index)
+{
+ Q_ASSERT(index < table.size());
+ return tableData[table.offset + index];
+}
+
+void HuffmanDecoder::setTableEntry(const PrefixTable &table, quint32 index,
+ const PrefixTableEntry &entry)
+{
+ Q_ASSERT(index < table.size());
+ tableData[table.offset + index] = entry;
+}
+
+bool huffman_decode_string(BitIStream &inputStream, QByteArray *outputBuffer)
+{
+ Q_ASSERT(outputBuffer);
+
+ static HuffmanDecoder decoder;
+ return decoder.decodeStream(inputStream, *outputBuffer);
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/http2/huffman_p.h b/src/network/access/http2/huffman_p.h
new file mode 100644
index 0000000000..7195661664
--- /dev/null
+++ b/src/network/access/http2/huffman_p.h
@@ -0,0 +1,182 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 HUFFMAN_P_H
+#define HUFFMAN_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class QByteArray;
+
+namespace HPack
+{
+
+struct CodeEntry
+{
+ CodeEntry() : byteValue(),
+ huffmanCode(),
+ bitLength()
+ {
+ }
+
+ CodeEntry(quint32 val, quint32 code, quint32 len)
+ : byteValue(val),
+ huffmanCode(code),
+ bitLength(len)
+ {
+ }
+
+ quint32 byteValue;
+ quint32 huffmanCode;
+ quint32 bitLength;
+};
+
+class BitOStream;
+
+quint64 huffman_encoded_bit_length(const QByteArray &inputData);
+void huffman_encode_string(const QByteArray &inputData, BitOStream &outputStream);
+
+// PrefixTable:
+// Huffman codes with a small bit length
+// fit into a table (these are 'terminal' symbols),
+// codes with longer codes require additional
+// tables, so several symbols will have the same index
+// in a table - pointing into the next table.
+// Every table has an 'indexLength' - that's
+// how many bits can fit in table's indices +
+// 'prefixLength' - how many bits were addressed
+// by its 'parent' table(s).
+// All PrefixTables are kept in 'prefixTables' array.
+// PrefixTable itself does not have any entries,
+// it just holds table's prefix/index + 'offset' -
+// there table's data starts in an array of all
+// possible entries ('tableData').
+
+struct PrefixTable
+{
+ PrefixTable()
+ : prefixLength(),
+ indexLength(),
+ offset()
+ {
+ }
+
+ PrefixTable(quint32 prefix, quint32 index)
+ : prefixLength(prefix),
+ indexLength(index),
+ offset()
+ {
+ }
+
+ quint32 size()const
+ {
+ // Number of entries table contains:
+ return 1 << indexLength;
+ }
+
+ quint32 prefixLength;
+ quint32 indexLength;
+ quint32 offset;
+};
+
+// Table entry is either a terminal entry (thus probably the code found)
+// or points into another table ('nextTable' - index into
+// 'prefixTables' array). If it's a terminal, 'nextTable' index
+// refers to the same table.
+
+struct PrefixTableEntry
+{
+ PrefixTableEntry()
+ : bitLength(),
+ nextTable(),
+ byteValue()
+ {
+ }
+
+ quint32 bitLength;
+ quint32 nextTable;
+ quint32 byteValue;
+};
+
+class BitIStream;
+
+class HuffmanDecoder
+{
+public:
+ enum class BitConstants
+ {
+ rootPrefix = 9,
+ childPrefix = 6
+ };
+
+ HuffmanDecoder();
+
+ bool decodeStream(BitIStream &inputStream, QByteArray &outputBuffer);
+
+private:
+ quint32 addTable(quint32 prefixLength, quint32 indexLength);
+ PrefixTableEntry tableEntry(const PrefixTable &table, quint32 index);
+ void setTableEntry(const PrefixTable &table, quint32 index, const PrefixTableEntry &entry);
+
+ std::vector<PrefixTable> prefixTables;
+ std::vector<PrefixTableEntry> tableData;
+ quint32 minCodeLength;
+};
+
+bool huffman_decode_string(BitIStream &inputStream, QByteArray *outputBuffer);
+
+} // namespace HPack
+
+QT_END_NAMESPACE
+
+#endif
+
diff --git a/src/network/access/qabstractnetworkcache.h b/src/network/access/qabstractnetworkcache.h
index 6b80147757..33b0bc4ce3 100644
--- a/src/network/access/qabstractnetworkcache.h
+++ b/src/network/access/qabstractnetworkcache.h
@@ -40,6 +40,7 @@
#ifndef QABSTRACTNETWORKCACHE_H
#define QABSTRACTNETWORKCACHE_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qobject.h>
#include <QtCore/qshareddata.h>
#include <QtCore/qpair.h>
diff --git a/src/network/access/qabstractnetworkcache_p.h b/src/network/access/qabstractnetworkcache_p.h
index 7be89f22b6..fee723e315 100644
--- a/src/network/access/qabstractnetworkcache_p.h
+++ b/src/network/access/qabstractnetworkcache_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "private/qobject_p.h"
QT_BEGIN_NAMESPACE
diff --git a/src/network/access/qabstractprotocolhandler_p.h b/src/network/access/qabstractprotocolhandler_p.h
index a05df3e429..30814d6737 100644
--- a/src/network/access/qabstractprotocolhandler_p.h
+++ b/src/network/access/qabstractprotocolhandler_p.h
@@ -51,9 +51,9 @@
// We mean it.
//
-#ifndef QT_NO_HTTP
+#include <QtNetwork/private/qtnetworkglobal_p.h>
-#include <QtCore/qglobal.h>
+#ifndef QT_NO_HTTP
QT_BEGIN_NAMESPACE
diff --git a/src/network/access/qftp.cpp b/src/network/access/qftp.cpp
index 7b6f830333..52b9dd9169 100644
--- a/src/network/access/qftp.cpp
+++ b/src/network/access/qftp.cpp
@@ -1121,7 +1121,7 @@ bool QFtpPI::processReply()
case Success:
// success handling
state = Idle;
- // no break!
+ Q_FALLTHROUGH();
case Idle:
if (dtp.hasError()) {
emit error(QFtp::UnknownError, dtp.errorMessage());
diff --git a/src/network/access/qftp_p.h b/src/network/access/qftp_p.h
index 37f8f2f8d2..6cf5116798 100644
--- a/src/network/access/qftp_p.h
+++ b/src/network/access/qftp_p.h
@@ -48,9 +48,10 @@
// We mean it.
//
-#ifndef QFTP_H
-#define QFTP_H
+#ifndef QFTP_P_H
+#define QFTP_P_H
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <QtCore/qstring.h>
#include <private/qurlinfo_p.h>
#include <QtCore/qobject.h>
@@ -172,4 +173,4 @@ private:
QT_END_NAMESPACE
-#endif // QFTP_H
+#endif // QFTP_P_H
diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp
new file mode 100644
index 0000000000..68a00c6837
--- /dev/null
+++ b/src/network/access/qhttp2protocolhandler.cpp
@@ -0,0 +1,1218 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 "qhttpnetworkconnection_p.h"
+#include "qhttp2protocolhandler_p.h"
+
+#if !defined(QT_NO_HTTP)
+
+#include "http2/bitstreams_p.h"
+
+#include <private/qnoncontiguousbytedevice_p.h>
+
+#include <QtNetwork/qabstractsocket.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qendian.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qurl.h>
+
+#include <algorithm>
+#include <vector>
+
+QT_BEGIN_NAMESPACE
+
+namespace
+{
+
+HPack::HttpHeader build_headers(const QHttpNetworkRequest &request, quint32 maxHeaderListSize)
+{
+ using namespace HPack;
+
+ HttpHeader header;
+ header.reserve(300);
+
+ // 1. Before anything - mandatory fields, if they do not fit into maxHeaderList -
+ // then stop immediately with error.
+ const auto auth = request.url().authority(QUrl::FullyEncoded | QUrl::RemoveUserInfo).toLatin1();
+ header.push_back(HeaderField(":authority", auth));
+ header.push_back(HeaderField(":method", request.methodName()));
+ header.push_back(HeaderField(":path", request.uri(false)));
+ header.push_back(HeaderField(":scheme", request.url().scheme().toLatin1()));
+
+ HeaderSize size = header_size(header);
+ if (!size.first) // Ooops!
+ return HttpHeader();
+
+ if (size.second > maxHeaderListSize)
+ return HttpHeader(); // Bad, we cannot send this request ...
+
+ for (const auto &field : request.header()) {
+ const HeaderSize delta = entry_size(field.first, field.second);
+ if (!delta.first) // Overflow???
+ break;
+ if (std::numeric_limits<quint32>::max() - delta.second < size.second)
+ break;
+ size.second += delta.second;
+ if (size.second > maxHeaderListSize)
+ break;
+
+ QByteArray key(field.first.toLower());
+ if (key == "connection" || key == "host" || key == "keep-alive"
+ || key == "proxy-connection" || key == "transfer-encoding")
+ continue; // Those headers are not valid (section 3.2.1) - from QSpdyProtocolHandler
+ // TODO: verify with specs, which fields are valid to send ....
+ // toLower - 8.1.2 .... "header field names MUST be converted to lowercase prior
+ // to their encoding in HTTP/2.
+ // A request or response containing uppercase header field names
+ // MUST be treated as malformed (Section 8.1.2.6)".
+ header.push_back(HeaderField(key, field.second));
+ }
+
+ return header;
+}
+
+bool sum_will_overflow(qint32 windowSize, qint32 delta)
+{
+ if (windowSize > 0)
+ return std::numeric_limits<qint32>::max() - windowSize < delta;
+ return std::numeric_limits<qint32>::min() - windowSize > delta;
+}
+
+}// Unnamed namespace
+
+// Since we anyway end up having this in every function definition:
+using namespace Http2;
+
+const std::deque<quint32>::size_type QHttp2ProtocolHandler::maxRecycledStreams = 10000;
+const qint32 QHttp2ProtocolHandler::sessionMaxRecvWindowSize;
+const qint32 QHttp2ProtocolHandler::streamInitialRecvWindowSize;
+const quint32 QHttp2ProtocolHandler::maxAcceptableTableSize;
+
+QHttp2ProtocolHandler::QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *channel)
+ : QAbstractProtocolHandler(channel),
+ decoder(HPack::FieldLookupTable::DefaultSize),
+ encoder(HPack::FieldLookupTable::DefaultSize, true)
+{
+ continuedFrames.reserve(20);
+}
+
+void QHttp2ProtocolHandler::_q_uploadDataReadyRead()
+{
+ auto data = qobject_cast<QNonContiguousByteDevice *>(sender());
+ Q_ASSERT(data);
+ const qint32 streamID = data->property("HTTP2StreamID").toInt();
+ Q_ASSERT(activeStreams.contains(streamID));
+ auto &stream = activeStreams[streamID];
+
+ if (!sendDATA(stream)) {
+ finishStreamWithError(stream, QNetworkReply::UnknownNetworkError,
+ QLatin1String("failed to send DATA"));
+ sendRST_STREAM(streamID, INTERNAL_ERROR);
+ markAsReset(streamID);
+ deleteActiveStream(streamID);
+ }
+}
+
+void QHttp2ProtocolHandler::_q_replyDestroyed(QObject *reply)
+{
+ const quint32 streamID = reply->property("HTTP2StreamID").toInt();
+ if (activeStreams.contains(streamID)) {
+ sendRST_STREAM(streamID, CANCEL);
+ markAsReset(streamID);
+ deleteActiveStream(streamID);
+ }
+}
+
+void QHttp2ProtocolHandler::_q_readyRead()
+{
+ _q_receiveReply();
+}
+
+void QHttp2ProtocolHandler::_q_receiveReply()
+{
+ Q_ASSERT(m_socket);
+ Q_ASSERT(m_channel);
+
+ do {
+ const auto result = frameReader.read(*m_socket);
+ switch (result) {
+ case FrameStatus::incompleteFrame:
+ return;
+ case FrameStatus::protocolError:
+ return connectionError(PROTOCOL_ERROR, "invalid frame");
+ case FrameStatus::sizeError:
+ return connectionError(FRAME_SIZE_ERROR, "invalid frame size");
+ default:
+ break;
+ }
+
+ Q_ASSERT(result == FrameStatus::goodFrame);
+
+ inboundFrame = std::move(frameReader.inboundFrame());
+
+ const auto frameType = inboundFrame.type();
+ if (continuationExpected && frameType != FrameType::CONTINUATION)
+ return connectionError(PROTOCOL_ERROR, "CONTINUATION expected");
+
+ switch (frameType) {
+ case FrameType::DATA:
+ handleDATA();
+ break;
+ case FrameType::HEADERS:
+ handleHEADERS();
+ break;
+ case FrameType::PRIORITY:
+ handlePRIORITY();
+ break;
+ case FrameType::RST_STREAM:
+ handleRST_STREAM();
+ break;
+ case FrameType::SETTINGS:
+ handleSETTINGS();
+ break;
+ case FrameType::PUSH_PROMISE:
+ handlePUSH_PROMISE();
+ break;
+ case FrameType::PING:
+ handlePING();
+ break;
+ case FrameType::GOAWAY:
+ handleGOAWAY();
+ break;
+ case FrameType::WINDOW_UPDATE:
+ handleWINDOW_UPDATE();
+ break;
+ case FrameType::CONTINUATION:
+ handleCONTINUATION();
+ break;
+ case FrameType::LAST_FRAME_TYPE:
+ // 5.1 - ignore unknown frames.
+ break;
+ }
+ } while (!goingAway || activeStreams.size());
+}
+
+bool QHttp2ProtocolHandler::sendRequest()
+{
+ if (goingAway)
+ return false;
+
+ if (!prefaceSent && !sendClientPreface())
+ return false;
+
+ auto &requests = m_channel->spdyRequestsToSend;
+ if (!requests.size())
+ return true;
+
+ const auto streamsToUse = std::min<quint32>(maxConcurrentStreams - activeStreams.size(),
+ requests.size());
+ auto it = requests.begin();
+ m_channel->state = QHttpNetworkConnectionChannel::WritingState;
+ for (quint32 i = 0; i < streamsToUse; ++i) {
+ const qint32 newStreamID = createNewStream(*it);
+ if (!newStreamID) {
+ // TODO: actually we have to open a new connection.
+ qCCritical(QT_HTTP2, "sendRequest: out of stream IDs");
+ break;
+ }
+
+ it = requests.erase(it);
+
+ Stream &newStream = activeStreams[newStreamID];
+ if (!sendHEADERS(newStream)) {
+ finishStreamWithError(newStream, QNetworkReply::UnknownNetworkError,
+ QLatin1String("failed to send HEADERS frame(s)"));
+ deleteActiveStream(newStreamID);
+ continue;
+ }
+
+ if (newStream.data() && !sendDATA(newStream)) {
+ finishStreamWithError(newStream, QNetworkReply::UnknownNetworkError,
+ QLatin1String("failed to send DATA frame(s)"));
+ sendRST_STREAM(newStreamID, INTERNAL_ERROR);
+ markAsReset(newStreamID);
+ deleteActiveStream(newStreamID);
+ }
+ }
+
+ m_channel->state = QHttpNetworkConnectionChannel::IdleState;
+
+ return true;
+}
+
+
+bool QHttp2ProtocolHandler::sendClientPreface()
+{
+ // 3.5 HTTP/2 Connection Preface
+ Q_ASSERT(m_socket);
+
+ if (prefaceSent)
+ return true;
+
+ const qint64 written = m_socket->write(Http2clientPreface,
+ Http2::clientPrefaceLength);
+ if (written != Http2::clientPrefaceLength)
+ return false;
+
+ // 6.5 SETTINGS
+ frameWriter.start(FrameType::SETTINGS, FrameFlag::EMPTY, Http2::connectionStreamID);
+ // MAX frame size (16 kb), disable PUSH
+ frameWriter.append(Settings::MAX_FRAME_SIZE_ID);
+ frameWriter.append(quint32(Http2::maxFrameSize));
+ frameWriter.append(Settings::ENABLE_PUSH_ID);
+ frameWriter.append(quint32(0));
+
+ if (!frameWriter.write(*m_socket))
+ return false;
+
+ sessionRecvWindowSize = sessionMaxRecvWindowSize;
+ if (defaultSessionWindowSize < sessionMaxRecvWindowSize) {
+ const auto delta = sessionMaxRecvWindowSize - defaultSessionWindowSize;
+ if (!sendWINDOW_UPDATE(connectionStreamID, delta))
+ return false;
+ }
+
+ prefaceSent = true;
+ waitingForSettingsACK = true;
+
+ return true;
+}
+
+bool QHttp2ProtocolHandler::sendSETTINGS_ACK()
+{
+ Q_ASSERT(m_socket);
+
+ if (!prefaceSent && !sendClientPreface())
+ return false;
+
+ frameWriter.start(FrameType::SETTINGS, FrameFlag::ACK, Http2::connectionStreamID);
+
+ return frameWriter.write(*m_socket);
+}
+
+bool QHttp2ProtocolHandler::sendHEADERS(Stream &stream)
+{
+ using namespace HPack;
+
+ frameWriter.start(FrameType::HEADERS, FrameFlag::PRIORITY | FrameFlag::END_HEADERS,
+ stream.streamID);
+
+ if (!stream.data()) {
+ frameWriter.addFlag(FrameFlag::END_STREAM);
+ stream.state = Stream::halfClosedLocal;
+ } else {
+ stream.state = Stream::open;
+ }
+
+ frameWriter.append(quint32()); // No stream dependency in Qt.
+ frameWriter.append(stream.weight());
+
+ const auto headers = build_headers(stream.request(), maxHeaderListSize);
+ if (!headers.size()) // nothing fits into maxHeaderListSize
+ return false;
+
+ // Compress in-place:
+ BitOStream outputStream(frameWriter.outboundFrame().buffer);
+ if (!encoder.encodeRequest(outputStream, headers))
+ return false;
+
+ return frameWriter.writeHEADERS(*m_socket, maxFrameSize);
+}
+
+bool QHttp2ProtocolHandler::sendDATA(Stream &stream)
+{
+ Q_ASSERT(maxFrameSize > frameHeaderSize);
+ Q_ASSERT(m_socket);
+ Q_ASSERT(stream.data());
+
+ const auto &request = stream.request();
+ auto reply = stream.reply();
+ Q_ASSERT(reply);
+ const auto replyPrivate = reply->d_func();
+ Q_ASSERT(replyPrivate);
+
+ auto slot = std::min<qint32>(sessionSendWindowSize, stream.sendWindow);
+ while (!stream.data()->atEnd() && slot) {
+ qint64 chunkSize = 0;
+ const uchar *src =
+ reinterpret_cast<const uchar *>(stream.data()->readPointer(slot, chunkSize));
+
+ if (chunkSize == -1)
+ return false;
+
+ if (!src || !chunkSize) {
+ // Stream is not suspended by the flow control,
+ // we do not have data ready yet.
+ return true;
+ }
+
+ frameWriter.start(FrameType::DATA, FrameFlag::EMPTY, stream.streamID);
+ const qint32 bytesWritten = std::min<qint32>(slot, chunkSize);
+
+ if (!frameWriter.writeDATA(*m_socket, maxFrameSize, src, bytesWritten))
+ return false;
+
+ stream.data()->advanceReadPointer(bytesWritten);
+ stream.sendWindow -= bytesWritten;
+ sessionSendWindowSize -= bytesWritten;
+ replyPrivate->totallyUploadedData += bytesWritten;
+ emit reply->dataSendProgress(replyPrivate->totallyUploadedData,
+ request.contentLength());
+ slot = std::min(sessionSendWindowSize, stream.sendWindow);
+ }
+
+ if (replyPrivate->totallyUploadedData == request.contentLength()) {
+ frameWriter.start(FrameType::DATA, FrameFlag::END_STREAM, stream.streamID);
+ frameWriter.setPayloadSize(0);
+ frameWriter.write(*m_socket);
+ stream.state = Stream::halfClosedLocal;
+ stream.data()->disconnect(this);
+ removeFromSuspended(stream.streamID);
+ } else if (!stream.data()->atEnd()) {
+ addToSuspended(stream);
+ }
+
+ return true;
+}
+
+bool QHttp2ProtocolHandler::sendWINDOW_UPDATE(quint32 streamID, quint32 delta)
+{
+ Q_ASSERT(m_socket);
+
+ frameWriter.start(FrameType::WINDOW_UPDATE, FrameFlag::EMPTY, streamID);
+ frameWriter.append(delta);
+ return frameWriter.write(*m_socket);
+}
+
+bool QHttp2ProtocolHandler::sendRST_STREAM(quint32 streamID, quint32 errorCode)
+{
+ Q_ASSERT(m_socket);
+
+ frameWriter.start(FrameType::RST_STREAM, FrameFlag::EMPTY, streamID);
+ frameWriter.append(errorCode);
+ return frameWriter.write(*m_socket);
+}
+
+bool QHttp2ProtocolHandler::sendGOAWAY(quint32 errorCode)
+{
+ Q_ASSERT(m_socket);
+
+ frameWriter.start(FrameType::GOAWAY, FrameFlag::EMPTY, connectionStreamID);
+ frameWriter.append(quint32(connectionStreamID));
+ frameWriter.append(errorCode);
+ return frameWriter.write(*m_socket);
+}
+
+void QHttp2ProtocolHandler::handleDATA()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::DATA);
+
+ const auto streamID = inboundFrame.streamID();
+ if (streamID == connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "DATA on stream 0x0");
+
+ if (!activeStreams.contains(streamID) && !streamWasReset(streamID))
+ return connectionError(ENHANCE_YOUR_CALM, "DATA on invalid stream");
+
+ if (qint32(inboundFrame.payloadSize()) > sessionRecvWindowSize)
+ return connectionError(FLOW_CONTROL_ERROR, "Flow control error");
+
+ sessionRecvWindowSize -= inboundFrame.payloadSize();
+
+ if (activeStreams.contains(streamID)) {
+ auto &stream = activeStreams[streamID];
+
+ if (qint32(inboundFrame.payloadSize()) > stream.recvWindow) {
+ finishStreamWithError(stream, QNetworkReply::ProtocolInvalidOperationError,
+ QLatin1String("flow control error"));
+ sendRST_STREAM(streamID, FLOW_CONTROL_ERROR);
+ markAsReset(streamID);
+ deleteActiveStream(streamID);
+ } else {
+ stream.recvWindow -= inboundFrame.payloadSize();
+ // Uncompress data if needed and append it ...
+ updateStream(stream, inboundFrame);
+
+ if (inboundFrame.flags().testFlag(FrameFlag::END_STREAM)) {
+ finishStream(stream);
+ deleteActiveStream(stream.streamID);
+ } else if (stream.recvWindow < streamInitialRecvWindowSize / 2) {
+ QMetaObject::invokeMethod(this, "sendWINDOW_UPDATE", Qt::QueuedConnection,
+ Q_ARG(quint32, stream.streamID),
+ Q_ARG(quint32, streamInitialRecvWindowSize - stream.recvWindow));
+ stream.recvWindow = streamInitialRecvWindowSize;
+ }
+ }
+ }
+
+ if (sessionRecvWindowSize < sessionMaxRecvWindowSize / 2) {
+ QMetaObject::invokeMethod(this, "sendWINDOW_UPDATE", Qt::QueuedConnection,
+ Q_ARG(quint32, connectionStreamID),
+ Q_ARG(quint32, sessionMaxRecvWindowSize - sessionRecvWindowSize));
+ sessionRecvWindowSize = sessionMaxRecvWindowSize;
+ }
+}
+
+void QHttp2ProtocolHandler::handleHEADERS()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::HEADERS);
+
+ const auto streamID = inboundFrame.streamID();
+ if (streamID == connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "HEADERS on 0x0 stream");
+
+ if (!activeStreams.contains(streamID) && !streamWasReset(streamID))
+ return connectionError(ENHANCE_YOUR_CALM, "HEADERS on invalid stream");
+
+ const auto flags = inboundFrame.flags();
+ if (flags.testFlag(FrameFlag::PRIORITY)) {
+ handlePRIORITY();
+ if (goingAway)
+ return;
+ }
+
+ const bool endHeaders = flags.testFlag(FrameFlag::END_HEADERS);
+ continuedFrames.clear();
+ continuedFrames.push_back(std::move(inboundFrame));
+ if (!endHeaders) {
+ continuationExpected = true;
+ return;
+ }
+
+ handleContinuedHEADERS();
+}
+
+void QHttp2ProtocolHandler::handlePRIORITY()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::PRIORITY ||
+ inboundFrame.type() == FrameType::HEADERS);
+
+ const auto streamID = inboundFrame.streamID();
+ if (streamID == connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "PIRORITY on 0x0 stream");
+
+ if (!activeStreams.contains(streamID) && !streamWasReset(streamID))
+ return connectionError(ENHANCE_YOUR_CALM, "PRIORITY on invalid stream");
+
+ quint32 streamDependency = 0;
+ uchar weight = 0;
+ const bool noErr = inboundFrame.priority(&streamDependency, &weight);
+ Q_UNUSED(noErr) Q_ASSERT(noErr);
+
+
+ const bool exclusive = streamDependency & 0x80000000;
+ streamDependency &= ~0x80000000;
+
+ // Ignore this for now ...
+ // Can be used for streams (re)prioritization - 5.3
+ Q_UNUSED(exclusive);
+ Q_UNUSED(weight);
+}
+
+void QHttp2ProtocolHandler::handleRST_STREAM()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::RST_STREAM);
+
+ // "RST_STREAM frames MUST be associated with a stream.
+ // If a RST_STREAM frame is received with a stream identifier of 0x0,
+ // the recipient MUST treat this as a connection error (Section 5.4.1)
+ // of type PROTOCOL_ERROR.
+ const auto streamID = inboundFrame.streamID();
+ if (streamID == connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "RST_STREAM on 0x0");
+
+ if (!(streamID & 0x1)) {
+ // RST_STREAM on a promised stream:
+ // since we do not keep track of such streams,
+ // just ignore.
+ return;
+ }
+
+ if (streamID >= nextID) {
+ // "RST_STREAM frames MUST NOT be sent for a stream
+ // in the "idle" state. .. the recipient MUST treat this
+ // as a connection error (Section 5.4.1) of type PROTOCOL_ERROR."
+ return connectionError(PROTOCOL_ERROR, "RST_STREAM on idle stream");
+ }
+
+ if (!activeStreams.contains(streamID)) {
+ // 'closed' stream, ignore.
+ return;
+ }
+
+ Q_ASSERT(inboundFrame.dataSize() == 4);
+
+ Stream &stream = activeStreams[streamID];
+ finishStreamWithError(stream, qFromBigEndian<quint32>(inboundFrame.dataBegin()));
+ markAsReset(stream.streamID);
+ deleteActiveStream(stream.streamID);
+}
+
+void QHttp2ProtocolHandler::handleSETTINGS()
+{
+ // 6.5 SETTINGS.
+ Q_ASSERT(inboundFrame.type() == FrameType::SETTINGS);
+
+ if (inboundFrame.streamID() != connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "SETTINGS on invalid stream");
+
+ if (inboundFrame.flags().testFlag(FrameFlag::ACK)) {
+ if (!waitingForSettingsACK)
+ return connectionError(PROTOCOL_ERROR, "unexpected SETTINGS ACK");
+ waitingForSettingsACK = false;
+ return;
+ }
+
+ if (inboundFrame.dataSize()) {
+ auto src = inboundFrame.dataBegin();
+ for (const uchar *end = src + inboundFrame.dataSize(); src != end; src += 6) {
+ const Settings identifier = Settings(qFromBigEndian<quint16>(src));
+ const quint32 intVal = qFromBigEndian<quint32>(src + 2);
+ if (!acceptSetting(identifier, intVal)) {
+ // If not accepted - we finish with connectionError.
+ return;
+ }
+ }
+ }
+
+ sendSETTINGS_ACK();
+}
+
+
+void QHttp2ProtocolHandler::handlePUSH_PROMISE()
+{
+ // 6.6 PUSH_PROMISE.
+ Q_ASSERT(inboundFrame.type() == FrameType::PUSH_PROMISE);
+
+ if (prefaceSent && !waitingForSettingsACK) {
+ // This means, server ACKed our 'NO PUSH',
+ // but sent us PUSH_PROMISE anyway.
+ return connectionError(PROTOCOL_ERROR, "unexpected PUSH_PROMISE frame");
+ }
+
+ const auto streamID = inboundFrame.streamID();
+ if (streamID == connectionStreamID) {
+ return connectionError(PROTOCOL_ERROR,
+ "PUSH_PROMISE with invalid associated stream (0x0)");
+ }
+
+ if (!activeStreams.contains(streamID) && !streamWasReset(streamID)) {
+ return connectionError(ENHANCE_YOUR_CALM,
+ "PUSH_PROMISE with invalid associated stream");
+ }
+
+ const auto reservedID = qFromBigEndian<quint32>(inboundFrame.dataBegin());
+ if (!reservedID || (reservedID & 0x1)) {
+ return connectionError(PROTOCOL_ERROR,
+ "PUSH_PROMISE with invalid promised stream ID");
+ }
+
+ // "ignoring a PUSH_PROMISE frame causes the stream
+ // state to become indeterminate" - let's RST_STREAM it then ...
+ sendRST_STREAM(reservedID, REFUSE_STREAM);
+ markAsReset(reservedID);
+
+ const bool endHeaders = inboundFrame.flags().testFlag(FrameFlag::END_HEADERS);
+ continuedFrames.clear();
+ continuedFrames.push_back(std::move(inboundFrame));
+
+ if (!endHeaders) {
+ continuationExpected = true;
+ return;
+ }
+
+ handleContinuedHEADERS();
+}
+
+void QHttp2ProtocolHandler::handlePING()
+{
+ // Since we're implementing a client and not
+ // a server, we only reply to a PING, ACKing it.
+ Q_ASSERT(inboundFrame.type() == FrameType::PING);
+ Q_ASSERT(m_socket);
+
+ if (inboundFrame.streamID() != connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "PING on invalid stream");
+
+ if (inboundFrame.flags() & FrameFlag::ACK)
+ return connectionError(PROTOCOL_ERROR, "unexpected PING ACK");
+
+ Q_ASSERT(inboundFrame.dataSize() == 8);
+
+ frameWriter.start(FrameType::PING, FrameFlag::ACK, connectionStreamID);
+ frameWriter.append(inboundFrame.dataBegin(), inboundFrame.dataBegin() + 8);
+ frameWriter.write(*m_socket);
+}
+
+void QHttp2ProtocolHandler::handleGOAWAY()
+{
+ // 6.8 GOAWAY
+
+ Q_ASSERT(inboundFrame.type() == FrameType::GOAWAY);
+ // "An endpoint MUST treat a GOAWAY frame with a stream identifier
+ // other than 0x0 as a connection error (Section 5.4.1) of type PROTOCOL_ERROR."
+ if (inboundFrame.streamID() != connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "GOAWAY on invalid stream");
+
+ const auto src = inboundFrame.dataBegin();
+ quint32 lastStreamID = qFromBigEndian<quint32>(src);
+ const quint32 errorCode = qFromBigEndian<quint32>(src + 4);
+
+ if (!lastStreamID) {
+ // "The last stream identifier can be set to 0 if no
+ // streams were processed."
+ lastStreamID = 1;
+ }
+
+ 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) {
+ // "A server that is attempting to gracefully shut down a connection SHOULD
+ // send an initial GOAWAY frame with the last stream identifier set to 2^31-1
+ // and a NO_ERROR code."
+ if (lastStreamID != (quint32(1) << 31) - 1 || errorCode != HTTP2_NO_ERROR)
+ return connectionError(PROTOCOL_ERROR, "GOAWAY invalid stream/error code");
+ lastStreamID = 1;
+ } else {
+ lastStreamID += 2;
+ }
+
+ goingAway = true;
+
+ QNetworkReply::NetworkError error = QNetworkReply::NoError;
+ QString message;
+ qt_error(errorCode, error, message);
+
+ for (quint32 id = lastStreamID; id < nextID; id += 2) {
+ const auto it = activeStreams.find(id);
+ if (it != activeStreams.end()) {
+ Stream &stream = *it;
+ finishStreamWithError(stream, error, message);
+ markAsReset(id);
+ deleteActiveStream(id);
+ } else {
+ removeFromSuspended(id);
+ }
+ }
+
+ if (!activeStreams.size())
+ closeSession();
+}
+
+void QHttp2ProtocolHandler::handleWINDOW_UPDATE()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::WINDOW_UPDATE);
+
+
+ const quint32 delta = qFromBigEndian<quint32>(inboundFrame.dataBegin());
+ const bool valid = delta && delta <= quint32(std::numeric_limits<qint32>::max());
+ const auto streamID = inboundFrame.streamID();
+
+ if (streamID == Http2::connectionStreamID) {
+ if (!valid || sum_will_overflow(sessionSendWindowSize, delta))
+ return connectionError(PROTOCOL_ERROR, "WINDOW_UPDATE invalid delta");
+ sessionSendWindowSize += delta;
+ } else {
+ if (!activeStreams.contains(streamID)) {
+ // WINDOW_UPDATE on closed streams can be ignored.
+ return;
+ }
+ auto &stream = activeStreams[streamID];
+ if (!valid || sum_will_overflow(stream.sendWindow, delta)) {
+ finishStreamWithError(stream, QNetworkReply::ProtocolInvalidOperationError,
+ QLatin1String("invalid WINDOW_UPDATE delta"));
+ sendRST_STREAM(streamID, PROTOCOL_ERROR);
+ markAsReset(streamID);
+ deleteActiveStream(streamID);
+ return;
+ }
+ stream.sendWindow += delta;
+ }
+
+ // Since we're in _q_receiveReply at the moment, let's first handle other
+ // frames and resume suspended streams (if any) == start sending our own frame
+ // after handling these frames, since one them can be e.g. GOAWAY.
+ QMetaObject::invokeMethod(this, "resumeSuspendedStreams", Qt::QueuedConnection);
+}
+
+void QHttp2ProtocolHandler::handleCONTINUATION()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::CONTINUATION);
+ Q_ASSERT(continuedFrames.size()); // HEADERS frame must be already in.
+
+ if (inboundFrame.streamID() != continuedFrames.front().streamID())
+ return connectionError(PROTOCOL_ERROR, "CONTINUATION on invalid stream");
+
+ const bool endHeaders = inboundFrame.flags().testFlag(FrameFlag::END_HEADERS);
+ continuedFrames.push_back(std::move(inboundFrame));
+
+ if (!endHeaders)
+ return;
+
+ continuationExpected = false;
+ handleContinuedHEADERS();
+}
+
+void QHttp2ProtocolHandler::handleContinuedHEADERS()
+{
+ Q_ASSERT(continuedFrames.size());
+
+ const auto streamID = continuedFrames[0].streamID();
+
+ if (continuedFrames[0].type() == FrameType::HEADERS) {
+ if (activeStreams.contains(streamID)) {
+ Stream &stream = activeStreams[streamID];
+ if (stream.state != Stream::halfClosedLocal) {
+ // If we're receiving headers, they're a response to a request we sent;
+ // and we closed our end when we finished sending that.
+ finishStreamWithError(stream, QNetworkReply::ProtocolInvalidOperationError,
+ QLatin1String("HEADERS on invalid stream"));
+ sendRST_STREAM(streamID, CANCEL);
+ markAsReset(streamID);
+ deleteActiveStream(streamID);
+ return;
+ }
+ } else if (!streamWasReset(streamID)) {
+ return connectionError(PROTOCOL_ERROR, "HEADERS on invalid stream");
+ }
+ }
+
+ quint32 total = 0;
+ for (const auto &frame : continuedFrames)
+ total += frame.dataSize();
+
+ if (!total) {
+ // It could be a PRIORITY sent in HEADERS - handled by this point.
+ return;
+ }
+
+ std::vector<uchar> hpackBlock(total);
+ auto dst = hpackBlock.begin();
+ for (const auto &frame : continuedFrames) {
+ if (!frame.dataSize())
+ continue;
+ const uchar *src = frame.dataBegin();
+ std::copy(src, src + frame.dataSize(), dst);
+ dst += frame.dataSize();
+ }
+
+ HPack::BitIStream inputStream{&hpackBlock[0],
+ &hpackBlock[0] + hpackBlock.size()};
+
+ if (!decoder.decodeHeaderFields(inputStream))
+ return connectionError(COMPRESSION_ERROR, "HPACK decompression failed");
+
+ if (continuedFrames[0].type() == FrameType::HEADERS) {
+ if (activeStreams.contains(streamID)) {
+ Stream &stream = activeStreams[streamID];
+ updateStream(stream, decoder.decodedHeader());
+ if (continuedFrames[0].flags() & FrameFlag::END_STREAM) {
+ finishStream(stream);
+ deleteActiveStream(stream.streamID);
+ }
+ }
+ }
+}
+
+bool QHttp2ProtocolHandler::acceptSetting(Http2::Settings identifier, quint32 newValue)
+{
+ if (identifier == Settings::HEADER_TABLE_SIZE_ID) {
+ if (newValue > maxAcceptableTableSize) {
+ connectionError(PROTOCOL_ERROR, "SETTINGS invalid table size");
+ return false;
+ }
+ encoder.setMaxDynamicTableSize(newValue);
+ }
+
+ if (identifier == Settings::INITIAL_WINDOW_SIZE_ID) {
+ // For every active stream - adjust its window
+ // (and handle possible overflows as errors).
+ if (newValue > quint32(std::numeric_limits<qint32>::max())) {
+ connectionError(FLOW_CONTROL_ERROR, "SETTINGS invalid initial window size");
+ return false;
+ }
+
+ const qint32 delta = qint32(newValue) - streamInitialSendWindowSize;
+ streamInitialSendWindowSize = newValue;
+
+ std::vector<quint32> brokenStreams;
+ brokenStreams.reserve(activeStreams.size());
+ for (auto &stream : activeStreams) {
+ if (sum_will_overflow(stream.sendWindow, delta)) {
+ brokenStreams.push_back(stream.streamID);
+ continue;
+ }
+ stream.sendWindow += delta;
+ }
+
+ for (auto id : brokenStreams) {
+ auto &stream = activeStreams[id];
+ finishStreamWithError(stream, QNetworkReply::ProtocolInvalidOperationError,
+ QLatin1String("SETTINGS window overflow"));
+ sendRST_STREAM(id, PROTOCOL_ERROR);
+ markAsReset(id);
+ deleteActiveStream(id);
+ }
+
+ QMetaObject::invokeMethod(this, "resumeSuspendedStreams", Qt::QueuedConnection);
+ }
+
+ if (identifier == Settings::MAX_CONCURRENT_STREAMS_ID) {
+ if (maxConcurrentStreams > maxPeerConcurrentStreams) {
+ connectionError(PROTOCOL_ERROR, "SETTINGS invalid number of concurrent streams");
+ return false;
+ }
+ maxConcurrentStreams = newValue;
+ }
+
+ if (identifier == Settings::MAX_FRAME_SIZE_ID) {
+ if (newValue < Http2::maxFrameSize || newValue > Http2::maxPayloadSize) {
+ connectionError(PROTOCOL_ERROR, "SETTGINGS max frame size is out of range");
+ return false;
+ }
+ maxFrameSize = newValue;
+ }
+
+ if (identifier == Settings::MAX_HEADER_LIST_SIZE_ID) {
+ // We just remember this value, it can later
+ // prevent us from sending any request (and this
+ // will end up in request/reply error).
+ maxHeaderListSize = newValue;
+ }
+
+ return true;
+}
+
+void QHttp2ProtocolHandler::updateStream(Stream &stream, const HPack::HttpHeader &headers)
+{
+ const auto httpReply = stream.reply();
+ Q_ASSERT(httpReply);
+ const auto httpReplyPrivate = httpReply->d_func();
+ for (const auto &pair : headers) {
+ const auto &name = pair.name;
+ auto value = pair.value;
+
+ if (name == ":status") {
+ httpReply->setStatusCode(value.left(3).toInt());
+ httpReplyPrivate->reasonPhrase = QString::fromLatin1(value.mid(4));
+ } else if (name == ":version") {
+ httpReplyPrivate->majorVersion = value.at(5) - '0';
+ httpReplyPrivate->minorVersion = value.at(7) - '0';
+ } else if (name == "content-length") {
+ bool ok = false;
+ const qlonglong length = value.toLongLong(&ok);
+ if (ok)
+ httpReply->setContentLength(length);
+ } else {
+ QByteArray binder(", ");
+ if (name == "set-cookie")
+ binder = "\n";
+ httpReply->setHeaderField(name, value.replace('\0', binder));
+ }
+ }
+
+ emit httpReply->headerChanged();
+}
+
+void QHttp2ProtocolHandler::updateStream(Stream &stream, const Frame &frame)
+{
+ Q_ASSERT(frame.type() == FrameType::DATA);
+
+ if (const auto length = frame.dataSize()) {
+ const char *data = reinterpret_cast<const char *>(frame.dataBegin());
+ auto &httpRequest = stream.request();
+ auto httpReply = stream.reply();
+ Q_ASSERT(httpReply);
+ auto replyPrivate = httpReply->d_func();
+
+ replyPrivate->compressedData.append(data, length);
+ replyPrivate->totalProgress += length;
+
+ const QByteArray wrapped(data, length);
+ if (httpRequest.d->autoDecompress && replyPrivate->isCompressed()) {
+ QByteDataBuffer inDataBuffer;
+ inDataBuffer.append(wrapped);
+ replyPrivate->uncompressBodyData(&inDataBuffer, &replyPrivate->responseData);
+ } else {
+ replyPrivate->responseData.append(wrapped);
+ }
+
+ if (replyPrivate->shouldEmitSignals()) {
+ emit httpReply->readyRead();
+ emit httpReply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
+ }
+ }
+}
+
+void QHttp2ProtocolHandler::finishStream(Stream &stream)
+{
+ stream.state = Stream::closed;
+ auto httpReply = stream.reply();
+ Q_ASSERT(httpReply);
+ httpReply->disconnect(this);
+ if (stream.data())
+ stream.data()->disconnect(this);
+
+ qCDebug(QT_HTTP2) << "stream" << stream.streamID << "closed";
+
+ emit httpReply->finished();
+}
+
+void QHttp2ProtocolHandler::finishStreamWithError(Stream &stream, quint32 errorCode)
+{
+ QNetworkReply::NetworkError error = QNetworkReply::NoError;
+ QString message;
+ qt_error(errorCode, error, message);
+ finishStreamWithError(stream, error, message);
+}
+
+void QHttp2ProtocolHandler::finishStreamWithError(Stream &stream, QNetworkReply::NetworkError error,
+ const QString &message)
+{
+ stream.state = Stream::closed;
+ auto httpReply = stream.reply();
+ Q_ASSERT(httpReply);
+ httpReply->disconnect(this);
+ if (stream.data())
+ stream.data()->disconnect(this);
+
+ qCWarning(QT_HTTP2) << "stream" << stream.streamID
+ << "finished with error:" << message;
+
+ // TODO: error message must be translated!!! (tr)
+ emit httpReply->finishedWithError(error, message);
+}
+
+quint32 QHttp2ProtocolHandler::createNewStream(const HttpMessagePair &message)
+{
+ const qint32 newStreamID = allocateStreamID();
+ if (!newStreamID)
+ return 0;
+
+ Q_ASSERT(!activeStreams.contains(newStreamID));
+
+ const auto reply = message.second;
+ const auto replyPrivate = reply->d_func();
+ replyPrivate->connection = m_connection;
+ replyPrivate->connectionChannel = m_channel;
+ reply->setSpdyWasUsed(true);
+ reply->setProperty("HTTP2StreamID", newStreamID);
+ connect(reply, SIGNAL(destroyed(QObject*)),
+ this, SLOT(_q_replyDestroyed(QObject*)));
+
+ const Stream newStream(message, newStreamID,
+ streamInitialSendWindowSize,
+ streamInitialRecvWindowSize);
+
+ if (auto src = newStream.data()) {
+ connect(src, SIGNAL(readyRead()), this,
+ SLOT(_q_uploadDataReadyRead()), Qt::QueuedConnection);
+ src->setProperty("HTTP2StreamID", newStreamID);
+ }
+
+ activeStreams.insert(newStreamID, newStream);
+
+ return newStreamID;
+}
+
+void QHttp2ProtocolHandler::addToSuspended(Stream &stream)
+{
+ qCDebug(QT_HTTP2) << "stream" << stream.streamID
+ << "suspended by flow control";
+ const auto priority = stream.priority();
+ Q_ASSERT(int(priority) >= 0 && int(priority) < 3);
+ suspendedStreams[priority].push_back(stream.streamID);
+}
+
+void QHttp2ProtocolHandler::markAsReset(quint32 streamID)
+{
+ // For now, we trace only client's streams (created by us,
+ // odd integer numbers).
+ if (streamID & 0x1) {
+ qCDebug(QT_HTTP2) << "stream" << streamID << "was reset";
+ // This part is quite tricky: I have to clear this set
+ // so that it does not become tOOO big.
+ if (recycledStreams.size() > maxRecycledStreams) {
+ // At least, I'm erasing the oldest first ...
+ recycledStreams.erase(recycledStreams.begin(),
+ recycledStreams.begin() +
+ recycledStreams.size() / 2);
+ }
+
+ const auto it = std::lower_bound(recycledStreams.begin(), recycledStreams.end(),
+ streamID);
+ if (it != recycledStreams.end() && *it == streamID)
+ return;
+
+ recycledStreams.insert(it, streamID);
+ }
+}
+
+quint32 QHttp2ProtocolHandler::popStreamToResume()
+{
+ quint32 streamID = connectionStreamID;
+ const int nQ = sizeof suspendedStreams / sizeof suspendedStreams[0];
+ using QNR = QHttpNetworkRequest;
+ const QNR::Priority ranks[nQ] = {QNR::HighPriority,
+ QNR::NormalPriority,
+ QNR::LowPriority};
+
+ for (int i = 0; i < nQ; ++i) {
+ auto &queue = suspendedStreams[ranks[i]];
+ auto it = queue.begin();
+ for (; it != queue.end(); ++it) {
+ if (!activeStreams.contains(*it))
+ continue;
+ if (activeStreams[*it].sendWindow > 0)
+ break;
+ }
+
+ if (it != queue.end()) {
+ streamID = *it;
+ queue.erase(it);
+ break;
+ }
+ }
+
+ return streamID;
+}
+
+void QHttp2ProtocolHandler::removeFromSuspended(quint32 streamID)
+{
+ const int nQ = sizeof suspendedStreams / sizeof suspendedStreams[0];
+ for (int i = 0; i < nQ; ++i) {
+ auto &q = suspendedStreams[i];
+ q.erase(std::remove(q.begin(), q.end(), streamID), q.end());
+ }
+}
+
+void QHttp2ProtocolHandler::deleteActiveStream(quint32 streamID)
+{
+ if (activeStreams.contains(streamID)) {
+ auto &stream = activeStreams[streamID];
+ if (stream.reply())
+ stream.reply()->disconnect(this);
+ if (stream.data())
+ stream.data()->disconnect(this);
+ activeStreams.remove(streamID);
+ }
+
+ removeFromSuspended(streamID);
+ if (m_channel->spdyRequestsToSend.size())
+ QMetaObject::invokeMethod(this, "sendRequest", Qt::QueuedConnection);
+}
+
+bool QHttp2ProtocolHandler::streamWasReset(quint32 streamID) const
+{
+ const auto it = std::lower_bound(recycledStreams.begin(),
+ recycledStreams.end(),
+ streamID);
+ return it != recycledStreams.end() && *it == streamID;
+}
+
+void QHttp2ProtocolHandler::resumeSuspendedStreams()
+{
+ while (sessionSendWindowSize > 0) {
+ const auto streamID = popStreamToResume();
+ if (!streamID)
+ return;
+
+ if (!activeStreams.contains(streamID))
+ continue;
+
+ Stream &stream = activeStreams[streamID];
+ if (!sendDATA(stream)) {
+ finishStreamWithError(stream, QNetworkReply::UnknownNetworkError,
+ QLatin1String("failed to send DATA"));
+ sendRST_STREAM(streamID, INTERNAL_ERROR);
+ markAsReset(streamID);
+ deleteActiveStream(streamID);
+ }
+ }
+}
+
+quint32 QHttp2ProtocolHandler::allocateStreamID()
+{
+ // With protocol upgrade streamID == 1 will become
+ // invalid. The logic must be updated.
+ if (nextID > quint32(std::numeric_limits<qint32>::max()))
+ return 0;
+
+ const quint32 streamID = nextID;
+ nextID += 2;
+
+ return streamID;
+}
+
+void QHttp2ProtocolHandler::connectionError(Http2::Http2Error errorCode,
+ const char *message)
+{
+ Q_ASSERT(message);
+ Q_ASSERT(!goingAway);
+
+ qCCritical(QT_HTTP2) << "connection error:" << message;
+
+ goingAway = true;
+ sendGOAWAY(errorCode);
+ const auto error = qt_error(errorCode);
+ m_channel->emitFinishedWithError(error, message);
+
+ for (auto &stream: activeStreams)
+ finishStreamWithError(stream, error, QLatin1String(message));
+
+ closeSession();
+}
+
+void QHttp2ProtocolHandler::closeSession()
+{
+ activeStreams.clear();
+ for (auto &q: suspendedStreams)
+ q.clear();
+ recycledStreams.clear();
+
+ m_channel->close();
+}
+
+QT_END_NAMESPACE
+
+#endif // !defined(QT_NO_HTTP)
diff --git a/src/network/access/qhttp2protocolhandler_p.h b/src/network/access/qhttp2protocolhandler_p.h
new file mode 100644
index 0000000000..92c6851078
--- /dev/null
+++ b/src/network/access/qhttp2protocolhandler_p.h
@@ -0,0 +1,208 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 QHTTP2PROTOCOLHANDLER_P_H
+#define QHTTP2PROTOCOLHANDLER_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qhttpnetworkconnectionchannel_p.h>
+#include <private/qabstractprotocolhandler_p.h>
+#include <private/qhttpnetworkrequest_p.h>
+
+#if !defined(QT_NO_HTTP)
+
+#include "http2/http2protocol_p.h"
+#include "http2/http2streams_p.h"
+#include "http2/http2frames_p.h"
+#include "http2/hpacktable_p.h"
+#include "http2/hpack_p.h"
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qglobal.h>
+#include <QtCore/qobject.h>
+#include <QtCore/qflags.h>
+#include <QtCore/qhash.h>
+
+#include <vector>
+#include <limits>
+#include <deque>
+#include <set>
+
+QT_BEGIN_NAMESPACE
+
+class QHttp2ProtocolHandler : public QObject, public QAbstractProtocolHandler
+{
+ Q_OBJECT
+
+public:
+ QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *channel);
+
+ QHttp2ProtocolHandler(const QHttp2ProtocolHandler &rhs) = delete;
+ QHttp2ProtocolHandler(QHttp2ProtocolHandler &&rhs) = delete;
+
+ QHttp2ProtocolHandler &operator = (const QHttp2ProtocolHandler &rhs) = delete;
+ QHttp2ProtocolHandler &operator = (QHttp2ProtocolHandler &&rhs) = delete;
+
+private slots:
+ void _q_uploadDataReadyRead();
+ void _q_replyDestroyed(QObject* reply);
+
+private:
+ using Stream = Http2::Stream;
+
+ void _q_readyRead() override;
+ void _q_receiveReply() override;
+ Q_INVOKABLE bool sendRequest() override;
+
+ bool sendClientPreface();
+ bool sendSETTINGS_ACK();
+ bool sendHEADERS(Stream &stream);
+ bool sendDATA(Stream &stream);
+ Q_INVOKABLE bool sendWINDOW_UPDATE(quint32 streamID, quint32 delta);
+ bool sendRST_STREAM(quint32 streamID, quint32 errorCoder);
+ bool sendGOAWAY(quint32 errorCode);
+
+ void handleDATA();
+ void handleHEADERS();
+ void handlePRIORITY();
+ void handleRST_STREAM();
+ void handleSETTINGS();
+ void handlePUSH_PROMISE();
+ void handlePING();
+ void handleGOAWAY();
+ void handleWINDOW_UPDATE();
+ void handleCONTINUATION();
+
+ void handleContinuedHEADERS();
+
+ bool acceptSetting(Http2::Settings identifier, quint32 newValue);
+
+ void updateStream(Stream &stream, const HPack::HttpHeader &headers);
+ void updateStream(Stream &stream, const Http2::Frame &dataFrame);
+ void finishStream(Stream &stream);
+ // Error code send by a peer (GOAWAY/RST_STREAM):
+ void finishStreamWithError(Stream &stream, quint32 errorCode);
+ // Locally encountered error:
+ void finishStreamWithError(Stream &stream, QNetworkReply::NetworkError error,
+ const QString &message);
+
+ // Stream's lifecycle management:
+ quint32 createNewStream(const HttpMessagePair &message);
+ void addToSuspended(Stream &stream);
+ void markAsReset(quint32 streamID);
+ quint32 popStreamToResume();
+ void removeFromSuspended(quint32 streamID);
+ void deleteActiveStream(quint32 streamID);
+ bool streamWasReset(quint32 streamID) const;
+
+ bool prefaceSent = false;
+ // In the current implementation we send
+ // SETTINGS only once, immediately after
+ // the client's preface 24-byte message.
+ bool waitingForSettingsACK = false;
+
+ static const quint32 maxAcceptableTableSize = 16 * HPack::FieldLookupTable::DefaultSize;
+ // HTTP/2 4.3: Header compression is stateful. One compression context and
+ // one decompression context are used for the entire connection.
+ HPack::Decoder decoder;
+ HPack::Encoder encoder;
+
+ QHash<quint32, Stream> activeStreams;
+ std::deque<quint32> suspendedStreams[3]; // 3 for priorities: High, Normal, Low.
+ static const std::deque<quint32>::size_type maxRecycledStreams;
+ std::deque<quint32> recycledStreams;
+
+ // Peer's max frame size.
+ quint32 maxFrameSize = Http2::maxFrameSize;
+
+ Http2::FrameReader frameReader;
+ Http2::Frame inboundFrame;
+ Http2::FrameWriter frameWriter;
+ // Temporary storage to assemble HEADERS' block
+ // from several CONTINUATION frames ...
+ bool continuationExpected = false;
+ std::vector<Http2::Frame> continuedFrames;
+
+ // Peer's max number of streams ...
+ quint32 maxConcurrentStreams = Http2::maxConcurrentStreams;
+
+ // Control flow:
+ static const qint32 sessionMaxRecvWindowSize = Http2::defaultSessionWindowSize * 10;
+ // Signed integer, it can become negative (it's still a valid window size):
+ qint32 sessionRecvWindowSize = sessionMaxRecvWindowSize;
+
+ // We do not negotiate this window size
+ // We have to send WINDOW_UPDATE frames to our peer also.
+ static const qint32 streamInitialRecvWindowSize = Http2::defaultSessionWindowSize;
+
+ // Updated by SETTINGS and WINDOW_UPDATE.
+ qint32 sessionSendWindowSize = Http2::defaultSessionWindowSize;
+ qint32 streamInitialSendWindowSize = Http2::defaultSessionWindowSize;
+
+ // It's unlimited by default, but can be changed via SETTINGS.
+ quint32 maxHeaderListSize = (std::numeric_limits<quint32>::max)();
+
+ Q_INVOKABLE void resumeSuspendedStreams();
+ // Our stream IDs (all odd), the first valid will be 1.
+ quint32 nextID = 1;
+ quint32 allocateStreamID();
+ bool validPeerStreamID() const;
+ bool goingAway = false;
+
+ // Errors:
+ void connectionError(Http2::Http2Error errorCode,
+ const char *message);
+ void closeSession();
+};
+
+QT_END_NAMESPACE
+
+#endif // !defined(QT_NO_HTTP)
+
+#endif
diff --git a/src/network/access/qhttpmultipart.h b/src/network/access/qhttpmultipart.h
index cbf28c083e..6d4531b099 100644
--- a/src/network/access/qhttpmultipart.h
+++ b/src/network/access/qhttpmultipart.h
@@ -40,6 +40,7 @@
#ifndef QHTTPMULTIPART_H
#define QHTTPMULTIPART_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/QSharedDataPointer>
#include <QtCore/QByteArray>
#include <QtCore/QIODevice>
diff --git a/src/network/access/qhttpmultipart_p.h b/src/network/access/qhttpmultipart_p.h
index 875f52f8e3..a03df9cb13 100644
--- a/src/network/access/qhttpmultipart_p.h
+++ b/src/network/access/qhttpmultipart_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "QtCore/qshareddata.h"
#include "qnetworkrequest_p.h" // for deriving QHttpPartPrivate from QNetworkHeadersPrivate
#include "private/qobject_p.h"
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index 79f418f675..8e5cfd9497 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -60,6 +60,7 @@
# include <QtNetwork/qsslkey.h>
# include <QtNetwork/qsslcipher.h>
# include <QtNetwork/qsslconfiguration.h>
+# include <QtNetwork/qsslerror.h>
#endif
@@ -82,9 +83,10 @@ QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &host
networkLayerState(Unknown),
hostName(hostName), port(port), encrypt(encrypt), delayIpv4(true)
#ifndef QT_NO_SSL
-, channelCount((type == QHttpNetworkConnection::ConnectionTypeSPDY) ? 1 : defaultHttpChannelCount)
+, channelCount((type == QHttpNetworkConnection::ConnectionTypeSPDY || type == QHttpNetworkConnection::ConnectionTypeHTTP2)
+ ? 1 : defaultHttpChannelCount)
#else
-, channelCount(defaultHttpChannelCount)
+, channelCount(type == QHttpNetworkConnection::ConnectionTypeHTTP2 ? 1 : defaultHttpChannelCount)
#endif // QT_NO_SSL
#ifndef QT_NO_NETWORKPROXY
, networkProxy(QNetworkProxy::NoProxy)
@@ -317,9 +319,9 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
if (systemLocale == QLatin1String("C"))
acceptLanguage = QString::fromLatin1("en,*");
else if (systemLocale.startsWith(QLatin1String("en-")))
- acceptLanguage = QString::fromLatin1("%1,*").arg(systemLocale);
+ acceptLanguage = systemLocale + QLatin1String(",*");
else
- acceptLanguage = QString::fromLatin1("%1,en,*").arg(systemLocale);
+ acceptLanguage = systemLocale + QLatin1String(",en,*");
request.setHeaderField("Accept-Language", acceptLanguage.toLatin1());
}
@@ -617,13 +619,11 @@ QHttpNetworkReply* QHttpNetworkConnectionPrivate::queueRequest(const QHttpNetwor
break;
}
}
-#ifndef QT_NO_SSL
- else { // SPDY
+ else { // SPDY, HTTP/2
if (!pair.second->d_func()->requestIsPrepared)
prepareRequest(pair);
channels[0].spdyRequestsToSend.insertMulti(request.priority(), pair);
}
-#endif // QT_NO_SSL
// For Happy Eyeballs the networkLayerState is set to Unknown
// untill we have started the first connection attempt. So no
@@ -1011,8 +1011,9 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
}
break;
}
+ case QHttpNetworkConnection::ConnectionTypeHTTP2:
case QHttpNetworkConnection::ConnectionTypeSPDY: {
-#ifndef QT_NO_SSL
+
if (channels[0].spdyRequestsToSend.isEmpty())
return;
@@ -1024,7 +1025,6 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
if (channels[0].socket && channels[0].socket->state() == QAbstractSocket::ConnectedState
&& !channels[0].pendingEncrypt)
channels[0].sendRequest();
-#endif // QT_NO_SSL
break;
}
}
diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h
index e05bc1df74..cad0ab1ac4 100644
--- a/src/network/access/qhttpnetworkconnection_p.h
+++ b/src/network/access/qhttpnetworkconnection_p.h
@@ -50,6 +50,8 @@
//
// We mean it.
//
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <QtNetwork/qnetworkrequest.h>
#include <QtNetwork/qnetworkreply.h>
#include <QtNetwork/qabstractsocket.h>
@@ -70,17 +72,6 @@
#ifndef QT_NO_HTTP
-#ifndef QT_NO_SSL
-#ifndef QT_NO_OPENSSL
-# include <private/qsslcontext_openssl_p.h>
-#endif
-# include <private/qsslsocket_p.h>
-# include <QtNetwork/qsslsocket.h>
-# include <QtNetwork/qsslerror.h>
-#else
-# include <QtNetwork/qtcpsocket.h>
-#endif
-
QT_BEGIN_NAMESPACE
class QHttpNetworkRequest;
@@ -88,6 +79,10 @@ class QHttpNetworkReply;
class QHttpThreadDelegate;
class QByteArray;
class QHostInfo;
+#ifndef QT_NO_SSL
+class QSslConfiguration;
+class QSslContext;
+#endif // !QT_NO_SSL
class QHttpNetworkConnectionPrivate;
class Q_AUTOTEST_EXPORT QHttpNetworkConnection : public QObject
@@ -97,7 +92,8 @@ public:
enum ConnectionType {
ConnectionTypeHTTP,
- ConnectionTypeSPDY
+ ConnectionTypeSPDY,
+ ConnectionTypeHTTP2
};
#ifndef QT_NO_BEARERMANAGEMENT
@@ -159,6 +155,7 @@ private:
friend class QHttpNetworkReply;
friend class QHttpNetworkReplyPrivate;
friend class QHttpNetworkConnectionChannel;
+ friend class QHttp2ProtocolHandler;
friend class QHttpProtocolHandler;
friend class QSpdyProtocolHandler;
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index 2f7be01078..b6a9b80511 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -47,10 +47,12 @@
#ifndef QT_NO_HTTP
+#include <private/qhttp2protocolhandler_p.h>
#include <private/qhttpprotocolhandler_p.h>
#include <private/qspdyprotocolhandler_p.h>
#ifndef QT_NO_SSL
+# include <private/qsslsocket_p.h>
# include <QtNetwork/qsslkey.h>
# include <QtNetwork/qsslcipher.h>
# include <QtNetwork/qsslconfiguration.h>
@@ -177,8 +179,11 @@ void QHttpNetworkConnectionChannel::init()
if (!sslConfiguration.isNull())
sslSocket->setSslConfiguration(sslConfiguration);
} else {
-#endif // QT_NO_SSL
- protocolHandler.reset(new QHttpProtocolHandler(this));
+#endif // !QT_NO_SSL
+ if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2)
+ protocolHandler.reset(new QHttp2ProtocolHandler(this));
+ else
+ protocolHandler.reset(new QHttpProtocolHandler(this));
#ifndef QT_NO_SSL
}
#endif
@@ -833,10 +838,17 @@ void QHttpNetworkConnectionChannel::_q_connected()
#endif
} else {
state = QHttpNetworkConnectionChannel::IdleState;
- if (!reply)
- connection->d_func()->dequeueRequest(socket);
- if (reply)
- sendRequest();
+ if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2) {
+ 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);
+ }
+ } else {
+ if (!reply)
+ connection->d_func()->dequeueRequest(socket);
+ if (reply)
+ sendRequest();
+ }
}
}
@@ -970,8 +982,12 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
}
} while (!connection->d_func()->highPriorityQueue.isEmpty()
|| !connection->d_func()->lowPriorityQueue.isEmpty());
+
+ if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
#ifndef QT_NO_SSL
- if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY) {
+ || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY
+#endif
+ ) {
QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values();
for (int a = 0; a < spdyPairs.count(); ++a) {
// emit error for all replies
@@ -980,7 +996,6 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
emit currentReply->finishedWithError(errorCode, errorString);
}
}
-#endif // QT_NO_SSL
// send the next request
QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);
@@ -1002,19 +1017,19 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
#ifndef QT_NO_NETWORKPROXY
void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth)
{
+ if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
#ifndef QT_NO_SSL
- if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY) {
+ || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY
+#endif
+ ) {
connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
} else { // HTTP
-#endif // QT_NO_SSL
// Need to dequeue the request before we can emit the error.
if (!reply)
connection->d_func()->dequeueRequest(socket);
if (reply)
connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
-#ifndef QT_NO_SSL
}
-#endif // QT_NO_SSL
}
#endif
@@ -1024,6 +1039,19 @@ void QHttpNetworkConnectionChannel::_q_uploadDataReadyRead()
sendRequest();
}
+void QHttpNetworkConnectionChannel::emitFinishedWithError(QNetworkReply::NetworkError error,
+ const char *message)
+{
+ if (reply)
+ emit reply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message));
+ QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values();
+ for (int a = 0; a < spdyPairs.count(); ++a) {
+ QHttpNetworkReply *currentReply = spdyPairs.at(a).second;
+ Q_ASSERT(currentReply);
+ emit currentReply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message));
+ }
+}
+
#ifndef QT_NO_SSL
void QHttpNetworkConnectionChannel::_q_encrypted()
{
@@ -1032,7 +1060,7 @@ void QHttpNetworkConnectionChannel::_q_encrypted()
if (!protocolHandler) {
switch (sslSocket->sslConfiguration().nextProtocolNegotiationStatus()) {
- case QSslConfiguration::NextProtocolNegotiationNegotiated: /* fall through */
+ case QSslConfiguration::NextProtocolNegotiationNegotiated:
case QSslConfiguration::NextProtocolNegotiationUnsupported: {
QByteArray nextProtocol = sslSocket->sslConfiguration().nextNegotiatedProtocol();
if (nextProtocol == QSslConfiguration::NextProtocolHttp1_1) {
@@ -1043,11 +1071,16 @@ void QHttpNetworkConnectionChannel::_q_encrypted()
// no need to re-queue requests, if SPDY was enabled on the request it
// has gone to the SPDY queue already
break;
+ } else if (nextProtocol == QSslConfiguration::ALPNProtocolHTTP2) {
+ protocolHandler.reset(new QHttp2ProtocolHandler(this));
+ connection->setConnectionType(QHttpNetworkConnection::ConnectionTypeHTTP2);
+ break;
} else {
emitFinishedWithError(QNetworkReply::SslHandshakeFailedError,
"detected unknown Next Protocol Negotiation protocol");
break;
}
+ Q_FALLTHROUGH();
}
case QSslConfiguration::NextProtocolNegotiationNone:
protocolHandler.reset(new QHttpProtocolHandler(this));
@@ -1066,11 +1099,13 @@ void QHttpNetworkConnectionChannel::_q_encrypted()
state = QHttpNetworkConnectionChannel::IdleState;
pendingEncrypt = false;
- if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY) {
+ if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY ||
+ connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2) {
// we call setSpdyWasUsed(true) on the replies in the SPDY handler when the request is sent
- if (spdyRequestsToSend.count() > 0)
+ 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);
+ }
} else { // HTTP
if (!reply)
connection->d_func()->dequeueRequest(socket);
@@ -1092,19 +1127,6 @@ void QHttpNetworkConnectionChannel::requeueSpdyRequests()
spdyRequestsToSend.clear();
}
-void QHttpNetworkConnectionChannel::emitFinishedWithError(QNetworkReply::NetworkError error,
- const char *message)
-{
- if (reply)
- emit reply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message));
- QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values();
- for (int a = 0; a < spdyPairs.count(); ++a) {
- QHttpNetworkReply *currentReply = spdyPairs.at(a).second;
- Q_ASSERT(currentReply);
- emit currentReply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message));
- }
-}
-
void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
{
if (!socket)
diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h
index b3b52a7f42..61aea9d35d 100644
--- a/src/network/access/qhttpnetworkconnectionchannel_p.h
+++ b/src/network/access/qhttpnetworkconnectionchannel_p.h
@@ -50,6 +50,8 @@
//
// We mean it.
//
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <QtNetwork/qnetworkrequest.h>
#include <QtNetwork/qnetworkreply.h>
#include <QtNetwork/qabstractsocket.h>
@@ -119,18 +121,21 @@ public:
bool authenticationCredentialsSent;
bool proxyCredentialsSent;
QScopedPointer<QAbstractProtocolHandler> protocolHandler;
+ // SPDY or HTTP/2 requests; SPDY is TLS-only, but
+ // HTTP/2 can be cleartext also, that's why it's
+ // outside of QT_NO_SSL section. Sorted by priority:
+ QMultiMap<int, HttpMessagePair> spdyRequestsToSend;
#ifndef QT_NO_SSL
bool ignoreAllSslErrors;
QList<QSslError> ignoreSslErrorsList;
QSslConfiguration sslConfiguration;
- QMultiMap<int, HttpMessagePair> spdyRequestsToSend; // sorted by priority
void ignoreSslErrors();
void ignoreSslErrors(const QList<QSslError> &errors);
void setSslConfiguration(const QSslConfiguration &config);
void requeueSpdyRequests(); // when we wanted SPDY but got HTTP
+#endif
// to emit the signal for all in-flight replies:
void emitFinishedWithError(QNetworkReply::NetworkError error, const char *message);
-#endif
#ifndef QT_NO_BEARERMANAGEMENT
QSharedPointer<QNetworkSession> networkSession;
#endif
diff --git a/src/network/access/qhttpnetworkheader_p.h b/src/network/access/qhttpnetworkheader_p.h
index 3eaab587a0..89169b9331 100644
--- a/src/network/access/qhttpnetworkheader_p.h
+++ b/src/network/access/qhttpnetworkheader_p.h
@@ -50,6 +50,9 @@
//
// We mean it.
//
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
#ifndef QT_NO_HTTP
#include <qshareddata.h>
diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp
index 3601c36bc2..24ada3a81f 100644
--- a/src/network/access/qhttpnetworkreply.cpp
+++ b/src/network/access/qhttpnetworkreply.cpp
@@ -484,8 +484,7 @@ qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
}
// is this a valid reply?
- if (fragment.length() >= 5 && !fragment.startsWith("HTTP/"))
- {
+ if (fragment.length() == 5 && !fragment.startsWith("HTTP/")) {
fragment.clear();
return -1;
}
@@ -739,6 +738,8 @@ qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuff
#ifndef QT_NO_COMPRESS
int QHttpNetworkReplyPrivate::initializeInflateStream()
{
+ Q_ASSERT(inflateStrm);
+
inflateStrm->zalloc = Z_NULL;
inflateStrm->zfree = Z_NULL;
inflateStrm->opaque = Z_NULL;
diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h
index da39633dd1..f3b007f594 100644
--- a/src/network/access/qhttpnetworkreply_p.h
+++ b/src/network/access/qhttpnetworkreply_p.h
@@ -50,9 +50,13 @@
//
// We mean it.
//
-#include <qplatformdefs.h>
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
#ifndef QT_NO_HTTP
+#include <qplatformdefs.h>
+
#ifndef QT_NO_COMPRESS
struct z_stream_s;
#endif
@@ -176,6 +180,7 @@ private:
friend class QHttpNetworkConnection;
friend class QHttpNetworkConnectionPrivate;
friend class QHttpNetworkConnectionChannel;
+ friend class QHttp2ProtocolHandler;
friend class QHttpProtocolHandler;
friend class QSpdyProtocolHandler;
};
diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp
index 5d51e7fb1a..802043d847 100644
--- a/src/network/access/qhttpnetworkrequest.cpp
+++ b/src/network/access/qhttpnetworkrequest.cpp
@@ -47,7 +47,7 @@ QT_BEGIN_NAMESPACE
QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(QHttpNetworkRequest::Operation op,
QHttpNetworkRequest::Priority pri, const QUrl &newUrl)
: QHttpNetworkHeaderPrivate(newUrl), operation(op), priority(pri), uploadByteDevice(0),
- autoDecompress(false), pipeliningAllowed(false), spdyAllowed(false),
+ autoDecompress(false), pipeliningAllowed(false), spdyAllowed(false), http2Allowed(false),
withCredentials(true), preConnect(false), followRedirect(false), redirectCount(0)
{
}
@@ -61,6 +61,7 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest
autoDecompress(other.autoDecompress),
pipeliningAllowed(other.pipeliningAllowed),
spdyAllowed(other.spdyAllowed),
+ http2Allowed(other.http2Allowed),
withCredentials(other.withCredentials),
ssl(other.ssl),
preConnect(other.preConnect),
@@ -82,6 +83,7 @@ bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &ot
&& (autoDecompress == other.autoDecompress)
&& (pipeliningAllowed == other.pipeliningAllowed)
&& (spdyAllowed == other.spdyAllowed)
+ && (http2Allowed == other.http2Allowed)
// we do not clear the customVerb in setOperation
&& (operation != QHttpNetworkRequest::Custom || (customVerb == other.customVerb))
&& (withCredentials == other.withCredentials)
@@ -331,6 +333,16 @@ void QHttpNetworkRequest::setSPDYAllowed(bool b)
d->spdyAllowed = b;
}
+bool QHttpNetworkRequest::isHTTP2Allowed() const
+{
+ return d->http2Allowed;
+}
+
+void QHttpNetworkRequest::setHTTP2Allowed(bool b)
+{
+ d->http2Allowed = b;
+}
+
bool QHttpNetworkRequest::withCredentials() const
{
return d->withCredentials;
diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h
index d9540e6369..d1abb76e28 100644
--- a/src/network/access/qhttpnetworkrequest_p.h
+++ b/src/network/access/qhttpnetworkrequest_p.h
@@ -50,6 +50,8 @@
//
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
#ifndef QT_NO_HTTP
#include <private/qhttpnetworkheader_p.h>
@@ -115,6 +117,9 @@ public:
bool isSPDYAllowed() const;
void setSPDYAllowed(bool b);
+ bool isHTTP2Allowed() const;
+ void setHTTP2Allowed(bool b);
+
bool withCredentials() const;
void setWithCredentials(bool b);
@@ -142,6 +147,7 @@ private:
friend class QHttpNetworkConnectionPrivate;
friend class QHttpNetworkConnectionChannel;
friend class QHttpProtocolHandler;
+ friend class QHttp2ProtocolHandler;
friend class QSpdyProtocolHandler;
};
@@ -163,6 +169,7 @@ public:
bool autoDecompress;
bool pipeliningAllowed;
bool spdyAllowed;
+ bool http2Allowed;
bool withCredentials;
bool ssl;
bool preConnect;
diff --git a/src/network/access/qhttpprotocolhandler.cpp b/src/network/access/qhttpprotocolhandler.cpp
index b486b75449..37e8b9bed8 100644
--- a/src/network/access/qhttpprotocolhandler.cpp
+++ b/src/network/access/qhttpprotocolhandler.cpp
@@ -99,7 +99,7 @@ void QHttpProtocolHandler::_q_receiveReply()
switch (state) {
case QHttpNetworkReplyPrivate::NothingDoneState: {
m_reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState;
- // fallthrough
+ Q_FALLTHROUGH();
}
case QHttpNetworkReplyPrivate::ReadingStatusState: {
qint64 statusBytes = m_reply->d_func()->readStatus(m_socket);
@@ -213,7 +213,8 @@ void QHttpProtocolHandler::_q_receiveReply()
if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState)
break;
- // everything done, fall through
+ // everything done
+ Q_FALLTHROUGH();
}
case QHttpNetworkReplyPrivate::AllDoneState:
m_channel->allDone();
@@ -428,7 +429,7 @@ bool QHttpProtocolHandler::sendRequest()
}
case QHttpNetworkConnectionChannel::ReadingState:
// ignore _q_bytesWritten in these states
- // fall through
+ Q_FALLTHROUGH();
default:
break;
}
diff --git a/src/network/access/qhttpprotocolhandler_p.h b/src/network/access/qhttpprotocolhandler_p.h
index b13993778c..863b988be3 100644
--- a/src/network/access/qhttpprotocolhandler_p.h
+++ b/src/network/access/qhttpprotocolhandler_p.h
@@ -52,6 +52,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <private/qabstractprotocolhandler_p.h>
#ifndef QT_NO_HTTP
diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp
index 3adf71ccfb..1dca7f02fb 100644
--- a/src/network/access/qhttpthreaddelegate.cpp
+++ b/src/network/access/qhttpthreaddelegate.cpp
@@ -285,9 +285,16 @@ void QHttpThreadDelegate::startRequest()
urlCopy.setPort(urlCopy.port(ssl ? 443 : 80));
QHttpNetworkConnection::ConnectionType connectionType
- = QHttpNetworkConnection::ConnectionTypeHTTP;
+ = httpRequest.isHTTP2Allowed() ? QHttpNetworkConnection::ConnectionTypeHTTP2
+ : QHttpNetworkConnection::ConnectionTypeHTTP;
+
#ifndef QT_NO_SSL
- if (httpRequest.isSPDYAllowed() && ssl) {
+ if (httpRequest.isHTTP2Allowed() && ssl) {
+ QList<QByteArray> protocols;
+ protocols << QSslConfiguration::ALPNProtocolHTTP2
+ << QSslConfiguration::NextProtocolHttp1_1;
+ incomingSslConfiguration.setAllowedNextProtocols(protocols);
+ } else if (httpRequest.isSPDYAllowed() && ssl) {
connectionType = QHttpNetworkConnection::ConnectionTypeSPDY;
urlCopy.setScheme(QStringLiteral("spdy")); // to differentiate SPDY requests from HTTPS requests
QList<QByteArray> nextProtocols;
diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h
index cec125d7a5..64c58cf648 100644
--- a/src/network/access/qhttpthreaddelegate_p.h
+++ b/src/network/access/qhttpthreaddelegate_p.h
@@ -52,6 +52,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <QObject>
#include <QThreadStorage>
#include <QNetworkProxy>
diff --git a/src/network/access/qnetworkaccessauthenticationmanager_p.h b/src/network/access/qnetworkaccessauthenticationmanager_p.h
index 3d1cd6b4fb..548675728f 100644
--- a/src/network/access/qnetworkaccessauthenticationmanager_p.h
+++ b/src/network/access/qnetworkaccessauthenticationmanager_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qnetworkaccessmanager.h"
#include "qnetworkaccesscache_p.h"
#include "qnetworkaccessbackend_p.h"
diff --git a/src/network/access/qnetworkaccessbackend_p.h b/src/network/access/qnetworkaccessbackend_p.h
index 47f5872235..7f39c942a3 100644
--- a/src/network/access/qnetworkaccessbackend_p.h
+++ b/src/network/access/qnetworkaccessbackend_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qnetworkreplyimpl_p.h"
#include "QtCore/qobject.h"
diff --git a/src/network/access/qnetworkaccesscache_p.h b/src/network/access/qnetworkaccesscache_p.h
index 2337dd10af..3732b5cbb4 100644
--- a/src/network/access/qnetworkaccesscache_p.h
+++ b/src/network/access/qnetworkaccesscache_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "QtCore/qobject.h"
#include "QtCore/qbasictimer.h"
#include "QtCore/qbytearray.h"
diff --git a/src/network/access/qnetworkaccesscachebackend_p.h b/src/network/access/qnetworkaccesscachebackend_p.h
index c9b8c84579..8db1a6b1d5 100644
--- a/src/network/access/qnetworkaccesscachebackend_p.h
+++ b/src/network/access/qnetworkaccesscachebackend_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qnetworkaccessbackend_p.h"
#include "qnetworkrequest.h"
#include "qnetworkreply.h"
diff --git a/src/network/access/qnetworkaccessdebugpipebackend_p.h b/src/network/access/qnetworkaccessdebugpipebackend_p.h
index 6f8e248880..1d1af61dbd 100644
--- a/src/network/access/qnetworkaccessdebugpipebackend_p.h
+++ b/src/network/access/qnetworkaccessdebugpipebackend_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qnetworkaccessbackend_p.h"
#include "qnetworkrequest.h"
#include "qnetworkreply.h"
diff --git a/src/network/access/qnetworkaccessfilebackend_p.h b/src/network/access/qnetworkaccessfilebackend_p.h
index dec90062e4..081ff2b9e7 100644
--- a/src/network/access/qnetworkaccessfilebackend_p.h
+++ b/src/network/access/qnetworkaccessfilebackend_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qnetworkaccessbackend_p.h"
#include "qnetworkrequest.h"
#include "qnetworkreply.h"
diff --git a/src/network/access/qnetworkaccessftpbackend_p.h b/src/network/access/qnetworkaccessftpbackend_p.h
index c5beaf94e8..cdf6b7a07f 100644
--- a/src/network/access/qnetworkaccessftpbackend_p.h
+++ b/src/network/access/qnetworkaccessftpbackend_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qnetworkaccessbackend_p.h"
#include "qnetworkaccesscache_p.h"
#include "qnetworkrequest.h"
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index 587ab27a0f..246b9a7bad 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -1105,6 +1105,47 @@ QNetworkReply *QNetworkAccessManager::sendCustomRequest(const QNetworkRequest &r
}
/*!
+ \since 5.8
+
+ \overload
+
+ Sends the contents of the \a data byte array to the destination
+ specified by \a request.
+*/
+QNetworkReply *QNetworkAccessManager::sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, const QByteArray &data)
+{
+ QBuffer *buffer = new QBuffer;
+ buffer->setData(data);
+ buffer->open(QIODevice::ReadOnly);
+
+ QNetworkReply *reply = sendCustomRequest(request, verb, buffer);
+ buffer->setParent(reply);
+ return reply;
+}
+
+/*!
+ \since 5.8
+
+ \overload
+
+ Sends a custom request to the server identified by the URL of \a request.
+
+ Sends the contents of the \a multiPart message to the destination
+ specified by \a request.
+
+ This can be used for sending MIME multipart messages for custom verbs.
+
+ \sa QHttpMultiPart, QHttpPart, put()
+*/
+QNetworkReply *QNetworkAccessManager::sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QHttpMultiPart *multiPart)
+{
+ QNetworkRequest newRequest = d_func()->prepareMultipart(request, multiPart);
+ QIODevice *device = multiPart->d_func()->device;
+ QNetworkReply *reply = sendCustomRequest(newRequest, verb, device);
+ return reply;
+}
+
+/*!
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
@@ -1516,27 +1557,35 @@ void QNetworkAccessManagerPrivate::clearCache(QNetworkAccessManager *manager)
manager->d_func()->objectCache.clear();
manager->d_func()->authenticationManager->clearCache();
- if (manager->d_func()->httpThread) {
- manager->d_func()->httpThread->quit();
- manager->d_func()->httpThread->wait(5000);
- if (manager->d_func()->httpThread->isFinished())
- delete manager->d_func()->httpThread;
- else
- QObject::connect(manager->d_func()->httpThread, SIGNAL(finished()), manager->d_func()->httpThread, SLOT(deleteLater()));
- manager->d_func()->httpThread = 0;
- }
+ manager->d_func()->destroyThread();
}
QNetworkAccessManagerPrivate::~QNetworkAccessManagerPrivate()
{
- if (httpThread) {
- httpThread->quit();
- httpThread->wait(5000);
- if (httpThread->isFinished())
- delete httpThread;
+ destroyThread();
+}
+
+QThread * QNetworkAccessManagerPrivate::createThread()
+{
+ if (!thread) {
+ thread = new QThread;
+ thread->setObjectName(QStringLiteral("QNetworkAccessManager thread"));
+ thread->start();
+ }
+ Q_ASSERT(thread);
+ return thread;
+}
+
+void QNetworkAccessManagerPrivate::destroyThread()
+{
+ if (thread) {
+ thread->quit();
+ thread->wait(5000);
+ if (thread->isFinished())
+ delete thread;
else
- QObject::connect(httpThread, SIGNAL(finished()), httpThread, SLOT(deleteLater()));
- httpThread = 0;
+ QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
+ thread = 0;
}
}
diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h
index cba5cc09d6..4b8c4ddf0e 100644
--- a/src/network/access/qnetworkaccessmanager.h
+++ b/src/network/access/qnetworkaccessmanager.h
@@ -40,6 +40,7 @@
#ifndef QNETWORKACCESSMANAGER_H
#define QNETWORKACCESSMANAGER_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/QObject>
#ifndef QT_NO_SSL
#include <QtNetwork/QSslConfiguration>
@@ -128,6 +129,8 @@ public:
QNetworkReply *put(const QNetworkRequest &request, QHttpMultiPart *multiPart);
QNetworkReply *deleteResource(const QNetworkRequest &request);
QNetworkReply *sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QIODevice *data = Q_NULLPTR);
+ QNetworkReply *sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, const QByteArray &data);
+ QNetworkReply *sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QHttpMultiPart *multiPart);
#ifndef QT_NO_BEARERMANAGEMENT
void setConfiguration(const QNetworkConfiguration &config);
@@ -173,6 +176,7 @@ private:
friend class QNetworkReplyImplPrivate;
friend class QNetworkReplyHttpImpl;
friend class QNetworkReplyHttpImplPrivate;
+ friend class QNetworkReplyFileImpl;
Q_DECLARE_PRIVATE(QNetworkAccessManager)
Q_PRIVATE_SLOT(d_func(), void _q_replyFinished())
diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h
index 44cf253368..bb4641ab8b 100644
--- a/src/network/access/qnetworkaccessmanager_p.h
+++ b/src/network/access/qnetworkaccessmanager_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qnetworkaccessmanager.h"
#include "qnetworkaccesscache_p.h"
#include "qnetworkaccessbackend_p.h"
@@ -74,7 +75,7 @@ class QNetworkAccessManagerPrivate: public QObjectPrivate
public:
QNetworkAccessManagerPrivate()
: networkCache(0), cookieJar(0),
- httpThread(0),
+ thread(0),
#ifndef QT_NO_NETWORKPROXY
proxyFactory(0),
#endif
@@ -107,6 +108,9 @@ public:
}
~QNetworkAccessManagerPrivate();
+ QThread * createThread();
+ void destroyThread();
+
void _q_replyFinished();
void _q_replyEncrypted();
void _q_replySslErrors(const QList<QSslError> &errors);
@@ -163,7 +167,7 @@ public:
QNetworkCookieJar *cookieJar;
- QThread *httpThread;
+ QThread *thread;
#ifndef QT_NO_NETWORKPROXY
diff --git a/src/network/access/qnetworkcookie.cpp b/src/network/access/qnetworkcookie.cpp
index 21dc12829a..b7cf989477 100644
--- a/src/network/access/qnetworkcookie.cpp
+++ b/src/network/access/qnetworkcookie.cpp
@@ -644,7 +644,7 @@ static QDateTime parseDateString(const QByteArray &dateString)
switch (end - 1) {
case 4:
minutes = atoi(dateString.mid(at + 3, 2).constData());
- // fall through
+ Q_FALLTHROUGH();
case 2:
hours = atoi(dateString.mid(at + 1, 2).constData());
break;
@@ -970,7 +970,7 @@ QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByt
if (ok) {
if (secs <= 0) {
//earliest representable time (RFC6265 section 5.2.2)
- cookie.setExpirationDate(QDateTime::fromTime_t(0));
+ cookie.setExpirationDate(QDateTime::fromSecsSinceEpoch(0));
} else {
cookie.setExpirationDate(now.addSecs(secs));
}
diff --git a/src/network/access/qnetworkcookie.h b/src/network/access/qnetworkcookie.h
index 8f4b26465a..e462b98555 100644
--- a/src/network/access/qnetworkcookie.h
+++ b/src/network/access/qnetworkcookie.h
@@ -40,6 +40,7 @@
#ifndef QNETWORKCOOKIE_H
#define QNETWORKCOOKIE_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/QSharedDataPointer>
#include <QtCore/QList>
#include <QtCore/QMetaType>
diff --git a/src/network/access/qnetworkcookie_p.h b/src/network/access/qnetworkcookie_p.h
index 61f04e7177..13538ad243 100644
--- a/src/network/access/qnetworkcookie_p.h
+++ b/src/network/access/qnetworkcookie_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "QtCore/qdatetime.h"
QT_BEGIN_NAMESPACE
diff --git a/src/network/access/qnetworkcookiejar.h b/src/network/access/qnetworkcookiejar.h
index d6370cbc05..f9c1549e20 100644
--- a/src/network/access/qnetworkcookiejar.h
+++ b/src/network/access/qnetworkcookiejar.h
@@ -40,6 +40,7 @@
#ifndef QNETWORKCOOKIEJAR_H
#define QNETWORKCOOKIEJAR_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/QObject>
#include <QtCore/QUrl>
diff --git a/src/network/access/qnetworkcookiejar_p.h b/src/network/access/qnetworkcookiejar_p.h
index b6f4eb2bc0..43f189a40c 100644
--- a/src/network/access/qnetworkcookiejar_p.h
+++ b/src/network/access/qnetworkcookiejar_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "private/qobject_p.h"
#include "qnetworkcookie.h"
diff --git a/src/network/access/qnetworkdiskcache.cpp b/src/network/access/qnetworkdiskcache.cpp
index 077826ccf6..ce3b773c64 100644
--- a/src/network/access/qnetworkdiskcache.cpp
+++ b/src/network/access/qnetworkdiskcache.cpp
@@ -422,7 +422,7 @@ QIODevice *QNetworkDiskCache::data(const QUrl &url)
// ### verify that QFile uses the fd size and not the file name
qint64 size = file->size() - file->pos();
const uchar *p = 0;
-#if !defined(Q_OS_WINCE) && !defined(Q_OS_INTEGRITY)
+#if !defined(Q_OS_INTEGRITY)
p = file->map(file->pos(), size);
#endif
if (p) {
diff --git a/src/network/access/qnetworkdiskcache.h b/src/network/access/qnetworkdiskcache.h
index ad8b83a5f3..a3aa8d3a07 100644
--- a/src/network/access/qnetworkdiskcache.h
+++ b/src/network/access/qnetworkdiskcache.h
@@ -40,6 +40,7 @@
#ifndef QNETWORKDISKCACHE_H
#define QNETWORKDISKCACHE_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtNetwork/qabstractnetworkcache.h>
QT_BEGIN_NAMESPACE
diff --git a/src/network/access/qnetworkdiskcache_p.h b/src/network/access/qnetworkdiskcache_p.h
index 9e035863dd..e47b93b09d 100644
--- a/src/network/access/qnetworkdiskcache_p.h
+++ b/src/network/access/qnetworkdiskcache_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "private/qabstractnetworkcache_p.h"
#include <qbuffer.h>
diff --git a/src/network/access/qnetworkfile.cpp b/src/network/access/qnetworkfile.cpp
new file mode 100644
index 0000000000..374dd26e2e
--- /dev/null
+++ b/src/network/access/qnetworkfile.cpp
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 "qnetworkfile_p.h"
+
+#include <QtCore/QDebug>
+#include <QNetworkReply>
+#include <QtCore/QDateTime>
+#include <QtCore/QFileInfo>
+#include <QtCore/QMetaObject>
+#include <QtCore/QCoreApplication>
+
+QT_BEGIN_NAMESPACE
+
+QNetworkFile::QNetworkFile()
+ : QFile()
+{
+}
+
+QNetworkFile::QNetworkFile(const QString &name)
+ : QFile(name)
+{
+}
+
+void QNetworkFile::open()
+{
+ bool opened = false;
+ QFileInfo fi(fileName());
+ if (fi.isDir()) {
+ QString msg = QCoreApplication::translate("QNetworkAccessFileBackend",
+ "Cannot open %1: Path is a directory").arg(fileName());
+ error(QNetworkReply::ContentOperationNotPermittedError, msg);
+ } else {
+ headerRead(QNetworkRequest::LastModifiedHeader, QVariant::fromValue(fi.lastModified()));
+ headerRead(QNetworkRequest::ContentLengthHeader, QVariant::fromValue(fi.size()));
+ opened = QFile::open(QIODevice::ReadOnly | QIODevice::Unbuffered);
+ if (!opened) {
+ QString msg = QCoreApplication::translate("QNetworkAccessFileBackend",
+ "Error opening %1: %2").arg(fileName(), errorString());
+ if (exists())
+ error(QNetworkReply::ContentAccessDenied, msg);
+ else
+ error(QNetworkReply::ContentNotFoundError, msg);
+ }
+ }
+ finished(opened);
+}
+
+void QNetworkFile::close()
+{
+ // This override is needed because 'using' keyword cannot be used for slots. And the base
+ // function is not an invokable/slot function.
+ QFile::close();
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkfile_p.h b/src/network/access/qnetworkfile_p.h
new file mode 100644
index 0000000000..dd56b24bd8
--- /dev/null
+++ b/src/network/access/qnetworkfile_p.h
@@ -0,0 +1,80 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 QNETWORKFILE_H
+#define QNETWORKFILE_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+#include <QFile>
+#include <qnetworkreply.h>
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkFile : public QFile
+{
+ Q_OBJECT
+public:
+ QNetworkFile();
+ QNetworkFile(const QString &name);
+ using QFile::open;
+
+public Q_SLOTS:
+ void open();
+ void close() Q_DECL_OVERRIDE;
+
+Q_SIGNALS:
+ void finished(bool ok);
+ void headerRead(QNetworkRequest::KnownHeaders header, const QVariant &value);
+ void error(QNetworkReply::NetworkError error, const QString &message);
+};
+
+QT_END_NAMESPACE
+
+#endif // QNETWORKFILE_H
diff --git a/src/network/access/qnetworkreply.h b/src/network/access/qnetworkreply.h
index 9aaf06a803..1419db8597 100644
--- a/src/network/access/qnetworkreply.h
+++ b/src/network/access/qnetworkreply.h
@@ -40,6 +40,7 @@
#ifndef QNETWORKREPLY_H
#define QNETWORKREPLY_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/QIODevice>
#include <QtCore/QString>
#include <QtCore/QVariant>
diff --git a/src/network/access/qnetworkreply_p.h b/src/network/access/qnetworkreply_p.h
index 420b862b17..66d8c9d527 100644
--- a/src/network/access/qnetworkreply_p.h
+++ b/src/network/access/qnetworkreply_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qnetworkrequest.h"
#include "qnetworkrequest_p.h"
#include "qnetworkreply.h"
diff --git a/src/network/access/qnetworkreplydataimpl_p.h b/src/network/access/qnetworkreplydataimpl_p.h
index 60d3f583cc..c8b44d7539 100644
--- a/src/network/access/qnetworkreplydataimpl_p.h
+++ b/src/network/access/qnetworkreplydataimpl_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qnetworkreply.h"
#include "qnetworkreply_p.h"
#include "qnetworkaccessmanager.h"
diff --git a/src/network/access/qnetworkreplyfileimpl.cpp b/src/network/access/qnetworkreplyfileimpl.cpp
index 36bc4b41df..ef319ebf0d 100644
--- a/src/network/access/qnetworkreplyfileimpl.cpp
+++ b/src/network/access/qnetworkreplyfileimpl.cpp
@@ -40,31 +40,45 @@
#include "qnetworkreplyfileimpl_p.h"
#include "QtCore/qdatetime.h"
+#include "qnetworkaccessmanager_p.h"
#include <QtCore/QCoreApplication>
#include <QtCore/QFileInfo>
+#include <QtCore/QThread>
+#include "qnetworkfile_p.h"
+#include "qnetworkrequest.h"
QT_BEGIN_NAMESPACE
QNetworkReplyFileImplPrivate::QNetworkReplyFileImplPrivate()
- : QNetworkReplyPrivate(), realFileSize(0)
+ : QNetworkReplyPrivate(), managerPrivate(0), realFile(0)
{
+ qRegisterMetaType<QNetworkRequest::KnownHeaders>();
+ qRegisterMetaType<QNetworkReply::NetworkError>();
}
QNetworkReplyFileImpl::~QNetworkReplyFileImpl()
{
+ QNetworkReplyFileImplPrivate *d = (QNetworkReplyFileImplPrivate*) d_func();
+ if (d->realFile) {
+ if (d->realFile->thread() == QThread::currentThread())
+ delete d->realFile;
+ else
+ QMetaObject::invokeMethod(d->realFile, "deleteLater", Qt::QueuedConnection);
+ }
}
-QNetworkReplyFileImpl::QNetworkReplyFileImpl(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op)
- : QNetworkReply(*new QNetworkReplyFileImplPrivate(), parent)
+QNetworkReplyFileImpl::QNetworkReplyFileImpl(QNetworkAccessManager *manager, const QNetworkRequest &req, const QNetworkAccessManager::Operation op)
+ : QNetworkReply(*new QNetworkReplyFileImplPrivate(), manager)
{
setRequest(req);
setUrl(req.url());
setOperation(op);
- setFinished(true);
QNetworkReply::open(QIODevice::ReadOnly);
QNetworkReplyFileImplPrivate *d = (QNetworkReplyFileImplPrivate*) d_func();
+ d->managerPrivate = manager->d_func();
+
QUrl url = req.url();
if (url.host() == QLatin1String("localhost"))
url.setHost(QString());
@@ -77,7 +91,7 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QObject *parent, const QNetworkRequ
setError(QNetworkReply::ProtocolInvalidOperationError, msg);
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProtocolInvalidOperationError));
- QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
+ fileOpenFinished(false);
return;
}
#endif
@@ -85,7 +99,6 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QObject *parent, const QNetworkRequ
url.setPath(QLatin1String("/"));
setUrl(url);
-
QString fileName = url.toLocalFile();
if (fileName.isEmpty()) {
const QString scheme = url.scheme();
@@ -101,68 +114,85 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QObject *parent, const QNetworkRequ
}
}
- QFileInfo fi(fileName);
- if (fi.isDir()) {
- QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Cannot open %1: Path is a directory").arg(url.toString());
- setError(QNetworkReply::ContentOperationNotPermittedError, msg);
- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
- Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentOperationNotPermittedError));
- QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
- return;
- }
-
- d->realFile.setFileName(fileName);
- bool opened = d->realFile.open(QIODevice::ReadOnly | QIODevice::Unbuffered);
-
- // could we open the file?
- if (!opened) {
- QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Error opening %1: %2")
- .arg(d->realFile.fileName(), d->realFile.errorString());
-
- if (d->realFile.exists()) {
- setError(QNetworkReply::ContentAccessDenied, msg);
- QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
- Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentAccessDenied));
- } else {
- setError(QNetworkReply::ContentNotFoundError, msg);
+ if (req.attribute(QNetworkRequest::BackgroundRequestAttribute).toBool()) { // Asynchronous open
+ auto realFile = new QNetworkFile(fileName);
+ connect(realFile, &QNetworkFile::headerRead, this, &QNetworkReplyFileImpl::setHeader,
+ Qt::QueuedConnection);
+ connect(realFile, &QNetworkFile::error, this, &QNetworkReplyFileImpl::setError,
+ Qt::QueuedConnection);
+ connect(realFile, SIGNAL(finished(bool)), SLOT(fileOpenFinished(bool)),
+ Qt::QueuedConnection);
+
+ realFile->moveToThread(d->managerPrivate->createThread());
+ QMetaObject::invokeMethod(realFile, "open", Qt::QueuedConnection);
+
+ d->realFile = realFile;
+ } else { // Synch open
+ setFinished(true);
+
+ QFileInfo fi(fileName);
+ if (fi.isDir()) {
+ QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Cannot open %1: Path is a directory").arg(url.toString());
+ setError(QNetworkReply::ContentOperationNotPermittedError, msg);
QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
- Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentNotFoundError));
+ Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentOperationNotPermittedError));
+ QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
+ return;
+ }
+ d->realFile = new QFile(fileName, this);
+ bool opened = d->realFile->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
+
+ // could we open the file?
+ if (!opened) {
+ QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Error opening %1: %2")
+ .arg(d->realFile->fileName(), d->realFile->errorString());
+
+ if (fi.exists()) {
+ setError(QNetworkReply::ContentAccessDenied, msg);
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentAccessDenied));
+ } else {
+ setError(QNetworkReply::ContentNotFoundError, msg);
+ QMetaObject::invokeMethod(this, "error", Qt::QueuedConnection,
+ Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ContentNotFoundError));
+ }
+ QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
+ return;
}
+ setHeader(QNetworkRequest::LastModifiedHeader, fi.lastModified());
+ setHeader(QNetworkRequest::ContentLengthHeader, fi.size());
+
+ QMetaObject::invokeMethod(this, "metaDataChanged", Qt::QueuedConnection);
+ QMetaObject::invokeMethod(this, "downloadProgress", Qt::QueuedConnection,
+ Q_ARG(qint64, fi.size()), Q_ARG(qint64, fi.size()));
+ QMetaObject::invokeMethod(this, "readyRead", Qt::QueuedConnection);
QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
- return;
}
-
- setHeader(QNetworkRequest::LastModifiedHeader, fi.lastModified());
- d->realFileSize = fi.size();
- setHeader(QNetworkRequest::ContentLengthHeader, d->realFileSize);
-
- QMetaObject::invokeMethod(this, "metaDataChanged", Qt::QueuedConnection);
- QMetaObject::invokeMethod(this, "downloadProgress", Qt::QueuedConnection,
- Q_ARG(qint64, d->realFileSize), Q_ARG(qint64, d->realFileSize));
- QMetaObject::invokeMethod(this, "readyRead", Qt::QueuedConnection);
- QMetaObject::invokeMethod(this, "finished", Qt::QueuedConnection);
}
void QNetworkReplyFileImpl::close()
{
Q_D(QNetworkReplyFileImpl);
QNetworkReply::close();
- d->realFile.close();
+ if (d->realFile) {
+ if (d->realFile->thread() == thread())
+ d->realFile->close();
+ else
+ QMetaObject::invokeMethod(d->realFile, "close", Qt::QueuedConnection);
+ }
}
void QNetworkReplyFileImpl::abort()
{
- Q_D(QNetworkReplyFileImpl);
- QNetworkReply::close();
- d->realFile.close();
+ close();
}
qint64 QNetworkReplyFileImpl::bytesAvailable() const
{
Q_D(const QNetworkReplyFileImpl);
- if (!d->realFile.isOpen())
+ if (!d->isFinished || !d->realFile || !d->realFile->isOpen())
return QNetworkReply::bytesAvailable();
- return QNetworkReply::bytesAvailable() + d->realFile.bytesAvailable();
+ return QNetworkReply::bytesAvailable() + d->realFile->bytesAvailable();
}
bool QNetworkReplyFileImpl::isSequential () const
@@ -172,8 +202,9 @@ bool QNetworkReplyFileImpl::isSequential () const
qint64 QNetworkReplyFileImpl::size() const
{
- Q_D(const QNetworkReplyFileImpl);
- return d->realFileSize;
+ bool ok;
+ int size = header(QNetworkRequest::ContentLengthHeader).toInt(&ok);
+ return ok ? size : 0;
}
/*!
@@ -182,11 +213,11 @@ qint64 QNetworkReplyFileImpl::size() const
qint64 QNetworkReplyFileImpl::readData(char *data, qint64 maxlen)
{
Q_D(QNetworkReplyFileImpl);
- if (!d->realFile.isOpen())
+ if (!d->isFinished || !d->realFile || !d->realFile->isOpen())
return -1;
- qint64 ret = d->realFile.read(data, maxlen);
- if (bytesAvailable() == 0 && d->realFile.isOpen())
- d->realFile.close();
+ qint64 ret = d->realFile->read(data, maxlen);
+ if (bytesAvailable() == 0)
+ d->realFile->close();
if (ret == 0 && bytesAvailable() == 0)
return -1;
else {
@@ -196,6 +227,17 @@ qint64 QNetworkReplyFileImpl::readData(char *data, qint64 maxlen)
}
}
+void QNetworkReplyFileImpl::fileOpenFinished(bool isOpen)
+{
+ setFinished(true);
+ if (isOpen) {
+ const auto fileSize = size();
+ Q_EMIT metaDataChanged();
+ Q_EMIT downloadProgress(fileSize, fileSize);
+ Q_EMIT readyRead();
+ }
+ Q_EMIT finished();
+}
QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkreplyfileimpl_p.h b/src/network/access/qnetworkreplyfileimpl_p.h
index bac00881a8..d6af66152e 100644
--- a/src/network/access/qnetworkreplyfileimpl_p.h
+++ b/src/network/access/qnetworkreplyfileimpl_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qnetworkreply.h"
#include "qnetworkreply_p.h"
#include "qnetworkaccessmanager.h"
@@ -59,13 +60,12 @@
QT_BEGIN_NAMESPACE
-
class QNetworkReplyFileImplPrivate;
class QNetworkReplyFileImpl: public QNetworkReply
{
Q_OBJECT
public:
- QNetworkReplyFileImpl(QObject *parent, const QNetworkRequest &req, const QNetworkAccessManager::Operation op);
+ QNetworkReplyFileImpl(QNetworkAccessManager *manager, const QNetworkRequest &req, const QNetworkAccessManager::Operation op);
~QNetworkReplyFileImpl();
virtual void abort() Q_DECL_OVERRIDE;
@@ -77,6 +77,9 @@ public:
virtual qint64 readData(char *data, qint64 maxlen) Q_DECL_OVERRIDE;
+private Q_SLOTS:
+ void fileOpenFinished(bool isOpen);
+
Q_DECLARE_PRIVATE(QNetworkReplyFileImpl)
};
@@ -85,12 +88,14 @@ class QNetworkReplyFileImplPrivate: public QNetworkReplyPrivate
public:
QNetworkReplyFileImplPrivate();
- QFile realFile;
- qint64 realFileSize;
+ QNetworkAccessManagerPrivate *managerPrivate;
+ QPointer<QFile> realFile;
Q_DECLARE_PUBLIC(QNetworkReplyFileImpl)
};
QT_END_NAMESPACE
+Q_DECLARE_METATYPE(QNetworkRequest::KnownHeaders)
+
#endif // QNETWORKREPLYFILEIMPL_H
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
index 6bf3c56db2..b5e44fa29a 100644
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
@@ -522,36 +522,36 @@ bool QNetworkReplyHttpImplPrivate::loadFromCacheIfAllowed(QHttpNetworkRequest &h
* now
* is the current (local) time
*/
- int age_value = 0;
+ qint64 age_value = 0;
it = cacheHeaders.findRawHeader("age");
if (it != cacheHeaders.rawHeaders.constEnd())
- age_value = it->second.toInt();
+ age_value = it->second.toLongLong();
QDateTime dateHeader;
- int date_value = 0;
+ qint64 date_value = 0;
it = cacheHeaders.findRawHeader("date");
if (it != cacheHeaders.rawHeaders.constEnd()) {
dateHeader = QNetworkHeadersPrivate::fromHttpDate(it->second);
- date_value = dateHeader.toTime_t();
+ date_value = dateHeader.toSecsSinceEpoch();
}
- int now = currentDateTime.toTime_t();
- int request_time = now;
- int response_time = now;
+ qint64 now = currentDateTime.toSecsSinceEpoch();
+ qint64 request_time = now;
+ qint64 response_time = now;
// Algorithm from RFC 2616 section 13.2.3
- int apparent_age = qMax(0, response_time - date_value);
- int corrected_received_age = qMax(apparent_age, age_value);
- int response_delay = response_time - request_time;
- int corrected_initial_age = corrected_received_age + response_delay;
- int resident_time = now - response_time;
- int current_age = corrected_initial_age + resident_time;
+ qint64 apparent_age = qMax<qint64>(0, response_time - date_value);
+ qint64 corrected_received_age = qMax(apparent_age, age_value);
+ qint64 response_delay = response_time - request_time;
+ qint64 corrected_initial_age = corrected_received_age + response_delay;
+ qint64 resident_time = now - response_time;
+ qint64 current_age = corrected_initial_age + resident_time;
- int freshness_lifetime = 0;
+ qint64 freshness_lifetime = 0;
// RFC 2616 13.2.4 Expiration Calculations
if (lastModified.isValid() && dateHeader.isValid()) {
- int diff = lastModified.secsTo(dateHeader);
+ qint64 diff = lastModified.secsTo(dateHeader);
freshness_lifetime = diff / 10;
if (httpRequest.headerField("Warning").isEmpty()) {
QDateTime dt = currentDateTime.addSecs(current_age);
@@ -601,17 +601,10 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
thread->setObjectName(QStringLiteral("Qt HTTP synchronous thread"));
QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
- } else if (!managerPrivate->httpThread) {
+ } else {
// We use the manager-global thread.
// At some point we could switch to having multiple threads if it makes sense.
- managerPrivate->httpThread = new QThread();
- managerPrivate->httpThread->setObjectName(QStringLiteral("Qt HTTP thread"));
- managerPrivate->httpThread->start();
-
- thread = managerPrivate->httpThread;
- } else {
- // Asynchronous request, thread already exists
- thread = managerPrivate->httpThread;
+ thread = managerPrivate->createThread();
}
QUrl url = newHttpRequest.url();
@@ -745,6 +738,9 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
if (request.attribute(QNetworkRequest::SpdyAllowedAttribute).toBool())
httpRequest.setSPDYAllowed(true);
+ if (request.attribute(QNetworkRequest::HTTP2AllowedAttribute).toBool())
+ httpRequest.setHTTP2Allowed(true);
+
if (static_cast<QNetworkRequest::LoadControl>
(newHttpRequest.attribute(QNetworkRequest::AuthenticationReuseAttribute,
QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Manual)
@@ -1123,8 +1119,8 @@ void QNetworkReplyHttpImplPrivate::onRedirected(const QUrl &redirectUrl, int htt
cookedHeaders.clear();
- if (managerPrivate->httpThread)
- managerPrivate->httpThread->disconnect();
+ if (managerPrivate->thread)
+ managerPrivate->thread->disconnect();
// Recurse
QMetaObject::invokeMethod(q, "start", Qt::QueuedConnection,
diff --git a/src/network/access/qnetworkreplyhttpimpl_p.h b/src/network/access/qnetworkreplyhttpimpl_p.h
index 4aba915c7d..868fa617b6 100644
--- a/src/network/access/qnetworkreplyhttpimpl_p.h
+++ b/src/network/access/qnetworkreplyhttpimpl_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qnetworkrequest.h"
#include "qnetworkreply.h"
diff --git a/src/network/access/qnetworkreplyimpl_p.h b/src/network/access/qnetworkreplyimpl_p.h
index 054cbcc3a7..7cd99392d3 100644
--- a/src/network/access/qnetworkreplyimpl_p.h
+++ b/src/network/access/qnetworkreplyimpl_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qnetworkreply.h"
#include "qnetworkreply_p.h"
#include "qnetworkaccessmanager.h"
diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp
index 63332d4fd1..29362b81e2 100644
--- a/src/network/access/qnetworkrequest.cpp
+++ b/src/network/access/qnetworkrequest.cpp
@@ -260,6 +260,14 @@ QT_BEGIN_NAMESPACE
Indicates whether SPDY was used for receiving
this reply.
+ \value HTTP2AllowedAttribute
+ Requests only, type: QMetaType::Bool (default: false)
+ Indicates whether the QNetworkAccessManager code is
+ allowed to use HTTP/2 with this request. This applies
+ to SSL requests or 'cleartext' HTTP/2.
+
+ \omitvalue HTTP2WasUsedAttribute
+
\value EmitAllUploadProgressSignalsAttribute
Requests only, type: QMetaType::Bool (default: false)
Indicates whether all upload signals should be emitted.
diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h
index 4a90a50e53..ad8f5bddd9 100644
--- a/src/network/access/qnetworkrequest.h
+++ b/src/network/access/qnetworkrequest.h
@@ -40,6 +40,7 @@
#ifndef QNETWORKREQUEST_H
#define QNETWORKREQUEST_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/QSharedDataPointer>
#include <QtCore/QString>
#include <QtCore/QUrl>
@@ -88,6 +89,8 @@ public:
SpdyWasUsedAttribute,
EmitAllUploadProgressSignalsAttribute,
FollowRedirectsAttribute,
+ HTTP2AllowedAttribute,
+ HTTP2WasUsedAttribute,
User = 1000,
UserMax = 32767
diff --git a/src/network/access/qnetworkrequest_p.h b/src/network/access/qnetworkrequest_p.h
index de6941c476..5e18da6d55 100644
--- a/src/network/access/qnetworkrequest_p.h
+++ b/src/network/access/qnetworkrequest_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qnetworkrequest.h"
#include "QtCore/qbytearray.h"
#include "QtCore/qlist.h"
diff --git a/src/network/access/qspdyprotocolhandler.cpp b/src/network/access/qspdyprotocolhandler.cpp
index 413b03cc22..445a2a1c29 100644
--- a/src/network/access/qspdyprotocolhandler.cpp
+++ b/src/network/access/qspdyprotocolhandler.cpp
@@ -458,7 +458,7 @@ bool QSpdyProtocolHandler::uncompressHeader(const QByteArray &input, QByteArray
break;
}
default: {
- qWarning() << "got unexpected zlib return value:" << zlibRet;
+ qWarning("got unexpected zlib return value: %d", zlibRet);
return false;
}
}
@@ -849,7 +849,7 @@ void QSpdyProtocolHandler::handleControlFrame(const QByteArray &frameHeaders) //
break;
}
default:
- qWarning() << "cannot handle frame of type" << type;
+ qWarning("cannot handle frame of type %d", int(type));
}
}
@@ -1073,7 +1073,7 @@ void QSpdyProtocolHandler::handleSETTINGS(char flags, quint32 /*length*/, const
break;
}
default:
- qWarning() << "found unknown settings value" << value;
+ qWarning("found unknown settings value %u", uint(value));
}
}
}
@@ -1112,7 +1112,7 @@ void QSpdyProtocolHandler::handleGOAWAY(char /*flags*/, quint32 /*length*/,
break;
}
default:
- qWarning() << "unexpected status code" << statusCode;
+ qWarning("unexpected status code %d", int(statusCode));
errorCode = QNetworkReply::ProtocolUnknownError;
}
diff --git a/src/network/access/qspdyprotocolhandler_p.h b/src/network/access/qspdyprotocolhandler_p.h
index aba081b9d1..0a18505b23 100644
--- a/src/network/access/qspdyprotocolhandler_p.h
+++ b/src/network/access/qspdyprotocolhandler_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <private/qabstractprotocolhandler_p.h>
#include <QtNetwork/qnetworkreply.h>
#include <private/qbytedata_p.h>
diff --git a/src/network/bearer/qbearerengine_p.h b/src/network/bearer/qbearerengine_p.h
index b36fedb741..5fc2578a78 100644
--- a/src/network/bearer/qbearerengine_p.h
+++ b/src/network/bearer/qbearerengine_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qnetworkconfiguration_p.h"
#include "qnetworksession.h"
#include "qnetworkconfigmanager.h"
diff --git a/src/network/bearer/qbearerplugin_p.h b/src/network/bearer/qbearerplugin_p.h
index caaa71ca1f..0cdde3c06c 100644
--- a/src/network/bearer/qbearerplugin_p.h
+++ b/src/network/bearer/qbearerplugin_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qbearerengine_p.h"
#include <QtCore/qplugin.h>
diff --git a/src/network/bearer/qnetworkconfigmanager.h b/src/network/bearer/qnetworkconfigmanager.h
index 4d841d8e33..da248bc7d0 100644
--- a/src/network/bearer/qnetworkconfigmanager.h
+++ b/src/network/bearer/qnetworkconfigmanager.h
@@ -40,6 +40,7 @@
#ifndef QNETWORKCONFIGMANAGER_H
#define QNETWORKCONFIGMANAGER_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qobject.h>
#include <QtNetwork/qnetworkconfiguration.h>
diff --git a/src/network/bearer/qnetworkconfigmanager_p.h b/src/network/bearer/qnetworkconfigmanager_p.h
index a322fb5cfe..a804e037a3 100644
--- a/src/network/bearer/qnetworkconfigmanager_p.h
+++ b/src/network/bearer/qnetworkconfigmanager_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qnetworkconfigmanager.h"
#include "qnetworkconfiguration_p.h"
diff --git a/src/network/bearer/qnetworkconfiguration.h b/src/network/bearer/qnetworkconfiguration.h
index 45f7402992..208f9f4692 100644
--- a/src/network/bearer/qnetworkconfiguration.h
+++ b/src/network/bearer/qnetworkconfiguration.h
@@ -40,7 +40,7 @@
#ifndef QNETWORKCONFIGURATION_H
#define QNETWORKCONFIGURATION_H
-# include <QtCore/qglobal.h>
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qshareddata.h>
#include <QtCore/qstring.h>
diff --git a/src/network/bearer/qnetworkconfiguration_p.h b/src/network/bearer/qnetworkconfiguration_p.h
index 316000c717..12d9676b59 100644
--- a/src/network/bearer/qnetworkconfiguration_p.h
+++ b/src/network/bearer/qnetworkconfiguration_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qnetworkconfiguration.h"
#include <QtCore/qshareddata.h>
diff --git a/src/network/bearer/qnetworksession.h b/src/network/bearer/qnetworksession.h
index 2ff1d89564..d96b8915eb 100644
--- a/src/network/bearer/qnetworksession.h
+++ b/src/network/bearer/qnetworksession.h
@@ -40,6 +40,7 @@
#ifndef QNETWORKSESSION_H
#define QNETWORKSESSION_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qobject.h>
#include <QtCore/qstring.h>
#include <QtNetwork/qnetworkinterface.h>
diff --git a/src/network/bearer/qnetworksession_p.h b/src/network/bearer/qnetworksession_p.h
index 45596ea003..661587603c 100644
--- a/src/network/bearer/qnetworksession_p.h
+++ b/src/network/bearer/qnetworksession_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qnetworksession.h"
#include "qnetworkconfiguration_p.h"
#include "QtCore/qsharedpointer.h"
diff --git a/src/network/bearer/qsharednetworksession_p.h b/src/network/bearer/qsharednetworksession_p.h
index ba4bd500ac..001b8af02a 100644
--- a/src/network/bearer/qsharednetworksession_p.h
+++ b/src/network/bearer/qsharednetworksession_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qnetworksession.h"
#include "qnetworkconfiguration.h"
#include <QHash>
diff --git a/src/network/configure.json b/src/network/configure.json
new file mode 100644
index 0000000000..97bf92167d
--- /dev/null
+++ b/src/network/configure.json
@@ -0,0 +1,274 @@
+{
+ "module": "network",
+ "depends": [
+ "core"
+ ],
+ "testDir": "../../config.tests",
+
+ "commandline": {
+ "assignments": {
+ "OPENSSL_LIBS": "openssl.libs",
+ "OPENSSL_LIBS_DEBUG": "openssl.libs.debug",
+ "OPENSSL_LIBS_RELEASE": "openssl.libs.release",
+ "OPENSSL_PATH": "openssl.prefix"
+ },
+ "options": {
+ "libproxy": "boolean",
+ "openssl": { "type": "optionalString", "values": [ "no", "yes", "linked", "runtime" ] },
+ "openssl-linked": { "type": "void", "name": "openssl", "value": "linked" },
+ "openssl-runtime": { "type": "void", "name": "openssl", "value": "runtime" },
+ "sctp": "boolean",
+ "securetransport": "boolean",
+ "ssl": "boolean",
+ "system-proxies": "boolean"
+ }
+ },
+
+ "libraries": {
+ "corewlan": {
+ "label": "CoreWLan",
+ "export": "",
+ "test": "mac/corewlan",
+ "sources": [
+ "-framework CoreWLAN -framework Foundation"
+ ]
+ },
+ "network": {
+ "export": "",
+ "sources": [
+ { "type": "makeSpec", "spec": "NETWORK" }
+ ]
+ },
+ "libproxy": {
+ "label": "libproxy",
+ "test": "common/libproxy",
+ "sources": [
+ "-lproxy"
+ ]
+ },
+ "openssl": {
+ "label": "OpenSSL Libraries",
+ "export": "",
+ "sources": [
+ { "type": "openssl" },
+ {
+ "comment": "placeholder for OPENSSL_LIBS{,_{DEBUG,RELEASE}}",
+ "libs": "",
+ "builds": {
+ "debug": "",
+ "release": ""
+ },
+ "condition": "config.win32 && !features.shared"
+ },
+ { "libs": "-lssleay32 -llibeay32", "condition": "config.win32 && features.shared" },
+ { "libs": "-lssl -lcrypto", "condition": "!config.win32" }
+ ]
+ }
+ },
+
+ "tests": {
+ "getaddrinfo": {
+ "label": "getaddrinfo()",
+ "type": "compile",
+ "test": "unix/getaddrinfo",
+ "use": "network"
+ },
+ "getifaddrs": {
+ "label": "getifaddrs()",
+ "type": "compile",
+ "test": "unix/getifaddrs",
+ "use": "network"
+ },
+ "ipv6ifname": {
+ "label": "IPv6 ifname",
+ "type": "compile",
+ "test": "unix/ipv6ifname",
+ "use": "network"
+ },
+ "openssl": {
+ "label": "OpenSSL",
+ "type": "compile",
+ "test": "unix/openssl"
+ },
+ "sctp": {
+ "label": "SCTP support",
+ "type": "compile",
+ "test": "unix/sctp",
+ "use": "network"
+ }
+ },
+
+ "features": {
+ "corewlan": {
+ "label": "CoreWLan",
+ "condition": "libs.corewlan",
+ "emitIf": "config.darwin",
+ "output": [ "feature", "privateFeature" ]
+ },
+ "getaddrinfo": {
+ "label": "getaddrinfo()",
+ "condition": "tests.getaddrinfo",
+ "output": [ "feature" ]
+ },
+ "getifaddrs": {
+ "label": "getifaddrs()",
+ "condition": "tests.getifaddrs",
+ "output": [ "feature" ]
+ },
+ "ipv6ifname": {
+ "label": "IPv6 ifname",
+ "condition": "tests.ipv6ifname",
+ "output": [ "feature" ]
+ },
+ "libproxy": {
+ "label": "libproxy",
+ "autoDetect": false,
+ "condition": "libs.libproxy",
+ "output": [ "privateFeature" ]
+ },
+ "openssl": {
+ "label": "OpenSSL",
+ "enable": "input.openssl == 'yes' || input.openssl == 'linked' || input.openssl == 'runtime'",
+ "disable": "input.openssl == 'no' || input.ssl == 'no'",
+ "autoDetect": "!config.winrt",
+ "condition": "!features.securetransport && tests.openssl",
+ "output": [
+ "privateFeature",
+ { "type": "publicQtConfig", "condition": "!features.openssl-linked" },
+ { "type": "define", "negative": true, "name": "QT_NO_OPENSSL" }
+ ]
+ },
+ "openssl-linked": {
+ "label": " Qt directly linked to OpenSSL",
+ "enable": "input.openssl == 'linked'",
+ "disable": "input.openssl != 'linked'",
+ "condition": "features.openssl && libs.openssl",
+ "output": [
+ "privateFeature",
+ { "type": "varAssign", "name": "OPENSSL_LIBS", "value": "libs.openssl.libs", "eval": "true" },
+ { "type": "varAssign", "name": "OPENSSL_LIBS_DEBUG", "value": "libs.openssl.builds.debug.libs",
+ "eval": "true", "condition": "config.win32" },
+ { "type": "varAssign", "name": "OPENSSL_LIBS_RELEASE", "value": "libs.openssl.builds.release.libs",
+ "eval": "true", "condition": "config.win32" },
+ { "type": "define", "name": "QT_LINKED_OPENSSL" }
+ ]
+ },
+ "securetransport": {
+ "label": "SecureTransport",
+ "disable": "input.securetransport == 'no' || input.ssl == 'no'",
+ "condition": "config.darwin && (input.openssl == '' || input.openssl == 'no')",
+ "output": [
+ "privateFeature",
+ { "type": "define", "name": "QT_SECURETRANSPORT" }
+ ]
+ },
+ "ssl": {
+ "label": "SSL",
+ "condition": "config.winrt || features.securetransport || features.openssl",
+ "output": [ "publicFeature", "feature" ]
+ },
+ "sctp": {
+ "label": "SCTP",
+ "autoDetect": false,
+ "condition": "tests.sctp",
+ "output": [ "publicFeature", "feature" ]
+ },
+ "system-proxies": {
+ "label": "Use system proxies",
+ "output": [ "privateFeature" ]
+ },
+ "ftp": {
+ "label": "FTP",
+ "purpose": "Provides support for the File Transfer Protocol in QNetworkAccessManager.",
+ "section": "Networking",
+ "condition": "features.textdate",
+ "output": [ "publicFeature", "feature" ]
+ },
+ "http": {
+ "label": "HTTP",
+ "purpose": "Provides support for the Hypertext Transfer Protocol in QNetworkAccessManager.",
+ "section": "Networking",
+ "output": [ "publicFeature", "feature" ]
+ },
+ "udpsocket": {
+ "label": "QUdpSocket",
+ "purpose": "Provides access to UDP sockets.",
+ "section": "Networking",
+ "output": [ "publicFeature", "feature" ]
+ },
+ "networkproxy": {
+ "label": "QNetworkProxy",
+ "purpose": "Provides network proxy support.",
+ "section": "Networking",
+ "output": [ "publicFeature", "feature" ]
+ },
+ "socks5": {
+ "label": "SOCKS5",
+ "purpose": "Provides SOCKS5 support in QNetworkProxy.",
+ "section": "Networking",
+ "output": [ "publicFeature", "feature" ]
+ },
+ "networkinterface": {
+ "label": "QNetworkInterface",
+ "purpose": "Supports enumerating a host's IP addresses and network interfaces.",
+ "section": "Networking",
+ "output": [ "publicFeature", "feature" ]
+ },
+ "networkdiskcache": {
+ "label": "QNetworkDiskCache",
+ "purpose": "Provides a disk cache for network resources.",
+ "section": "Networking",
+ "condition": "features.temporaryfile",
+ "output": [ "publicFeature", "feature" ]
+ },
+ "bearermanagement": {
+ "label": "Bearer management",
+ "purpose": "Provides bearer management for the network stack.",
+ "section": "Networking",
+ "condition": "features.library && features.networkinterface && features.properties",
+ "output": [ "publicFeature", "feature" ]
+ },
+ "localserver": {
+ "label": "QLocalServer",
+ "purpose": "Provides a local socket based server.",
+ "section": "Networking",
+ "condition": "features.temporaryfile",
+ "output": [ "publicFeature", "feature" ]
+ }
+ },
+
+ "report": [
+ {
+ "type": "note",
+ "condition": "features.openssl-linked && libs.openssl.source != 0
+ && input.openssl.prefix == '' && input.openssl.libs == '' && input.openssl.libs.debug == ''",
+ "message": "When linking against OpenSSL, you can override the default
+library names through OPENSSL_LIBS.
+For example:
+ OPENSSL_LIBS='-L/opt/ssl/lib -lssl -lcrypto' ./configure -openssl-linked"
+ }
+ ],
+
+ "summary": [
+ {
+ "section": "Qt Network",
+ "entries": [
+ {
+ "type": "feature",
+ "args": "corewlan",
+ "condition": "config.darwin"
+ },
+ "getaddrinfo", "getifaddrs", "ipv6ifname", "libproxy",
+ {
+ "type": "feature",
+ "args": "securetransport",
+ "condition": "config.darwin"
+ },
+ "openssl",
+ "openssl-linked",
+ "sctp",
+ "system-proxies"
+ ]
+ }
+ ]
+}
diff --git a/src/network/configure.pri b/src/network/configure.pri
new file mode 100644
index 0000000000..57568902e4
--- /dev/null
+++ b/src/network/configure.pri
@@ -0,0 +1,12 @@
+# custom tests
+
+defineTest(qtConfLibrary_openssl) {
+ libs = $$getenv("OPENSSL_LIBS")
+ !isEmpty(libs) {
+ $${1}.libs = $$libs
+ export($${1}.libs)
+ return(true)
+ }
+ return(false)
+}
+
diff --git a/src/network/doc/snippets/code/src_network_socket_qsctpsocket.cpp b/src/network/doc/snippets/code/src_network_socket_qsctpsocket.cpp
new file mode 100644
index 0000000000..3783a6f939
--- /dev/null
+++ b/src/network/doc/snippets/code/src_network_socket_qsctpsocket.cpp
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the documentation of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//! [0]
+QSctpSocket *socket = new QSctpSocket(this);
+
+socket->setMaxChannelCount(16);
+socket->connectToHost(QHostAddress::LocalHost, 1973);
+
+if (socket->waitForConnected(1000)) {
+ int inputChannels = socket->readChannelCount();
+ int outputChannels = socket->writeChannelCount();
+
+ ....
+}
+//! [0]
diff --git a/src/network/doc/snippets/code/src_network_socket_qudpsocket.cpp b/src/network/doc/snippets/code/src_network_socket_qudpsocket.cpp
index 9e491f802e..a98e31b10b 100644
--- a/src/network/doc/snippets/code/src_network_socket_qudpsocket.cpp
+++ b/src/network/doc/snippets/code/src_network_socket_qudpsocket.cpp
@@ -61,14 +61,7 @@ void Server::initSocket()
void Server::readPendingDatagrams()
{
while (udpSocket->hasPendingDatagrams()) {
- QByteArray datagram;
- datagram.resize(udpSocket->pendingDatagramSize());
- QHostAddress sender;
- quint16 senderPort;
-
- udpSocket->readDatagram(datagram.data(), datagram.size(),
- &sender, &senderPort);
-
+ QNetworkDatagram datagram = udpSocket->receiveDatagram();
processTheDatagram(datagram);
}
}
diff --git a/src/network/kernel/kernel.pri b/src/network/kernel/kernel.pri
index 811d8b6f0d..8f37e28669 100644
--- a/src/network/kernel/kernel.pri
+++ b/src/network/kernel/kernel.pri
@@ -3,7 +3,9 @@
PRECOMPILED_HEADER = ../corelib/global/qt_pch.h
INCLUDEPATH += $$PWD
-HEADERS += kernel/qauthenticator.h \
+HEADERS += kernel/qtnetworkglobal.h \
+ kernel/qtnetworkglobal_p.h \
+ kernel/qauthenticator.h \
kernel/qauthenticator_p.h \
kernel/qdnslookup.h \
kernel/qdnslookup_p.h \
@@ -11,6 +13,7 @@ HEADERS += kernel/qauthenticator.h \
kernel/qhostaddress_p.h \
kernel/qhostinfo.h \
kernel/qhostinfo_p.h \
+ kernel/qnetworkdatagram.h \
kernel/qnetworkdatagram_p.h \
kernel/qnetworkinterface.h \
kernel/qnetworkinterface_p.h \
@@ -22,9 +25,10 @@ SOURCES += kernel/qauthenticator.cpp \
kernel/qdnslookup.cpp \
kernel/qhostaddress.cpp \
kernel/qhostinfo.cpp \
- kernel/qurlinfo.cpp \
+ kernel/qnetworkdatagram.cpp \
+ kernel/qnetworkinterface.cpp \
kernel/qnetworkproxy.cpp \
- kernel/qnetworkinterface.cpp
+ kernel/qurlinfo.cpp
unix {
!integrity: SOURCES += kernel/qdnslookup_unix.cpp
@@ -52,14 +56,15 @@ win32: {
}
mac {
- LIBS_PRIVATE += -framework SystemConfiguration -framework CoreFoundation
- !ios: LIBS_PRIVATE += -framework CoreServices
+ LIBS_PRIVATE += -framework CoreFoundation
+ !uikit: LIBS_PRIVATE += -framework CoreServices
+ !if(watchos:CONFIG(device, simulator|device)): LIBS_PRIVATE += -framework SystemConfiguration
}
-mac:!ios:SOURCES += kernel/qnetworkproxy_mac.cpp
+osx:SOURCES += kernel/qnetworkproxy_mac.cpp
else:win32:SOURCES += kernel/qnetworkproxy_win.cpp
-else:contains(QT_CONFIG, libproxy) {
+else: qtConfig(libproxy) {
SOURCES += kernel/qnetworkproxy_libproxy.cpp
- LIBS_PRIVATE += -lproxy
+ QMAKE_USE_PRIVATE += libproxy
}
else:SOURCES += kernel/qnetworkproxy_generic.cpp
diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp
index 6da231d019..107addae58 100644
--- a/src/network/kernel/qauthenticator.cpp
+++ b/src/network/kernel/qauthenticator.cpp
@@ -1271,14 +1271,10 @@ static QByteArray qEncodeNtlmv2Response(const QAuthenticatorPrivate *ctx,
if(timeArray.size()) {
ds.writeRawData(timeArray.constData(), timeArray.size());
} else {
- QDateTime currentTime(QDate::currentDate(),
- QTime::currentTime(), Qt::UTC);
-
- // number of seconds between 1601 and epoc(1970)
+ // number of seconds between 1601 and the epoch (1970)
// 369 years, 89 leap years
// ((369 * 365) + 89) * 24 * 3600 = 11644473600
-
- time = currentTime.toTime_t() + Q_UINT64_C(11644473600);
+ time = QDateTime::currentSecsSinceEpoch() + 11644473600;
// represented as 100 nano seconds
time = time * Q_UINT64_C(10000000);
@@ -1455,15 +1451,9 @@ static bool q_NTLM_SSPI_library_load()
if (pSecurityFunctionTable == NULL) {
securityDLLHandle = LoadLibrary(L"secur32.dll");
if (securityDLLHandle != NULL) {
-#if defined(Q_OS_WINCE)
- INIT_SECURITY_INTERFACE pInitSecurityInterface =
- (INIT_SECURITY_INTERFACE)GetProcAddress(securityDLLHandle,
- L"InitSecurityInterfaceW");
-#else
INIT_SECURITY_INTERFACE pInitSecurityInterface =
(INIT_SECURITY_INTERFACE)GetProcAddress(securityDLLHandle,
"InitSecurityInterfaceW");
-#endif
if (pInitSecurityInterface != NULL)
pSecurityFunctionTable = pInitSecurityInterface();
}
diff --git a/src/network/kernel/qauthenticator.h b/src/network/kernel/qauthenticator.h
index 7f8d912b49..1032c2f501 100644
--- a/src/network/kernel/qauthenticator.h
+++ b/src/network/kernel/qauthenticator.h
@@ -40,6 +40,7 @@
#ifndef QAUTHENTICATOR_H
#define QAUTHENTICATOR_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qstring.h>
#include <QtCore/qvariant.h>
diff --git a/src/network/kernel/qauthenticator_p.h b/src/network/kernel/qauthenticator_p.h
index 46388a0185..8a1ee0ebe6 100644
--- a/src/network/kernel/qauthenticator_p.h
+++ b/src/network/kernel/qauthenticator_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <qhash.h>
#include <qbytearray.h>
#include <qstring.h>
diff --git a/src/network/kernel/qdnslookup.h b/src/network/kernel/qdnslookup.h
index bf8b6debbe..ead5e650f5 100644
--- a/src/network/kernel/qdnslookup.h
+++ b/src/network/kernel/qdnslookup.h
@@ -40,6 +40,7 @@
#ifndef QDNSLOOKUP_H
#define QDNSLOOKUP_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qlist.h>
#include <QtCore/qobject.h>
#include <QtCore/qshareddata.h>
diff --git a/src/network/kernel/qdnslookup_p.h b/src/network/kernel/qdnslookup_p.h
index 98d828b7b5..d070286383 100644
--- a/src/network/kernel/qdnslookup_p.h
+++ b/src/network/kernel/qdnslookup_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "QtCore/qmutex.h"
#include "QtCore/qrunnable.h"
#include "QtCore/qsharedpointer.h"
diff --git a/src/network/kernel/qhostaddress.cpp b/src/network/kernel/qhostaddress.cpp
index 13ecfac3f5..7e3d2c5d6e 100644
--- a/src/network/kernel/qhostaddress.cpp
+++ b/src/network/kernel/qhostaddress.cpp
@@ -73,7 +73,7 @@ QT_BEGIN_NAMESPACE
// sockaddr_in6 size changed between old and new SDK
// Only the new version is the correct one, so always
// use this structure.
-#if defined(Q_OS_WINCE) || defined(Q_OS_WINRT)
+#if defined(Q_OS_WINRT)
# if !defined(u_char)
# define u_char unsigned char
# endif
@@ -151,20 +151,37 @@ void QHostAddressPrivate::setAddress(quint32 a_)
/// parses v4-mapped addresses or the AnyIPv6 address and stores in \a a;
/// returns true if the address was one of those
-static bool convertToIpv4(quint32& a, const Q_IPV6ADDR &a6)
+static bool convertToIpv4(quint32& a, const Q_IPV6ADDR &a6, const QHostAddress::ConversionMode mode)
{
+ if (mode == QHostAddress::StrictConversion)
+ return false;
+
const uchar *ptr = a6.c;
if (qFromUnaligned<quint64>(ptr) != 0)
return false;
- if (qFromBigEndian<quint32>(ptr + 8) == 0) {
- // is it AnyIPv6?
- a = 0;
- return qFromBigEndian<quint32>(ptr + 12) == 0;
+
+ const quint32 mid = qFromBigEndian<quint32>(ptr + 8);
+ if ((mid == 0xffff) && (mode & QHostAddress::ConvertV4MappedToIPv4)) {
+ a = qFromBigEndian<quint32>(ptr + 12);
+ return true;
}
- if (qFromBigEndian<quint32>(ptr + 8) != 0xFFFF)
+ if (mid != 0)
return false;
- a = qFromBigEndian<quint32>(ptr + 12);
- return true;
+
+ const quint32 low = qFromBigEndian<quint32>(ptr + 12);
+ if ((low == 0) && (mode & QHostAddress::ConvertUnspecifiedAddress)) {
+ a = 0;
+ return true;
+ }
+ if ((low == 1) && (mode & QHostAddress::ConvertLocalHost)) {
+ a = INADDR_LOOPBACK;
+ return true;
+ }
+ if ((low != 1) && (mode & QHostAddress::ConvertV4CompatToIPv4)) {
+ a = low;
+ return true;
+ }
+ return false;
}
void QHostAddressPrivate::setAddress(const quint8 *a_)
@@ -173,7 +190,8 @@ void QHostAddressPrivate::setAddress(const quint8 *a_)
isParsed = true;
memcpy(a6.c, a_, sizeof(a6));
a = 0;
- convertToIpv4(a, a6);
+ convertToIpv4(a, a6, (QHostAddress::ConvertV4MappedToIPv4
+ | QHostAddress::ConvertUnspecifiedAddress));
}
void QHostAddressPrivate::setAddress(const Q_IPV6ADDR &a_)
@@ -385,6 +403,20 @@ void QNetmaskAddress::setPrefixLength(QAbstractSocket::NetworkLayerProtocol prot
\value Any The dual stack any-address. A socket bound with this address will listen on both IPv4 and IPv6 interfaces.
*/
+/*! \enum QHostAddress::ConversionModeFlag
+
+ \since 5.8
+
+ \value StrictConversion Don't convert IPv6 addresses to IPv4 when comparing two QHostAddress objects of different protocols, so they will always be considered different.
+ \value ConvertV4MappedToIPv4 Convert IPv4-mapped IPv6 addresses (RFC 4291 sect. 2.5.5.2) when comparing. Therefore QHostAddress("::ffff:192.168.1.1") will compare equal to QHostAddress("192.168.1.1").
+ \value ConvertV4CompatToIPv4 Convert IPv4-compatible IPv6 addresses (RFC 4291 sect. 2.5.5.1) when comparing. Therefore QHostAddress("::192.168.1.1") will compare equal to QHostAddress("192.168.1.1").
+ \value ConvertLocalHost Convert the IPv6 loopback addresses to its IPv4 equivalent when comparing. Therefore e.g. QHostAddress("::1") will compare equal to QHostAddress("127.0.0.1").
+ \value ConvertUnspecifiedAddress All unspecified addresses will compare equal, namely AnyIPv4, AnyIPv6 and Any.
+ \value TolerantConversion Sets all three preceding flags.
+
+ \sa isEqual()
+ */
+
/*! Constructs a null host address object, i.e. an address which is not valid for any host or interface.
\sa clear()
@@ -504,7 +536,7 @@ QHostAddress::QHostAddress(SpecialAddress address)
case LocalHostIPv6:
ip6[15] = 1;
- // fall through
+ Q_FALLTHROUGH();
case AnyIPv6:
d->setAddress(ip6);
return;
@@ -690,7 +722,9 @@ quint32 QHostAddress::toIPv4Address(bool *ok) const
quint32 dummy;
if (ok)
*ok = d->protocol == QAbstractSocket::IPv4Protocol || d->protocol == QAbstractSocket::AnyIPProtocol
- || (d->protocol == QAbstractSocket::IPv6Protocol && convertToIpv4(dummy, d->a6));
+ || (d->protocol == QAbstractSocket::IPv6Protocol
+ && convertToIpv4(dummy, d->a6, ConversionMode(QHostAddress::ConvertV4MappedToIPv4
+ | QHostAddress::ConvertUnspecifiedAddress)));
return d->a;
}
@@ -812,19 +846,73 @@ void QHostAddress::setScopeId(const QString &id)
/*!
Returns \c true if this host address is the same as the \a other address
- given; otherwise returns \c false.
+ given; otherwise returns \c false. This operator just calls isEqual(other, StrictConversion).
+
+ \sa isEqual()
*/
bool QHostAddress::operator==(const QHostAddress &other) const
{
+ return isEqual(other, StrictConversion);
+}
+
+/*!
+ \since 5.8
+
+ Returns \c true if this host address is the same as the \a other address
+ given; otherwise returns \c false.
+
+ The parameter \a mode controls which conversions are preformed between addresses
+ of differing protocols. If no \a mode is given, \c TolerantConversion is performed
+ by default.
+
+ \sa ConversionMode, operator==()
+ */
+bool QHostAddress::isEqual(const QHostAddress &other, ConversionMode mode) const
+{
QT_ENSURE_PARSED(this);
QT_ENSURE_PARSED(&other);
- if (d->protocol == QAbstractSocket::IPv4Protocol)
- return other.d->protocol == QAbstractSocket::IPv4Protocol && d->a == other.d->a;
+ if (d->protocol == QAbstractSocket::IPv4Protocol) {
+ switch (other.d->protocol) {
+ case QAbstractSocket::IPv4Protocol:
+ return d->a == other.d->a;
+ case QAbstractSocket::IPv6Protocol:
+ quint32 a4;
+ return convertToIpv4(a4, other.d->a6, mode) && (a4 == d->a);
+ case QAbstractSocket::AnyIPProtocol:
+ return (mode & QHostAddress::ConvertUnspecifiedAddress) && d->a == 0;
+ case QAbstractSocket::UnknownNetworkLayerProtocol:
+ return false;
+ }
+ }
+
if (d->protocol == QAbstractSocket::IPv6Protocol) {
- return other.d->protocol == QAbstractSocket::IPv6Protocol
- && memcmp(&d->a6, &other.d->a6, sizeof(Q_IPV6ADDR)) == 0;
+ switch (other.d->protocol) {
+ case QAbstractSocket::IPv4Protocol:
+ quint32 a4;
+ return convertToIpv4(a4, d->a6, mode) && (a4 == other.d->a);
+ case QAbstractSocket::IPv6Protocol:
+ return memcmp(&d->a6, &other.d->a6, sizeof(Q_IPV6ADDR)) == 0;
+ case QAbstractSocket::AnyIPProtocol:
+ return (mode & QHostAddress::ConvertUnspecifiedAddress)
+ && (other.d->a6_64.c[0] == 0) && (other.d->a6_64.c[1] == 0);
+ case QAbstractSocket::UnknownNetworkLayerProtocol:
+ return false;
+ }
+ }
+
+ if ((d->protocol == QAbstractSocket::AnyIPProtocol)
+ && (mode & QHostAddress::ConvertUnspecifiedAddress)) {
+ switch (other.d->protocol) {
+ case QAbstractSocket::IPv4Protocol:
+ return other.d->a == 0;
+ case QAbstractSocket::IPv6Protocol:
+ return (other.d->a6_64.c[0] == 0) && (other.d->a6_64.c[1] == 0);
+ default:
+ break;
+ }
}
+
return d->protocol == other.d->protocol;
}
diff --git a/src/network/kernel/qhostaddress.h b/src/network/kernel/qhostaddress.h
index 8236a71986..58af14ee33 100644
--- a/src/network/kernel/qhostaddress.h
+++ b/src/network/kernel/qhostaddress.h
@@ -41,6 +41,7 @@
#ifndef QHOSTADDRESS_H
#define QHOSTADDRESS_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qpair.h>
#include <QtCore/qstring.h>
#include <QtCore/qscopedpointer.h>
@@ -79,6 +80,16 @@ public:
AnyIPv6,
AnyIPv4
};
+ enum ConversionModeFlag {
+ ConvertV4MappedToIPv4 = 1,
+ ConvertV4CompatToIPv4 = 2,
+ ConvertUnspecifiedAddress = 4,
+ ConvertLocalHost = 8,
+ TolerantConversion = 0xff,
+
+ StrictConversion = 0
+ };
+ Q_DECLARE_FLAGS(ConversionMode, ConversionModeFlag)
QHostAddress();
explicit QHostAddress(quint32 ip4Addr);
@@ -118,6 +129,7 @@ public:
QString scopeId() const;
void setScopeId(const QString &id);
+ bool isEqual(const QHostAddress &address, ConversionMode mode = TolerantConversion) const;
bool operator ==(const QHostAddress &address) const;
bool operator ==(SpecialAddress address) const;
inline bool operator !=(const QHostAddress &address) const
@@ -139,6 +151,7 @@ public:
protected:
QScopedPointer<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)
diff --git a/src/network/kernel/qhostaddress_p.h b/src/network/kernel/qhostaddress_p.h
index 7b43af01bb..55c3e5afde 100644
--- a/src/network/kernel/qhostaddress_p.h
+++ b/src/network/kernel/qhostaddress_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qhostaddress.h"
#include "qabstractsocket.h"
diff --git a/src/network/kernel/qhostinfo.h b/src/network/kernel/qhostinfo.h
index 42317c4c65..9b4a4853d9 100644
--- a/src/network/kernel/qhostinfo.h
+++ b/src/network/kernel/qhostinfo.h
@@ -40,6 +40,7 @@
#ifndef QHOSTINFO_H
#define QHOSTINFO_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qlist.h>
#include <QtCore/qscopedpointer.h>
#include <QtNetwork/qhostaddress.h>
diff --git a/src/network/kernel/qhostinfo_p.h b/src/network/kernel/qhostinfo_p.h
index 68d7dfae5b..ba342bf533 100644
--- a/src/network/kernel/qhostinfo_p.h
+++ b/src/network/kernel/qhostinfo_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "QtCore/qcoreapplication.h"
#include "private/qcoreapplication_p.h"
#include "QtNetwork/qhostinfo.h"
diff --git a/src/network/kernel/qhostinfo_win.cpp b/src/network/kernel/qhostinfo_win.cpp
index b98f36a612..9e5d556f2b 100644
--- a/src/network/kernel/qhostinfo_win.cpp
+++ b/src/network/kernel/qhostinfo_win.cpp
@@ -81,11 +81,7 @@ static bool resolveLibraryInternal()
{
// Attempt to resolve getaddrinfo(); without it we'll have to fall
// back to gethostbyname(), which has no IPv6 support.
-#if defined(Q_OS_WINCE)
- local_getaddrinfo = (getaddrinfoProto) QSystemLibrary::resolve(QLatin1String("ws2"), "getaddrinfo");
- local_freeaddrinfo = (freeaddrinfoProto) QSystemLibrary::resolve(QLatin1String("ws2"), "freeaddrinfo");
- local_getnameinfo = (getnameinfoProto) QSystemLibrary::resolve(QLatin1String("ws2"), "getnameinfo");
-#elif defined (Q_OS_WINRT)
+#if defined (Q_OS_WINRT)
local_getaddrinfo = (getaddrinfoProto) &getaddrinfo;
local_freeaddrinfo = (freeaddrinfoProto) &freeaddrinfo;
local_getnameinfo = (getnameinfoProto) getnameinfo;
@@ -116,11 +112,6 @@ static void translateWSAError(int error, QHostInfo *results)
QHostInfo QHostInfoAgent::fromName(const QString &hostName)
{
-#if defined(Q_OS_WINCE)
- static QBasicMutex qPrivCEMutex;
- QMutexLocker locker(&qPrivCEMutex);
-#endif
-
QSysInfo::machineHostName(); // this initializes ws2_32.dll
// Load res_init on demand.
diff --git a/src/network/kernel/qnetworkdatagram.cpp b/src/network/kernel/qnetworkdatagram.cpp
new file mode 100644
index 0000000000..ba8a063edf
--- /dev/null
+++ b/src/network/kernel/qnetworkdatagram.cpp
@@ -0,0 +1,535 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Intel Corporation.
+** Contact: http://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 "qnetworkdatagram.h"
+#include "qnetworkdatagram_p.h"
+
+#ifndef QT_NO_UDPSOCKET
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \class QNetworkDatagram
+ \brief The QNetworkDatagram class provides the data and matadata of a UDP datagram.
+ \since 5.8
+ \ingroup network
+ \inmodule QtNetwork
+ \reentrant
+
+ QNetworkDatagram can be used with the \l QUdpSocket class to represent the full
+ information contained in a UDP (User Datagram Protocol) datagram.
+ QNetworkDatagram encapsulates the following information of a datagram:
+ \list
+ \li the payload data;
+ \li the sender address and port number;
+ \li the destination address and port number;
+ \li the remaining hop count limit (on IPv4, this field is usually called "time to live" - TTL);
+ \li the network interface index the datagram was received on or to be sent on.
+ \endlist
+
+ QUdpSocket will try to match a common behavior as much as possible on all
+ operating systems, but not all of the metadata above can be obtained in
+ some operating systems. Metadata that cannot be set on the datagram when
+ sending with QUdpSocket::writeDatagram() will be silently discarded.
+
+ Upon reception, the senderAddress() and senderPort() properties contain the
+ address and port of the peer that sent the datagram, while
+ destinationAddress() and destinationPort() contain the target that was
+ contained in the datagram. That is usually an address local to the current
+ machine, but it can also be an IPv4 broadcast address (such as
+ "255.255.255.255") or an IPv4 or IPv6 multicast address. Applications may
+ find it useful to determine if the datagram was sent specifically to this
+ machine via unicast addressing or whether it was sent to multiple destinations.
+
+ When sending, the senderAddress() and senderPort() should contain the local
+ address to be used when sending. The sender address must be an address that
+ is assigned to this machine, which can be obtained using
+ \l{QNetworkInterface}, and the port number must be the port number that the
+ socket is bound to. Either field can be left unset and will be filled in by
+ the operating system with default values. The destinationAddress() and
+ destinationPort() fields may be set to a target address different from the
+ one the UDP socket is currently associated with.
+
+ Usually, when sending a datagram in reply to a datagram previously
+ received, one will set the destinationAddress() to be the senderAddress()
+ of the incoming datagram and similarly for the port numbers. To facilitate
+ this common process, QNetworkDatagram provides the function makeReply().
+
+ The hopCount() function contains, for a received datagram, the remaining
+ hop count limit for the packet. When sending, it contains the hop count
+ limit to be set. Most protocols will leave this value set to the default
+ and let the operating system decide on the best value to be used.
+ Multicasting over IPv4 often uses this field to indicate the scope of the
+ multicast group (link-local, local to an organization or global).
+
+ The interfaceIndex() function contains the index of the operating system's
+ interface that received the packet. This value is the same one that can be
+ set on a QHostAddress::scopeId() property and matches the
+ QNetworkInterface::index() property. When sending packets to global
+ addresses, it is not necessary to set the interface index as the operating
+ system will choose the correct one using the system routing table. This
+ property is important when sending datagrams to link-local destinations,
+ whether unicast or multicast.
+
+ \section1 Feature support
+
+ Some features of QNetworkDatagram are not supported in all operating systems.
+ Only the address and ports of the remote host (sender in received packets
+ and destination for outgoing packets) are supported in all systems. On most
+ operating systems, the other features are supported only for IPv6. Software
+ should check at runtime whether the rest could be determined for IPv4
+ addresses.
+
+ The current feature support is as follows:
+
+ \table
+ \header \li Operating system \li Local address \li Hop count \li Interface index
+ \row \li FreeBSD \li Supported \li Supported \li Only for IPv6
+ \row \li Linux \li Supported \li Supported \li Supported
+ \row \li OS X \li Supported \li Supported \li Only for IPv6
+ \row \li Other Unix supporting RFC 3542 \li Only for IPv6 \li Only for IPv6 \li Only for IPv6
+ \row \li Windows XP and older \li Not supported \li Not supported \li Not supported
+ \row \li Windows Vista & up \li Supported \li Supported \li Supported
+ \row \li Windows CE \li Not supported \li Not supported \li Not supported
+ \row \li Windows RT \li Not supported \li Not supported \li Not supported
+ \endtable
+
+ \sa QUdpSocket, QNetworkInterface
+ */
+
+/*!
+ Creates a QNetworkDatagram object with no payload data and undefined destination address.
+
+ The payload can be modified by using setData() and the destination address
+ can be set with setDestination().
+
+ If the destination address is left undefined, QUdpSocket::writeDatagram()
+ will attempt to send the datagram to the address last associated with, by
+ using QUdpSocket::connectToHost().
+ */
+QNetworkDatagram::QNetworkDatagram()
+ : d(new QNetworkDatagramPrivate)
+{
+}
+
+/*!
+ Creates a QNetworkDatagram object and sets \a data as the payload data, along with
+ \a destinationAddress and \a port as the destination address of the datagram.
+ */
+QNetworkDatagram::QNetworkDatagram(const QByteArray &data, const QHostAddress &destinationAddress, quint16 port)
+ : d(new QNetworkDatagramPrivate(data, destinationAddress, port))
+{
+}
+
+/*!
+ Creates a copy of the \a other datagram, including the payload and metadata.
+
+ To create a datagram suitable for sending in a reply, use QNetworkDatagram::makeReply();
+ */
+QNetworkDatagram::QNetworkDatagram(const QNetworkDatagram &other)
+ : d(new QNetworkDatagramPrivate(*other.d))
+{
+}
+
+/*! \internal */
+QNetworkDatagram::QNetworkDatagram(QNetworkDatagramPrivate &dd)
+ : d(&dd)
+{
+}
+
+/*!
+ Copies the \a other datagram, including the payload and metadata.
+
+ To create a datagram suitable for sending in a reply, use QNetworkDatagram::makeReply();
+ */
+QNetworkDatagram &QNetworkDatagram::operator=(const QNetworkDatagram &other)
+{
+ *d = *other.d;
+ return *this;
+}
+
+/*!
+ Clears the payload data and metadata in this QNetworkDatagram object, resetting
+ them to their default values.
+ */
+void QNetworkDatagram::clear()
+{
+ d->data.clear();
+ d->header.senderAddress.clear();
+ d->header.destinationAddress.clear();
+ d->header.hopLimit = -1;
+ d->header.ifindex = 0;
+}
+
+/*!
+ \fn QNetworkDatagram::isNull() const
+ Returns true if this QNetworkDatagram object is null. This function is the
+ opposite of isValid().
+ */
+
+/*!
+ Returns true if this QNetworkDatagram object is valid. A valid QNetworkDatagram
+ object contains at least one sender or receiver address. Valid datagrams
+ can contain empty payloads.
+ */
+bool QNetworkDatagram::isValid() const
+{
+ return d->header.senderAddress.protocol() != QAbstractSocket::UnknownNetworkLayerProtocol ||
+ d->header.destinationAddress.protocol() != QAbstractSocket::UnknownNetworkLayerProtocol;
+}
+
+/*!
+ Returns the sender address associated with this datagram. For a datagram
+ received from the network, it is the address of the peer node that sent the
+ datagram. For an outgoing datagrams, it is the local address to be used
+ when sending.
+
+ If no sender address was set on this datagram, the returned object will
+ report true to QHostAddress::isNull().
+
+ \sa destinationAddress(), senderPort(), setSender()
+*/
+QHostAddress QNetworkDatagram::senderAddress() const
+{
+ return d->header.senderAddress;
+}
+
+/*!
+ Returns the destination address associated with this datagram. For a
+ datagram received from the network, it is the address the peer node sent
+ the datagram to, which can either be a local address of this machine or a
+ multicast or broadcast address. For an outgoing datagrams, it is the
+ address the datagram should be sent to.
+
+ If no destination address was set on this datagram, the returned object
+ will report true to QHostAddress::isNull().
+
+ \sa senderAddress(), destinationPort(), setDestination()
+*/
+QHostAddress QNetworkDatagram::destinationAddress() const
+{
+ return d->header.destinationAddress;
+}
+
+/*!
+ Returns the port number of the sender associated with this datagram. For a
+ datagram received from the network, it is the port number that the peer
+ node sent the datagram from. For an outgoing datagram, it is the local port
+ the datagram should be sent from.
+
+ If no sender address was associated with this datagram, this function
+ returns -1.
+
+ \sa senderAddress(), destinationPort(), setSender()
+*/
+int QNetworkDatagram::senderPort() const
+{
+ return d->header.senderAddress.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol
+ ? -1 : d->header.senderPort;
+}
+
+/*!
+ Returns the port number of the destination associated with this datagram.
+ For a datagram received from the network, it is the local port number that
+ the peer node sent the datagram to. For an outgoing datagram, it is the
+ peer port the datagram should be sent to.
+
+ If no destination address was associated with this datagram, this function
+ returns -1.
+
+ \sa destinationAddress(), senderPort(), setDestination()
+*/
+int QNetworkDatagram::destinationPort() const
+{
+ return d->header.destinationAddress.protocol() == QAbstractSocket::UnknownNetworkLayerProtocol
+ ? -1 : d->header.destinationPort;
+}
+
+/*!
+ Sets the sender address associated with this datagram to be the address \a
+ address and port number \a port. The sender address and port numbers are
+ usually set by \l QUdpSocket upon reception, so there's no need to call
+ this function on a received datagram.
+
+ For outgoing datagrams, this function can be used to set the address the
+ datagram should carry. The address \a address must usually be one of the
+ local addresses assigned to this machine, which can be obtained using \l
+ QNetworkInterface. If left unset, the operating system will choose the most
+ appropriate address to use given the destination in question.
+
+ The port number \a port must be the port number associated with the socket,
+ if there is one. The value of 0 can be used to indicate that the operating
+ system should choose the port number.
+
+ \sa QUdpSocket::writeDatagram(), senderAddress(), senderPort(), setDestination()
+ */
+void QNetworkDatagram::setSender(const QHostAddress &address, quint16 port)
+{
+ d->header.senderAddress = address;
+ d->header.senderPort = port;
+}
+
+/*!
+ Sets the destination address associated with this datagram to be the
+ address \a address and port number \a port. The destination address and
+ port numbers are usually set by \l QUdpSocket upon reception, so there's no
+ need to call this function on a received datagram.
+
+ For outgoing datagrams, this function can be used to set the address the
+ datagram should be sent to. It can be the unicast address used to
+ communicate with the peer or a broadcast or multicast address to send to a
+ group of devices.
+
+ \sa QUdpSocket::writeDatagram(), destinationAddress(), destinationPort(), setSender()
+ */
+void QNetworkDatagram::setDestination(const QHostAddress &address, quint16 port)
+{
+ d->header.destinationAddress = address;
+ d->header.destinationPort = port;
+}
+
+/*!
+ Returns the hop count limit associated with this datagram. The hop count
+ limit is the number of nodes that are allowed to forward the IP packet
+ before it expires and an error is sent back to the sender of the datagram.
+ In IPv4, this value is usually known as "time to live" (TTL).
+
+ If this datagram was received from the network, this is the remaining hop
+ count of the datagram after reception and was decremented by 1 by each node
+ that forwarded the packet. A value of -1 indicates that the hop limit count
+ not be obtained.
+
+ If this is an outgoing datagram, this is the value to be set in the IP header
+ upon sending. A value of -1 indicates the operating system should choose
+ the value.
+
+ \sa setHopLimit()
+ */
+int QNetworkDatagram::hopLimit() const
+{
+ return d->header.hopLimit;
+}
+
+/*!
+ Sets the hop count limit associated with this datagram to \a count. The hop
+ count limit is the number of nodes that are allowed to forward the IP
+ packet before it expires and an error is sent back to the sender of the
+ datagram. In IPv4, this value is usually known as "time to live" (TTL).
+
+ It is usually not necessary to call this function on datagrams received
+ from the network.
+
+ If this is an outgoing packet, this is the value to be set in the IP header
+ upon sending. The valid range for the value is 1 to 255. This function also
+ accepts a value of -1 to indicate that the operating system should choose
+ the value.
+
+ \sa hopLimit()
+ */
+void QNetworkDatagram::setHopLimit(int count)
+{
+ d->header.hopLimit = count;
+}
+
+/*!
+ Returns the interface index this datagram is associated with. The interface
+ index is a positive number that uniquely identifies the network interface
+ in the operating system. This number matches the value returned by
+ QNetworkInterface::index() for the interface.
+
+ If this datagram was received from the network, this is the index of the
+ interface that the packet was received from. If this is an outgoing
+ datagram, this is the index of the interface that the datagram should be
+ sent on.
+
+ A value of 0 indicates that the interface index is unknown.
+
+ \sa setInterfaceIndex()
+ */
+uint QNetworkDatagram::interfaceIndex() const
+{
+ return d->header.ifindex;
+}
+
+/*!
+ Sets the interface index this datagram is associated with to \a index. The
+ interface index is a positive number that uniquely identifies the network
+ interface in the operating system. This number matches the value returned
+ by QNetworkInterface::index() for the interface.
+
+ It is usually not necessary to call this function on datagrams received
+ from the network.
+
+ If this is an outgoing packet, this is the index of the interface the
+ datagram should be sent on. A value of 0 indicates that the operating
+ system should choose the interface based on other factors.
+
+ Note that the interface index can also be set with
+ QHostAddress::setScopeId() for IPv6 destination addresses and then with
+ setDestination(). If the scope ID set in the destination address and \a
+ index are different and neither is zero, it is undefined which interface
+ the operating system will send the datagram on.
+
+ \sa setInterfaceIndex()
+ */
+void QNetworkDatagram::setInterfaceIndex(uint index)
+{
+ d->header.ifindex = index;
+}
+
+/*!
+ Returns the data payload of this datagram. For a datagram received from the
+ network, it contains the payload of the datagram. For an outgoing datagram,
+ it is the datagram to be sent.
+
+ Note that datagrams can be transmitted with no data, so the returned
+ QByteArray may be empty.
+
+ \sa setData()
+ */
+QByteArray QNetworkDatagram::data() const
+{
+ return d->data;
+}
+
+/*!
+ Sets the data payload of this datagram to \a data. It is usually not
+ necessary to call this function on received datagrams. For outgoing
+ datagrams, this function sets the data to be sent on the network.
+
+ Since datagrams can empty, an empty QByteArray is a valid value for \a
+ data.
+
+ \sa data()
+ */
+void QNetworkDatagram::setData(const QByteArray &data)
+{
+ d->data = data;
+}
+
+/*!
+ \fn QNetworkDatagram QNetworkDatagram::makeReply(const QByteArray &data) const
+
+ Creates a new QNetworkDatagram representing a reply to this incoming datagram
+ and sets the payload data to \a data. This function is a very convenient
+ way of responding to a datagram back to the original sender.
+
+ Example:
+ \code
+ void Server::readPendingDatagrams()
+ {
+ while (udpSocket->hasPendingDatagrams()) {
+ QNetworkDatagram datagram = udpSocket->receiveDatagram();
+ QByteArray replyData = processThePayload(datagram.data());
+ udpSocket->writeDatagram(datagram.makeReply(replyData));
+ }
+ }
+ \endcode
+
+ This function is especially convenient since it will automatically copy
+ parameters from this datagram to the new datagram as appropriate:
+
+ \list
+ \li this datagram's sender address and port are copied to the new
+ datagram's destination address and port;
+ \li this datagram's interface index, if any, is copied to the new
+ datagram's interface index;
+ \li this datagram's destination address and port are copied to the new
+ datagram's sender address and port only if the address is IPv6
+ global (non-multicast) address;
+ \li the hop count limit on the new datagram is reset to the default (-1);
+ \endlist
+
+ If QNetworkDatagram is modified in a future version of Qt to carry further
+ metadata, this function will copy that metadata as appropriate.
+
+ This datagram's destination address is not copied if it is an IPv4 address
+ because it is not possible to tell an IPv4 broadcast address apart from a
+ regular IPv4 address without an exhaustive search of all addresses assigned
+ to this machine. Attempting to send a datagram with the sender address
+ equal to the broadcast address is likely to fail. However, this should not
+ affect the communication as network interfaces with multiple IPv4 addresses
+ are uncommon, so the address the operating system will select will likely
+ be one the peer will understand.
+
+ \note This function comes with both rvalue- and lvalue-reference qualifier
+ overloads, so it is a good idea to make sure this object is rvalue, if
+ possible, before calling makeReply, so as to make better use of move
+ semantics. To achieve that, the example above would use:
+ \code
+ udpSocket->writeDatagram(std::move(datagram).makeReply(replyData));
+ \endcode
+ */
+
+static bool isNonMulticast(const QHostAddress &addr)
+{
+ // is it a multicast address?
+ return !addr.isMulticast();
+}
+
+QNetworkDatagram QNetworkDatagram::makeReply_helper(const QByteArray &data) const
+{
+ QNetworkDatagramPrivate *x = new QNetworkDatagramPrivate(data, d->header.senderAddress, d->header.senderPort);
+ x->header.ifindex = d->header.ifindex;
+ if (isNonMulticast(d->header.destinationAddress)) {
+ x->header.senderAddress = d->header.destinationAddress;
+ x->header.senderPort = d->header.destinationPort;
+ }
+ return QNetworkDatagram(*x);
+}
+
+void QNetworkDatagram::makeReply_helper_inplace(const QByteArray &data)
+{
+ d->data = data;
+ d->header.hopLimit = -1;
+ qSwap(d->header.destinationPort, d->header.senderPort);
+ qSwap(d->header.destinationAddress, d->header.senderAddress);
+ if (!isNonMulticast(d->header.senderAddress))
+ d->header.senderAddress.clear();
+}
+
+void QNetworkDatagram::destroy(QNetworkDatagramPrivate *d)
+{
+ Q_ASSUME(d);
+ delete d;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_UDPSOCKET
diff --git a/src/network/kernel/qnetworkdatagram.h b/src/network/kernel/qnetworkdatagram.h
new file mode 100644
index 0000000000..a20d69185a
--- /dev/null
+++ b/src/network/kernel/qnetworkdatagram.h
@@ -0,0 +1,123 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Intel Corporation.
+** Contact: http://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 QNETWORKDATAGRAM_H
+#define QNETWORKDATAGRAM_H
+
+#include <QtCore/qbytearray.h>
+#include <QtNetwork/qhostaddress.h>
+
+#ifndef QT_NO_UDPSOCKET
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkDatagramPrivate;
+class QUdpSocketPrivate;
+
+class Q_NETWORK_EXPORT QNetworkDatagram
+{
+public:
+ QNetworkDatagram();
+ QNetworkDatagram(const QByteArray &data, const QHostAddress &destinationAddress = QHostAddress(),
+ quint16 port = 0); // implicit
+ QNetworkDatagram(const QNetworkDatagram &other);
+ QNetworkDatagram &operator=(const QNetworkDatagram &other);
+ ~QNetworkDatagram()
+ { if (d) destroy(d); }
+
+ QNetworkDatagram(QNetworkDatagram &&other) Q_DECL_NOTHROW
+ : d(other.d)
+ { other.d = Q_NULLPTR; }
+ QNetworkDatagram &operator=(QNetworkDatagram &&other) Q_DECL_NOTHROW
+ { swap(other); return *this; }
+
+ void swap(QNetworkDatagram &other) Q_DECL_NOTHROW
+ { qSwap(d, other.d); }
+
+ void clear();
+ bool isValid() const;
+ bool isNull() const
+ { return !isValid(); }
+
+ uint interfaceIndex() const;
+ void setInterfaceIndex(uint index);
+
+ QHostAddress senderAddress() const;
+ QHostAddress destinationAddress() const;
+ int senderPort() const;
+ int destinationPort() const;
+ void setSender(const QHostAddress &address, quint16 port = 0);
+ void setDestination(const QHostAddress &address, quint16 port);
+
+ int hopLimit() const;
+ void setHopLimit(int count);
+
+ QByteArray data() const;
+ void setData(const QByteArray &data);
+
+#ifdef Q_COMPILER_REF_QUALIFIERS
+ QNetworkDatagram makeReply(const QByteArray &payload) const &
+ { return makeReply_helper(payload); }
+ QNetworkDatagram makeReply(const QByteArray &payload) &&
+ { makeReply_helper_inplace(payload); return *this; }
+#else
+ QNetworkDatagram makeReply(const QByteArray &paylaod) const
+ { return makeReply_helper(paylaod); }
+#endif
+
+private:
+ QNetworkDatagramPrivate *d;
+ friend class QUdpSocket;
+ friend class QSctpSocket;
+
+ explicit QNetworkDatagram(QNetworkDatagramPrivate &dd);
+ QNetworkDatagram makeReply_helper(const QByteArray &data) const;
+ void makeReply_helper_inplace(const QByteArray &data);
+ static void destroy(QNetworkDatagramPrivate *d);
+};
+
+Q_DECLARE_SHARED(QNetworkDatagram)
+
+QT_END_NAMESPACE
+
+Q_DECLARE_METATYPE(QNetworkDatagram)
+
+#endif // QT_NO_UDPSOCKET
+
+#endif // QNETWORKDATAGRAM_H
diff --git a/src/network/kernel/qnetworkdatagram_p.h b/src/network/kernel/qnetworkdatagram_p.h
index 4941d8c94b..e55651a78b 100644
--- a/src/network/kernel/qnetworkdatagram_p.h
+++ b/src/network/kernel/qnetworkdatagram_p.h
@@ -45,6 +45,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <QtNetwork/qhostaddress.h>
QT_BEGIN_NAMESPACE
@@ -53,7 +54,8 @@ class QIpPacketHeader
{
public:
QIpPacketHeader(const QHostAddress &dstAddr = QHostAddress(), quint16 port = 0)
- : destinationAddress(dstAddr), ifindex(0), hopLimit(-1), destinationPort(port)
+ : destinationAddress(dstAddr), ifindex(0), hopLimit(-1), streamNumber(-1),
+ destinationPort(port), endOfRecord(false)
{}
void clear()
@@ -62,6 +64,8 @@ public:
destinationAddress.clear();
ifindex = 0;
hopLimit = -1;
+ streamNumber = -1;
+ endOfRecord = false;
}
QHostAddress senderAddress;
@@ -69,8 +73,25 @@ public:
uint ifindex;
int hopLimit;
+ int streamNumber;
quint16 senderPort;
quint16 destinationPort;
+ bool endOfRecord;
+};
+
+class QNetworkDatagramPrivate
+{
+public:
+ QNetworkDatagramPrivate(const QByteArray &data = QByteArray(),
+ const QHostAddress &dstAddr = QHostAddress(), quint16 port = 0)
+ : data(data), header(dstAddr, port)
+ {}
+ QNetworkDatagramPrivate(const QByteArray &data, const QIpPacketHeader &header)
+ : data(data), header(header)
+ {}
+
+ QByteArray data;
+ QIpPacketHeader header;
};
QT_END_NAMESPACE
diff --git a/src/network/kernel/qnetworkinterface.cpp b/src/network/kernel/qnetworkinterface.cpp
index 28893aa71c..c5d1adbef0 100644
--- a/src/network/kernel/qnetworkinterface.cpp
+++ b/src/network/kernel/qnetworkinterface.cpp
@@ -536,7 +536,7 @@ QList<QNetworkAddressEntry> QNetworkInterface::addressEntries() const
QNetworkInterface::interfaceFromName(name).index()
\endcode
- \sa interfaceFromName(), interfaceNameFromIndex()
+ \sa interfaceFromName(), interfaceNameFromIndex(), QNetworkDatagram::interfaceIndex()
*/
int QNetworkInterface::interfaceIndexFromName(const QString &name)
{
@@ -596,7 +596,7 @@ QNetworkInterface QNetworkInterface::interfaceFromIndex(int index)
QNetworkInterface::interfaceFromIndex(index).name()
\endcode
- \sa interfaceFromIndex(), interfaceIndexFromName()
+ \sa interfaceFromIndex(), interfaceIndexFromName(), QNetworkDatagram::interfaceIndex()
*/
QString QNetworkInterface::interfaceNameFromIndex(int index)
{
diff --git a/src/network/kernel/qnetworkinterface.h b/src/network/kernel/qnetworkinterface.h
index c519de8081..c31621c3cb 100644
--- a/src/network/kernel/qnetworkinterface.h
+++ b/src/network/kernel/qnetworkinterface.h
@@ -40,6 +40,7 @@
#ifndef QNETWORKINTERFACE_H
#define QNETWORKINTERFACE_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qshareddata.h>
#include <QtCore/qscopedpointer.h>
#include <QtNetwork/qhostaddress.h>
diff --git a/src/network/kernel/qnetworkinterface_p.h b/src/network/kernel/qnetworkinterface_p.h
index aa21d7f92c..ec25fdf37e 100644
--- a/src/network/kernel/qnetworkinterface_p.h
+++ b/src/network/kernel/qnetworkinterface_p.h
@@ -44,13 +44,14 @@
// W A R N I N G
// -------------
//
-// This file is not part of the Qt API. It exists for the convenience
-// of the QLibrary class. This header file may change from
-// version to version without notice, or even be removed.
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
//
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <QtCore/qatomic.h>
#include <QtCore/qlist.h>
#include <QtCore/qreadwritelock.h>
diff --git a/src/network/kernel/qnetworkinterface_unix.cpp b/src/network/kernel/qnetworkinterface_unix.cpp
index da53ccfe70..2fa98c0e07 100644
--- a/src/network/kernel/qnetworkinterface_unix.cpp
+++ b/src/network/kernel/qnetworkinterface_unix.cpp
@@ -488,7 +488,7 @@ static QList<QNetworkInterfacePrivate *> interfaceListing()
interfaces = createInterfaces(interfaceListing);
for (ifaddrs *ptr = interfaceListing; ptr; ptr = ptr->ifa_next) {
// Find the interface
- QString name = QString::fromLatin1(ptr->ifa_name);
+ QLatin1String name(ptr->ifa_name);
QNetworkInterfacePrivate *iface = 0;
QList<QNetworkInterfacePrivate *>::Iterator if_it = interfaces.begin();
for ( ; if_it != interfaces.end(); ++if_it)
diff --git a/src/network/kernel/qnetworkinterface_win.cpp b/src/network/kernel/qnetworkinterface_win.cpp
index 0a82eac417..3002b2497b 100644
--- a/src/network/kernel/qnetworkinterface_win.cpp
+++ b/src/network/kernel/qnetworkinterface_win.cpp
@@ -81,19 +81,11 @@ static void resolveLibs()
HINSTANCE iphlpapiHnd = GetModuleHandle(L"iphlpapi");
Q_ASSERT(iphlpapiHnd);
-#if defined(Q_OS_WINCE)
- // since Windows Embedded Compact 7
- ptrConvertInterfaceIndexToLuid = (PtrConvertInterfaceIndexToLuid)GetProcAddress(iphlpapiHnd, L"ConvertInterfaceIndexToLuid");
- ptrConvertInterfaceLuidToName = (PtrConvertInterfaceLuidToName)GetProcAddress(iphlpapiHnd, L"ConvertInterfaceLuidToNameW");
- ptrConvertInterfaceLuidToIndex = (PtrConvertInterfaceLuidToIndex)GetProcAddress(iphlpapiHnd, L"ConvertInterfaceLuidToIndex");
- ptrConvertInterfaceNameToLuid = (PtrConvertInterfaceNameToLuid)GetProcAddress(iphlpapiHnd, L"ConvertInterfaceNameToLuidW");
-#else
// since Windows Vista
ptrConvertInterfaceIndexToLuid = (PtrConvertInterfaceIndexToLuid)GetProcAddress(iphlpapiHnd, "ConvertInterfaceIndexToLuid");
ptrConvertInterfaceLuidToName = (PtrConvertInterfaceLuidToName)GetProcAddress(iphlpapiHnd, "ConvertInterfaceLuidToNameW");
ptrConvertInterfaceLuidToIndex = (PtrConvertInterfaceLuidToIndex)GetProcAddress(iphlpapiHnd, "ConvertInterfaceLuidToIndex");
ptrConvertInterfaceNameToLuid = (PtrConvertInterfaceNameToLuid)GetProcAddress(iphlpapiHnd, "ConvertInterfaceNameToLuidW");
-#endif
done = true;
}
}
diff --git a/src/network/kernel/qnetworkproxy.cpp b/src/network/kernel/qnetworkproxy.cpp
index 645f74c616..bb1a7d0d8b 100644
--- a/src/network/kernel/qnetworkproxy.cpp
+++ b/src/network/kernel/qnetworkproxy.cpp
@@ -212,6 +212,12 @@
lookup on a remote host name and connect to it, as opposed to
requiring the application to perform the name lookup and request
connection to IP addresses only.
+
+ \value SctpTunnelingCapability Ability to open transparent, tunneled
+ SCTP connections to a remote host.
+
+ \value SctpListeningCapability Ability to create a listening socket
+ and wait for an incoming SCTP connection from a remote host.
*/
#include "qnetworkproxy.h"
@@ -369,7 +375,9 @@ static QNetworkProxy::Capabilities defaultCapabilitiesForType(QNetworkProxy::Pro
/* [QNetworkProxy::DefaultProxy] = */
(int(QNetworkProxy::ListeningCapability) |
int(QNetworkProxy::TunnelingCapability) |
- int(QNetworkProxy::UdpTunnelingCapability)),
+ int(QNetworkProxy::UdpTunnelingCapability) |
+ int(QNetworkProxy::SctpTunnelingCapability) |
+ int(QNetworkProxy::SctpListeningCapability)),
/* [QNetworkProxy::Socks5Proxy] = */
(int(QNetworkProxy::TunnelingCapability) |
int(QNetworkProxy::ListeningCapability) |
@@ -379,7 +387,9 @@ static QNetworkProxy::Capabilities defaultCapabilitiesForType(QNetworkProxy::Pro
/* [QNetworkProxy::NoProxy] = */
(int(QNetworkProxy::ListeningCapability) |
int(QNetworkProxy::TunnelingCapability) |
- int(QNetworkProxy::UdpTunnelingCapability)),
+ int(QNetworkProxy::UdpTunnelingCapability) |
+ int(QNetworkProxy::SctpTunnelingCapability) |
+ int(QNetworkProxy::SctpListeningCapability)),
/* [QNetworkProxy::HttpProxy] = */
(int(QNetworkProxy::TunnelingCapability) |
int(QNetworkProxy::CachingCapability) |
@@ -966,6 +976,14 @@ template<> void QSharedDataPointer<QNetworkProxyQueryPrivate>::detach()
characteristics of the socket. The URL component is not used.
\row
+ \li SctpSocket
+ \li Message-oriented sockets requesting a connection to a remote
+ server. The peer hostname and peer port match the values passed
+ to QSctpSocket::connectToHost(). The local port is usually -1,
+ indicating the socket has no preference in which port should be
+ used. The URL component is not used.
+
+ \row
\li TcpServer
\li Passive server sockets that listen on a port and await
incoming connections from the network. Normally, only the
@@ -981,6 +999,14 @@ template<> void QSharedDataPointer<QNetworkProxyQueryPrivate>::detach()
indicate that more detailed information is present in the URL
component. For ease of implementation, the URL's host and
port are set as the destination address.
+
+ \row
+ \li SctpServer
+ \li Passive server sockets that listen on an SCTP port and await
+ incoming connections from the network. Normally, only the
+ local port is used, but the remote address could be used in
+ specific circumstances, for example to indicate which remote
+ host a connection is expected from. The URL component is not used.
\endtable
It should be noted that any of the criteria may be missing or
@@ -1001,10 +1027,13 @@ template<> void QSharedDataPointer<QNetworkProxyQueryPrivate>::detach()
\value TcpSocket a normal, outgoing TCP socket
\value UdpSocket a datagram-based UDP socket, which could send
to multiple destinations
+ \value SctpSocket a message-oriented, outgoing SCTP socket
\value TcpServer a TCP server that listens for incoming
connections from the network
\value UrlRequest a more complex request which involves loading
of a URL
+ \value SctpServer an SCTP server that listens for incoming
+ connections from the network
\sa queryType(), setQueryType()
*/
@@ -1614,6 +1643,10 @@ QDebug operator<<(QDebug debug, const QNetworkProxy &proxy)
scaps << QStringLiteral("Caching");
if (caps & QNetworkProxy::HostNameLookupCapability)
scaps << QStringLiteral("NameLookup");
+ if (caps & QNetworkProxy::SctpTunnelingCapability)
+ scaps << QStringLiteral("SctpTunnel");
+ if (caps & QNetworkProxy::SctpListeningCapability)
+ scaps << QStringLiteral("SctpListen");
debug << '[' << scaps.join(QLatin1Char(' ')) << ']';
return debug;
}
diff --git a/src/network/kernel/qnetworkproxy.h b/src/network/kernel/qnetworkproxy.h
index e60a84fa34..fc919a24a6 100644
--- a/src/network/kernel/qnetworkproxy.h
+++ b/src/network/kernel/qnetworkproxy.h
@@ -40,6 +40,7 @@
#ifndef QNETWORKPROXY_H
#define QNETWORKPROXY_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtNetwork/qhostaddress.h>
#include <QtNetwork/qnetworkrequest.h>
#include <QtCore/qshareddata.h>
@@ -59,8 +60,10 @@ public:
enum QueryType {
TcpSocket,
UdpSocket,
+ SctpSocket,
TcpServer = 100,
- UrlRequest
+ UrlRequest,
+ SctpServer
};
QNetworkProxyQuery();
@@ -140,7 +143,9 @@ public:
ListeningCapability = 0x0002,
UdpTunnelingCapability = 0x0004,
CachingCapability = 0x0008,
- HostNameLookupCapability = 0x0010
+ HostNameLookupCapability = 0x0010,
+ SctpTunnelingCapability = 0x00020,
+ SctpListeningCapability = 0x00040
};
Q_DECLARE_FLAGS(Capabilities, Capability)
diff --git a/src/network/kernel/qnetworkproxy_generic.cpp b/src/network/kernel/qnetworkproxy_generic.cpp
index e69870a98c..3ff0cc5168 100644
--- a/src/network/kernel/qnetworkproxy_generic.cpp
+++ b/src/network/kernel/qnetworkproxy_generic.cpp
@@ -79,7 +79,7 @@ static bool ignoreProxyFor(const QNetworkProxyQuery &query)
if (!peerHostName.startsWith('.'))
peerHostName.prepend('.');
- if (peerHostName.endsWith(QString::fromLatin1(token)))
+ if (peerHostName.endsWith(QLatin1String(token)))
return true;
}
diff --git a/src/network/kernel/qnetworkproxy_mac.cpp b/src/network/kernel/qnetworkproxy_mac.cpp
index 06a6fbac45..76a2d2df9f 100644
--- a/src/network/kernel/qnetworkproxy_mac.cpp
+++ b/src/network/kernel/qnetworkproxy_mac.cpp
@@ -180,31 +180,23 @@ static QNetworkProxy proxyFromDictionary(CFDictionaryRef dict)
return QNetworkProxy(proxyType, hostName, port, user, password);
}
-const char * cfurlErrorDescription(SInt32 errorCode)
+namespace {
+struct PACInfo {
+ QCFType<CFArrayRef> proxies;
+ QCFType<CFErrorRef> error;
+ bool done = false;
+};
+
+void proxyAutoConfigCallback(void *client, CFArrayRef proxylist, CFErrorRef error)
{
- switch (errorCode) {
- case kCFURLUnknownError:
- return "Unknown Error";
- case kCFURLUnknownSchemeError:
- return "Unknown Scheme";
- case kCFURLResourceNotFoundError:
- return "Resource Not Found";
- case kCFURLResourceAccessViolationError:
- return "Resource Access Violation";
- case kCFURLRemoteHostUnavailableError:
- return "Remote Host Unavailable";
- case kCFURLImproperArgumentsError:
- return "Improper Arguments";
- case kCFURLUnknownPropertyKeyError:
- return "Unknown Property Key";
- case kCFURLPropertyKeyUnavailableError:
- return "Property Key Unavailable";
- case kCFURLTimeoutError:
- return "Timeout";
- default:
- return "Really Unknown Error";
- }
+ PACInfo *info = reinterpret_cast<PACInfo *>(reinterpret_cast<CFStreamClientContext *>(client)->info);
+ info->done = true;
+ if (proxylist)
+ CFRetain(proxylist);
+ info->proxies = proxylist;
+ info->error = error;
}
+} // anon namespace
QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query)
{
@@ -234,60 +226,51 @@ QList<QNetworkProxy> macQueryInternal(const QNetworkProxyQuery &query)
QCFType<CFStringRef> cfPacLocation = CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, pacLocationSetting, NULL, NULL,
kCFStringEncodingUTF8);
- if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
- QCFType<CFDataRef> pacData;
- QCFType<CFURLRef> pacUrl = CFURLCreateWithString(kCFAllocatorDefault, cfPacLocation, NULL);
- if (!pacUrl) {
- qWarning("Invalid PAC URL \"%s\"", qPrintable(QCFString::toQString(cfPacLocation)));
- return result;
- }
- SInt32 errorCode;
- if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, pacUrl, &pacData, NULL, NULL, &errorCode)) {
- QString pacLocation = QCFString::toQString(cfPacLocation);
- qWarning("Unable to get the PAC script at \"%s\" (%s)", qPrintable(pacLocation), cfurlErrorDescription(errorCode));
- return result;
- }
- if (!pacData) {
- qWarning("\"%s\" returned an empty PAC script", qPrintable(QCFString::toQString(cfPacLocation)));
- return result;
- }
- QCFType<CFStringRef> pacScript = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault, pacData, kCFStringEncodingISOLatin1);
- if (!pacScript) {
- // This should never happen, but the documentation says it may return NULL if there was a problem creating the object.
- QString pacLocation = QCFString::toQString(cfPacLocation);
- qWarning("Unable to read the PAC script at \"%s\"", qPrintable(pacLocation));
- return result;
- }
-
- QByteArray encodedURL = query.url().toEncoded(); // converted to UTF-8
- if (encodedURL.isEmpty()) {
- return result; // Invalid URL, abort
- }
-
- QCFType<CFURLRef> targetURL = CFURLCreateWithBytes(kCFAllocatorDefault, (UInt8*)encodedURL.data(), encodedURL.size(), kCFStringEncodingUTF8, NULL);
- if (!targetURL) {
- return result; // URL creation problem, abort
- }
-
- QCFType<CFErrorRef> pacError;
- QCFType<CFArrayRef> proxies = CFNetworkCopyProxiesForAutoConfigurationScript(pacScript, targetURL, &pacError);
- if (!proxies) {
- QString pacLocation = QCFString::toQString(cfPacLocation);
- QCFType<CFStringRef> pacErrorDescription = CFErrorCopyDescription(pacError);
- qWarning("Execution of PAC script at \"%s\" failed: %s", qPrintable(pacLocation), qPrintable(QCFString::toQString(pacErrorDescription)));
- return result;
- }
-
- CFIndex size = CFArrayGetCount(proxies);
- for (CFIndex i = 0; i < size; ++i) {
- CFDictionaryRef proxy = (CFDictionaryRef)CFArrayGetValueAtIndex(proxies, i);
- result << proxyFromDictionary(proxy);
- }
+ QCFType<CFDataRef> pacData;
+ QCFType<CFURLRef> pacUrl = CFURLCreateWithString(kCFAllocatorDefault, cfPacLocation, NULL);
+ if (!pacUrl) {
+ qWarning("Invalid PAC URL \"%s\"", qPrintable(QCFString::toQString(cfPacLocation)));
return result;
- } else {
+ }
+
+ QByteArray encodedURL = query.url().toEncoded(); // converted to UTF-8
+ if (encodedURL.isEmpty()) {
+ return result; // Invalid URL, abort
+ }
+
+ QCFType<CFURLRef> targetURL = CFURLCreateWithBytes(kCFAllocatorDefault, (UInt8*)encodedURL.data(), encodedURL.size(), kCFStringEncodingUTF8, NULL);
+ if (!targetURL) {
+ return result; // URL creation problem, abort
+ }
+
+ CFStreamClientContext pacCtx;
+ pacCtx.version = 0;
+ PACInfo pacInfo;
+ pacCtx.info = &pacInfo;
+ pacCtx.retain = NULL;
+ pacCtx.release = NULL;
+ pacCtx.copyDescription = NULL;
+
+ static CFStringRef pacRunLoopMode = CFSTR("qtPACRunLoopMode");
+
+ QCFType<CFRunLoopSourceRef> pacRunLoopSource = CFNetworkExecuteProxyAutoConfigurationURL(pacUrl, targetURL, &proxyAutoConfigCallback, &pacCtx);
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), pacRunLoopSource, pacRunLoopMode);
+ while (!pacInfo.done)
+ CFRunLoopRunInMode(pacRunLoopMode, 1000, /*returnAfterSourceHandled*/ true);
+
+ if (!pacInfo.proxies) {
QString pacLocation = QCFString::toQString(cfPacLocation);
- qWarning("Mac system proxy: PAC script at \"%s\" not handled", qPrintable(pacLocation));
+ QCFType<CFStringRef> pacErrorDescription = CFErrorCopyDescription(pacInfo.error);
+ qWarning("Execution of PAC script at \"%s\" failed: %s", qPrintable(pacLocation), qPrintable(QCFString::toQString(pacErrorDescription)));
+ return result;
+ }
+
+ CFIndex size = CFArrayGetCount(pacInfo.proxies);
+ for (CFIndex i = 0; i < size; ++i) {
+ CFDictionaryRef proxy = (CFDictionaryRef)CFArrayGetValueAtIndex(pacInfo.proxies, i);
+ result << proxyFromDictionary(proxy);
}
+ return result;
}
}
diff --git a/src/network/kernel/qnetworkproxy_p.h b/src/network/kernel/qnetworkproxy_p.h
index 74cc4105a0..733d8436d0 100644
--- a/src/network/kernel/qnetworkproxy_p.h
+++ b/src/network/kernel/qnetworkproxy_p.h
@@ -40,18 +40,19 @@
#ifndef QNETWORKPROXY_P_H
#define QNETWORKPROXY_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 QLibrary class. This header file may change from
-// version to version without notice, or even be removed.
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
//
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
#ifndef QT_NO_NETWORKPROXY
QT_BEGIN_NAMESPACE
diff --git a/src/network/kernel/qnetworkproxy_win.cpp b/src/network/kernel/qnetworkproxy_win.cpp
index 513eeaac12..949f9fe12b 100644
--- a/src/network/kernel/qnetworkproxy_win.cpp
+++ b/src/network/kernel/qnetworkproxy_win.cpp
@@ -53,7 +53,6 @@
#include <qt_windows.h>
#include <wininet.h>
#include <lmcons.h>
-#include "qnetworkfunctions_wince.h"
/*
* Information on the WinHTTP DLL:
@@ -123,7 +122,6 @@ static PtrWinHttpGetIEProxyConfigForCurrentUser ptrWinHttpGetIEProxyConfigForCur
static PtrWinHttpCloseHandle ptrWinHttpCloseHandle = 0;
-#ifndef Q_OS_WINCE
static bool currentProcessIsService()
{
typedef BOOL (WINAPI *PtrGetUserName)(LPTSTR lpBuffer, LPDWORD lpnSize);
@@ -153,7 +151,6 @@ static bool currentProcessIsService()
}
return false;
}
-#endif // ! Q_OS_WINCE
static QStringList splitSpaceSemicolon(const QString &source)
{
@@ -235,9 +232,15 @@ static QList<QNetworkProxy> filterProxyListByCapabilities(const QList<QNetworkPr
case QNetworkProxyQuery::UdpSocket:
requiredCaps = QNetworkProxy::UdpTunnelingCapability;
break;
+ case QNetworkProxyQuery::SctpSocket:
+ requiredCaps = QNetworkProxy::SctpTunnelingCapability;
+ break;
case QNetworkProxyQuery::TcpServer:
requiredCaps = QNetworkProxy::ListeningCapability;
break;
+ case QNetworkProxyQuery::SctpServer:
+ requiredCaps = QNetworkProxy::SctpListeningCapability;
+ break;
default:
return proxyList;
break;
@@ -284,7 +287,10 @@ static QList<QNetworkProxy> parseServerList(const QNetworkProxyQuery &query, con
QList<QNetworkProxy> result;
QHash<QString, QNetworkProxy> taggedProxies;
const QString requiredTag = query.protocolTag();
- bool checkTags = !requiredTag.isEmpty() && query.queryType() != QNetworkProxyQuery::TcpServer; //windows tags are only for clients
+ // windows tags are only for clients
+ bool checkTags = !requiredTag.isEmpty()
+ && query.queryType() != QNetworkProxyQuery::TcpServer
+ && query.queryType() != QNetworkProxyQuery::SctpServer;
for (const QString &entry : proxyList) {
int server = 0;
@@ -361,7 +367,7 @@ static QList<QNetworkProxy> parseServerList(const QNetworkProxyQuery &query, con
return removeDuplicateProxies(result);
}
-#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
+#if !defined(Q_OS_WINRT)
namespace {
class QRegistryWatcher {
public:
@@ -412,7 +418,7 @@ private:
QVector<HKEY> m_registryHandles;
};
} // namespace
-#endif // !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
+#endif // !defined(Q_OS_WINRT)
class QWindowsSystemProxy
{
@@ -431,7 +437,7 @@ public:
QStringList proxyServerList;
QStringList proxyBypass;
QList<QNetworkProxy> defaultResult;
-#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
+#if !defined(Q_OS_WINRT)
QRegistryWatcher proxySettingsWatcher;
#endif
bool initialized;
@@ -467,7 +473,7 @@ void QWindowsSystemProxy::reset()
void QWindowsSystemProxy::init()
{
bool proxySettingsChanged = false;
-#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
+#if !defined(Q_OS_WINRT)
proxySettingsChanged = proxySettingsWatcher.hasChanged();
#endif
@@ -477,12 +483,7 @@ void QWindowsSystemProxy::init()
reset();
-#ifdef Q_OS_WINCE
- // Windows CE does not have any of the following API
- return;
-#else
-
-#if !defined(Q_OS_WINCE) && !defined(Q_OS_WINRT)
+#if !defined(Q_OS_WINRT)
proxySettingsWatcher.clear(); // needs reset to trigger a new detection
proxySettingsWatcher.addLocation(HKEY_CURRENT_USER, QStringLiteral("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"));
proxySettingsWatcher.addLocation(HKEY_LOCAL_MACHINE, QStringLiteral("Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"));
@@ -570,7 +571,6 @@ void QWindowsSystemProxy::init()
}
functional = isAutoConfig || !proxyServerList.isEmpty();
-#endif
}
QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkProxyQuery &query)
diff --git a/src/network/kernel/qnetworkfunctions_wince.h b/src/network/kernel/qtnetworkglobal.h
index 0e464a47f3..586b847816 100644
--- a/src/network/kernel/qnetworkfunctions_wince.h
+++ b/src/network/kernel/qtnetworkglobal.h
@@ -37,60 +37,25 @@
**
****************************************************************************/
-#ifndef QNETWORKFUNCTIONS_WINCE_H
-#define QNETWORKFUNCTIONS_WINCE_H
+#ifndef QTNETWORKGLOBAL_H
+#define QTNETWORKGLOBAL_H
-#if 0
-#pragma qt_sync_stop_processing
-#endif
-
-#ifdef Q_OS_WINCE
-
-#include <qt_windows.h>
+#include <QtCore/qglobal.h>
+#include <QtNetwork/qtnetwork-config.h>
QT_BEGIN_NAMESPACE
-DECLARE_HANDLE(SC_HANDLE);
-typedef enum _SC_ENUM_TYPE {
- SC_ENUM_PROCESS_INFO = 0
-} SC_ENUM_TYPE;
-#define SC_MANAGER_CONNECT 0x0001
-#define SC_MANAGER_CREATE_SERVICE 0x0002
-#define SC_MANAGER_ENUMERATE_SERVICE 0x0004
-#define SERVICE_ACTIVE 0x00000001
-#define SERVICE_INACTIVE 0x00000002
-#define SERVICE_STATE_ALL (SERVICE_ACTIVE | \
- SERVICE_INACTIVE)
-typedef struct _SERVICE_STATUS_PROCESS {
- DWORD dwServiceType;
- DWORD dwCurrentState;
- DWORD dwControlsAccepted;
- DWORD dwWin32ExitCode;
- DWORD dwServiceSpecificExitCode;
- DWORD dwCheckPoint;
- DWORD dwWaitHint;
- DWORD dwProcessId;
- DWORD dwServiceFlags;
-} SERVICE_STATUS_PROCESS, *LPSERVICE_STATUS_PROCESS;
-typedef struct _ENUM_SERVICE_STATUS_PROCESSA {
- LPSTR lpServiceName;
- LPSTR lpDisplayName;
- SERVICE_STATUS_PROCESS ServiceStatusProcess;
-} ENUM_SERVICE_STATUS_PROCESSA, *LPENUM_SERVICE_STATUS_PROCESSA;
-typedef struct _ENUM_SERVICE_STATUS_PROCESSW {
- LPWSTR lpServiceName;
- LPWSTR lpDisplayName;
- SERVICE_STATUS_PROCESS ServiceStatusProcess;
-} ENUM_SERVICE_STATUS_PROCESSW, *LPENUM_SERVICE_STATUS_PROCESSW;
-#ifdef UNICODE
-typedef ENUM_SERVICE_STATUS_PROCESSW ENUM_SERVICE_STATUS_PROCESS;
-typedef LPENUM_SERVICE_STATUS_PROCESSW LPENUM_SERVICE_STATUS_PROCESS;
+#ifndef QT_STATIC
+# if defined(QT_BUILD_NETWORK_LIB)
+# define Q_NETWORK_EXPORT Q_DECL_EXPORT
+# else
+# define Q_NETWORK_EXPORT Q_DECL_IMPORT
+# endif
#else
-typedef ENUM_SERVICE_STATUS_PROCESSA ENUM_SERVICE_STATUS_PROCESS;
-typedef LPENUM_SERVICE_STATUS_PROCESSA LPENUM_SERVICE_STATUS_PROCESS;
-#endif // UNICODE
+# define Q_NETWORK_EXPORT
+#endif
QT_END_NAMESPACE
-#endif // Q_OS_WINCE
-#endif // QNETWORKFUNCTIONS_WINCE_H
+#endif
+
diff --git a/src/network/kernel/qtnetworkglobal_p.h b/src/network/kernel/qtnetworkglobal_p.h
new file mode 100644
index 0000000000..859e3d9ebd
--- /dev/null
+++ b/src/network/kernel/qtnetworkglobal_p.h
@@ -0,0 +1,58 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 QTNETWORKGLOBAL_P_H
+#define QTNETWORKGLOBAL_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNetwork/qtnetworkglobal.h>
+#include <QtCore/private/qglobal_p.h>
+#include <QtNetwork/private/qtnetwork-config_p.h>
+
+#endif // QTNETWORKGLOBAL_P_H
diff --git a/src/network/kernel/qurlinfo_p.h b/src/network/kernel/qurlinfo_p.h
index b239f94c31..3a430a3321 100644
--- a/src/network/kernel/qurlinfo_p.h
+++ b/src/network/kernel/qurlinfo_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <QtCore/qdatetime.h>
#include <QtCore/qstring.h>
#include <QtCore/qiodevice.h>
diff --git a/src/network/network.pro b/src/network/network.pro
index 256d718df6..75105bd681 100644
--- a/src/network/network.pro
+++ b/src/network/network.pro
@@ -9,6 +9,7 @@ DEFINES += QT_NO_USING_NAMESPACE QT_NO_FOREACH
#DEFINES += QABSTRACTSOCKET_DEBUG QNATIVESOCKETENGINE_DEBUG
#DEFINES += QTCPSOCKETENGINE_DEBUG QTCPSOCKET_DEBUG QTCPSERVER_DEBUG QSSLSOCKET_DEBUG
#DEFINES += QUDPSOCKET_DEBUG QUDPSERVER_DEBUG
+#DEFINES += QSCTPSOCKET_DEBUG QSCTPSERVER_DEBUG
win32-msvc*|win32-icc:QMAKE_LFLAGS += /BASE:0x64000000
QMAKE_DOCS = $$PWD/doc/qtnetwork.qdocconf
diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp
index 96021f45f4..34a8025cdd 100644
--- a/src/network/socket/qabstractsocket.cpp
+++ b/src/network/socket/qabstractsocket.cpp
@@ -267,7 +267,8 @@
\value TcpSocket TCP
\value UdpSocket UDP
- \value UnknownSocketType Other than TCP and UDP
+ \value SctpSocket SCTP
+ \value UnknownSocketType Other than TCP, UDP and SCTP
\sa QAbstractSocket::socketType()
*/
@@ -563,6 +564,7 @@ QAbstractSocketPrivate::QAbstractSocketPrivate()
cachedSocketDescriptor(-1),
readBufferMaxSize(0),
isBuffered(false),
+ hasPendingData(false),
connectTimer(0),
disconnectTimer(0),
hostLookupId(-1),
@@ -593,6 +595,7 @@ void QAbstractSocketPrivate::resetSocketLayer()
qDebug("QAbstractSocketPrivate::resetSocketLayer()");
#endif
+ hasPendingData = false;
if (socketEngine) {
socketEngine->close();
socketEngine->disconnect();
@@ -624,6 +627,7 @@ bool QAbstractSocketPrivate::initSocketLayer(QAbstractSocket::NetworkLayerProtoc
QString typeStr;
if (q->socketType() == QAbstractSocket::TcpSocket) typeStr = QLatin1String("TcpSocket");
else if (q->socketType() == QAbstractSocket::UdpSocket) typeStr = QLatin1String("UdpSocket");
+ else if (q->socketType() == QAbstractSocket::SctpSocket) typeStr = QLatin1String("SctpSocket");
else typeStr = QLatin1String("UnknownSocketType");
QString protocolStr;
if (protocol == QAbstractSocket::IPv4Protocol) protocolStr = QLatin1String("IPv4Protocol");
@@ -668,6 +672,12 @@ bool QAbstractSocketPrivate::initSocketLayer(QAbstractSocket::NetworkLayerProtoc
*/
void QAbstractSocketPrivate::configureCreatedSocket()
{
+#ifndef QT_NO_SCTP
+ Q_Q(QAbstractSocket);
+ // Set single stream mode for unbuffered SCTP socket
+ if (socketEngine && q->socketType() == QAbstractSocket::SctpSocket)
+ socketEngine->setOption(QAbstractSocketEngine::MaxStreamsSocketOption, 1);
+#endif
}
/*! \internal
@@ -683,15 +693,13 @@ bool QAbstractSocketPrivate::canReadNotification()
qDebug("QAbstractSocketPrivate::canReadNotification()");
#endif
- if (!isBuffered)
- socketEngine->setReadNotificationEnabled(false);
-
// If buffered, read data from the socket into the read buffer
if (isBuffered) {
const qint64 oldBufferSize = buffer.size();
// Return if there is no space in the buffer
if (readBufferMaxSize && oldBufferSize >= readBufferMaxSize) {
+ socketEngine->setReadNotificationEnabled(false);
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocketPrivate::canReadNotification() buffer is full");
#endif
@@ -714,27 +722,21 @@ bool QAbstractSocketPrivate::canReadNotification()
// to indicate that the data was discarded.
return !q->isReadable();
}
-
- // If read buffer is full, disable the read socket notifier.
- if (readBufferMaxSize && buffer.size() == readBufferMaxSize) {
+ } else {
+ if (hasPendingData) {
socketEngine->setReadNotificationEnabled(false);
+ return true;
}
+ hasPendingData = true;
}
emitReadyRead();
- // If we were closed as a result of the readyRead() signal,
- // return.
- if (state == QAbstractSocket::UnconnectedState || state == QAbstractSocket::ClosingState) {
#if defined (QABSTRACTSOCKET_DEBUG)
+ // If we were closed as a result of the readyRead() signal.
+ if (state == QAbstractSocket::UnconnectedState || state == QAbstractSocket::ClosingState)
qDebug("QAbstractSocketPrivate::canReadNotification() socket is closing - returning");
#endif
- return true;
- }
-
- // turn the socket engine off if we've reached the buffer size limit
- if (socketEngine && isBuffered)
- socketEngine->setReadNotificationEnabled(readBufferMaxSize == 0 || readBufferMaxSize > q->bytesAvailable());
return true;
}
@@ -776,7 +778,8 @@ void QAbstractSocketPrivate::canCloseNotification()
QMetaObject::invokeMethod(socketEngine, "closeNotification", Qt::QueuedConnection);
}
- } else if (socketType == QAbstractSocket::TcpSocket && socketEngine) {
+ } else if ((socketType == QAbstractSocket::TcpSocket ||
+ socketType == QAbstractSocket::SctpSocket) && socketEngine) {
emitReadyRead();
}
}
@@ -792,12 +795,8 @@ bool QAbstractSocketPrivate::canWriteNotification()
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocketPrivate::canWriteNotification() flushing");
#endif
- bool dataWasWritten = writeToSocket();
- if (socketEngine && writeBuffer.isEmpty() && socketEngine->bytesToWrite() == 0)
- socketEngine->setWriteNotificationEnabled(false);
-
- return dataWasWritten;
+ return writeToSocket();
}
/*! \internal
@@ -837,8 +836,12 @@ bool QAbstractSocketPrivate::writeToSocket()
#endif
// this covers the case when the buffer was empty, but we had to wait for the socket engine to finish
- if (state == QAbstractSocket::ClosingState)
+ if (state == QAbstractSocket::ClosingState) {
q->disconnectFromHost();
+ } else {
+ if (socketEngine)
+ socketEngine->setWriteNotificationEnabled(false);
+ }
return false;
}
@@ -867,17 +870,12 @@ bool QAbstractSocketPrivate::writeToSocket()
if (written > 0) {
// Remove what we wrote so far.
writeBuffer.free(written);
- // Don't emit bytesWritten() recursively.
- if (!emittedBytesWritten) {
- QScopedValueRollback<bool> r(emittedBytesWritten);
- emittedBytesWritten = true;
- emit q->bytesWritten(written);
- }
- emit q->channelBytesWritten(0, written);
+
+ // Emit notifications.
+ emitBytesWritten(written);
}
- if (writeBuffer.isEmpty() && socketEngine && socketEngine->isWriteNotificationEnabled()
- && !socketEngine->bytesToWrite())
+ if (writeBuffer.isEmpty() && socketEngine && !socketEngine->bytesToWrite())
socketEngine->setWriteNotificationEnabled(false);
if (state == QAbstractSocket::ClosingState)
q->disconnectFromHost();
@@ -895,7 +893,7 @@ bool QAbstractSocketPrivate::flush()
{
bool dataWasWritten = false;
- while (!writeBuffer.isEmpty() && writeToSocket())
+ while (!allWriteBuffersEmpty() && writeToSocket())
dataWasWritten = true;
return dataWasWritten;
@@ -918,6 +916,8 @@ void QAbstractSocketPrivate::resolveProxy(const QString &hostname, quint16 port)
QNetworkProxyQuery query(hostname, port, QString(),
socketType == QAbstractSocket::TcpSocket ?
QNetworkProxyQuery::TcpSocket :
+ socketType == QAbstractSocket::SctpSocket ?
+ QNetworkProxyQuery::SctpSocket :
QNetworkProxyQuery::UdpSocket);
proxies = QNetworkProxyFactory::proxyForQuery(query);
}
@@ -932,6 +932,10 @@ void QAbstractSocketPrivate::resolveProxy(const QString &hostname, quint16 port)
(p.capabilities() & QNetworkProxy::TunnelingCapability) == 0)
continue;
+ if (socketType == QAbstractSocket::SctpSocket &&
+ (p.capabilities() & QNetworkProxy::SctpTunnelingCapability) == 0)
+ continue;
+
proxyInUse = p;
return;
}
@@ -1150,12 +1154,10 @@ void QAbstractSocketPrivate::_q_connectToNextAddress()
*/
void QAbstractSocketPrivate::_q_testConnection()
{
- if (socketEngine) {
- if (threadData->hasEventDispatcher()) {
- if (connectTimer)
- connectTimer->stop();
- }
+ if (connectTimer)
+ connectTimer->stop();
+ if (socketEngine) {
if (socketEngine->state() == QAbstractSocket::ConnectedState) {
// Fetch the parameters if our connection is completed;
// otherwise, fall out and try the next address.
@@ -1172,11 +1174,6 @@ void QAbstractSocketPrivate::_q_testConnection()
addresses.clear();
}
- if (threadData->hasEventDispatcher()) {
- if (connectTimer)
- connectTimer->stop();
- }
-
#if defined(QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocketPrivate::_q_testConnection() connection failed,"
" checking for alternative addresses");
@@ -1291,18 +1288,36 @@ bool QAbstractSocketPrivate::readFromSocket()
/*! \internal
- Prevents from the recursive readyRead() emission.
+ Emits readyRead(), protecting against recursion.
*/
-void QAbstractSocketPrivate::emitReadyRead()
+void QAbstractSocketPrivate::emitReadyRead(int channel)
{
Q_Q(QAbstractSocket);
// Only emit readyRead() when not recursing.
- if (!emittedReadyRead) {
+ if (!emittedReadyRead && channel == currentReadChannel) {
QScopedValueRollback<bool> r(emittedReadyRead);
emittedReadyRead = true;
emit q->readyRead();
}
- emit q->channelReadyRead(0);
+ // channelReadyRead() can be emitted recursively - even for the same channel.
+ emit q->channelReadyRead(channel);
+}
+
+/*! \internal
+
+ Emits bytesWritten(), protecting against recursion.
+*/
+void QAbstractSocketPrivate::emitBytesWritten(qint64 bytes, int channel)
+{
+ Q_Q(QAbstractSocket);
+ // Only emit bytesWritten() when not recursing.
+ if (!emittedBytesWritten && channel == currentWriteChannel) {
+ QScopedValueRollback<bool> r(emittedBytesWritten);
+ emittedBytesWritten = true;
+ emit q->bytesWritten(bytes);
+ }
+ // channelBytesWritten() can be emitted recursively - even for the same channel.
+ emit q->channelBytesWritten(channel, bytes);
}
/*! \internal
@@ -1413,8 +1428,8 @@ QAbstractSocket::QAbstractSocket(SocketType socketType,
Q_D(QAbstractSocket);
#if defined(QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocket::QAbstractSocket(%sSocket, QAbstractSocketPrivate == %p, parent == %p)",
- socketType == TcpSocket ? "Tcp" : socketType == UdpSocket
- ? "Udp" : "Unknown", &dd, parent);
+ socketType == TcpSocket ? "Tcp" : socketType == UdpSocket ? "Udp"
+ : socketType == SctpSocket ? "Sctp" : "Unknown", &dd, parent);
#endif
d->socketType = socketType;
}
@@ -1678,9 +1693,9 @@ void QAbstractSocket::connectToHost(const QString &hostName, quint16 port,
#endif
if (openMode & QIODevice::Unbuffered)
- d->isBuffered = false; // Unbuffered QTcpSocket
+ d->isBuffered = false;
else if (!d_func()->isBuffered)
- openMode |= QAbstractSocket::Unbuffered; // QUdpSocket
+ openMode |= QAbstractSocket::Unbuffered;
QIODevice::open(openMode);
d->readChannelCount = d->writeChannelCount = 0;
@@ -2385,11 +2400,6 @@ void QAbstractSocket::abort()
return;
}
#endif
- if (d->connectTimer) {
- d->connectTimer->stop();
- delete d->connectTimer;
- d->connectTimer = 0;
- }
d->abortCalled = true;
close();
@@ -2436,15 +2446,7 @@ bool QAbstractSocket::atEnd() const
// Note! docs copied to QSslSocket::flush()
bool QAbstractSocket::flush()
{
- Q_D(QAbstractSocket);
-#ifndef QT_NO_SSL
- // Manual polymorphism; flush() isn't virtual, but QSslSocket overloads
- // it.
- if (QSslSocket *socket = qobject_cast<QSslSocket *>(this))
- return socket->flush();
-#endif
- Q_CHECK_SOCKETENGINE(false);
- return d->flush();
+ return d_func()->flush();
}
/*! \reimp
@@ -2467,8 +2469,9 @@ qint64 QAbstractSocket::readData(char *data, qint64 maxSize)
d->setError(d->socketEngine->error(), d->socketEngine->errorString());
d->resetSocketLayer();
d->state = QAbstractSocket::UnconnectedState;
- } else if (!d->socketEngine->isReadNotificationEnabled()) {
+ } else {
// Only do this when there was no error
+ d->hasPendingData = false;
d->socketEngine->setReadNotificationEnabled(true);
}
@@ -2528,10 +2531,8 @@ qint64 QAbstractSocket::writeData(const char *data, qint64 size)
qt_prettyDebug(data, qMin((int)size, 32), size).data(),
size, written);
#endif
- if (written >= 0) {
- emit bytesWritten(written);
- emit channelBytesWritten(0, written);
- }
+ if (written >= 0)
+ d->emitBytesWritten(written);
return written;
}
@@ -2731,14 +2732,14 @@ void QAbstractSocket::disconnectFromHost()
}
// Wait for pending data to be written.
- if (d->socketEngine && d->socketEngine->isValid() && (d->writeBuffer.size() > 0
+ if (d->socketEngine && d->socketEngine->isValid() && (!d->allWriteBuffersEmpty()
|| d->socketEngine->bytesToWrite() > 0)) {
// hack: when we are waiting for the socket engine to write bytes (only
// possible when using Socks5 or HTTP socket engine), then close
// anyway after 2 seconds. This is to prevent a timeout on Mac, where we
// sometimes just did not get the write notifier from the underlying
// CFSocket and no progress was made.
- if (d->writeBuffer.size() == 0 && d->socketEngine->bytesToWrite() > 0) {
+ if (d->allWriteBuffersEmpty() && d->socketEngine->bytesToWrite() > 0) {
if (!d->disconnectTimer) {
d->disconnectTimer = new QTimer(this);
connect(d->disconnectTimer, SIGNAL(timeout()), this,
@@ -2827,12 +2828,12 @@ void QAbstractSocket::setReadBufferSize(qint64 size)
if (d->readBufferMaxSize == size)
return;
d->readBufferMaxSize = size;
- if (!d->emittedReadyRead && d->socketEngine) {
- // ensure that the read notification is enabled if we've now got
- // room in the read buffer
- // but only if we're not inside canReadNotification -- that will take care on its own
- if ((size == 0 || d->buffer.size() < size) && d->state == QAbstractSocket::ConnectedState) // Do not change the notifier unless we are connected.
- d->socketEngine->setReadNotificationEnabled(true);
+
+ // Do not change the notifier unless we are connected.
+ if (d->socketEngine && d->state == QAbstractSocket::ConnectedState) {
+ // Ensure that the read notification is enabled if we've now got
+ // room in the read buffer.
+ d->socketEngine->setReadNotificationEnabled(size == 0 || d->buffer.size() < size);
}
}
diff --git a/src/network/socket/qabstractsocket.h b/src/network/socket/qabstractsocket.h
index aabdcd4776..73a8f11537 100644
--- a/src/network/socket/qabstractsocket.h
+++ b/src/network/socket/qabstractsocket.h
@@ -40,6 +40,7 @@
#ifndef QABSTRACTSOCKET_H
#define QABSTRACTSOCKET_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qiodevice.h>
#include <QtCore/qobject.h>
#ifndef QT_NO_DEBUG_STREAM
@@ -63,6 +64,7 @@ public:
enum SocketType {
TcpSocket,
UdpSocket,
+ SctpSocket,
UnknownSocketType = -1
};
Q_ENUM(SocketType)
diff --git a/src/network/socket/qabstractsocket_p.h b/src/network/socket/qabstractsocket_p.h
index b718c21ff5..1578d7bb35 100644
--- a/src/network/socket/qabstractsocket_p.h
+++ b/src/network/socket/qabstractsocket_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "QtNetwork/qabstractsocket.h"
#include "QtCore/qbytearray.h"
#include "QtCore/qlist.h"
@@ -85,7 +86,7 @@ public:
virtual bool bind(const QHostAddress &address, quint16 port, QAbstractSocket::BindMode mode);
- bool canReadNotification();
+ virtual bool canReadNotification();
bool canWriteNotification();
void canCloseNotification();
@@ -128,22 +129,23 @@ public:
inline void resolveProxy(quint16 port) { resolveProxy(QString(), port); }
void resetSocketLayer();
- bool flush();
+ virtual bool flush();
bool initSocketLayer(QAbstractSocket::NetworkLayerProtocol protocol);
virtual void configureCreatedSocket();
void startConnectingByName(const QString &host);
void fetchConnectionParameters();
- void setupSocketNotifiers();
bool readFromSocket();
- bool writeToSocket();
- void emitReadyRead();
+ virtual bool writeToSocket();
+ void emitReadyRead(int channel = 0);
+ void emitBytesWritten(qint64 bytes, int channel = 0);
void setError(QAbstractSocket::SocketError errorCode, const QString &errorString);
void setErrorAndEmit(QAbstractSocket::SocketError errorCode, const QString &errorString);
qint64 readBufferMaxSize;
bool isBuffered;
+ bool hasPendingData;
QTimer *connectTimer;
QTimer *disconnectTimer;
diff --git a/src/network/socket/qabstractsocketengine_p.h b/src/network/socket/qabstractsocketengine_p.h
index 0073a8b1f2..0cb519ce90 100644
--- a/src/network/socket/qabstractsocketengine_p.h
+++ b/src/network/socket/qabstractsocketengine_p.h
@@ -52,6 +52,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "QtNetwork/qhostaddress.h"
#include "QtNetwork/qabstractsocket.h"
#include "private/qobject_p.h"
@@ -103,7 +104,8 @@ public:
MulticastLoopbackOption,
TypeOfServiceOption,
ReceivePacketInformation,
- ReceiveHopLimit
+ ReceiveHopLimit,
+ MaxStreamsSocketOption
};
enum PacketHeaderOption {
@@ -111,6 +113,8 @@ public:
WantDatagramSender = 0x01,
WantDatagramDestination = 0x02,
WantDatagramHopLimit = 0x04,
+ WantStreamNumber = 0x08,
+ WantEndOfRecord = 0x10,
WantAll = 0xff
};
@@ -146,13 +150,13 @@ public:
virtual bool setMulticastInterface(const QNetworkInterface &iface) = 0;
#endif // QT_NO_NETWORKINTERFACE
- virtual qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader *header = 0,
- PacketHeaderOptions = WantNone) = 0;
- virtual qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &header) = 0;
virtual bool hasPendingDatagrams() const = 0;
virtual qint64 pendingDatagramSize() const = 0;
#endif // QT_NO_UDPSOCKET
+ virtual qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader *header = 0,
+ PacketHeaderOptions = WantNone) = 0;
+ virtual qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &header) = 0;
virtual qint64 bytesToWrite() const = 0;
virtual int option(SocketOption option) const = 0;
diff --git a/src/network/socket/qhttpsocketengine.cpp b/src/network/socket/qhttpsocketengine.cpp
index f9ff958525..899c02fba6 100644
--- a/src/network/socket/qhttpsocketengine.cpp
+++ b/src/network/socket/qhttpsocketengine.cpp
@@ -289,34 +289,34 @@ bool QHttpSocketEngine::setMulticastInterface(const QNetworkInterface &)
}
#endif // QT_NO_NETWORKINTERFACE
-qint64 QHttpSocketEngine::readDatagram(char *, qint64, QIpPacketHeader *, PacketHeaderOptions)
+bool QHttpSocketEngine::hasPendingDatagrams() const
{
qWarning("Operation is not supported");
- setError(QAbstractSocket::UnsupportedSocketOperationError,
- QLatin1String("Unsupported socket operation"));
- return -1;
+ return false;
}
-qint64 QHttpSocketEngine::writeDatagram(const char *, qint64, const QIpPacketHeader &)
+qint64 QHttpSocketEngine::pendingDatagramSize() const
{
qWarning("Operation is not supported");
- setError(QAbstractSocket::UnsupportedSocketOperationError,
- QLatin1String("Unsupported socket operation"));
return -1;
}
+#endif // QT_NO_UDPSOCKET
-bool QHttpSocketEngine::hasPendingDatagrams() const
+qint64 QHttpSocketEngine::readDatagram(char *, qint64, QIpPacketHeader *, PacketHeaderOptions)
{
qWarning("Operation is not supported");
- return false;
+ setError(QAbstractSocket::UnsupportedSocketOperationError,
+ QLatin1String("Unsupported socket operation"));
+ return -1;
}
-qint64 QHttpSocketEngine::pendingDatagramSize() const
+qint64 QHttpSocketEngine::writeDatagram(const char *, qint64, const QIpPacketHeader &)
{
qWarning("Operation is not supported");
+ setError(QAbstractSocket::UnsupportedSocketOperationError,
+ QLatin1String("Unsupported socket operation"));
return -1;
}
-#endif // QT_NO_UDPSOCKET
qint64 QHttpSocketEngine::bytesToWrite() const
{
diff --git a/src/network/socket/qhttpsocketengine_p.h b/src/network/socket/qhttpsocketengine_p.h
index 1cc168afec..07815a7e51 100644
--- a/src/network/socket/qhttpsocketengine_p.h
+++ b/src/network/socket/qhttpsocketengine_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "private/qabstractsocketengine_p.h"
#include "qabstractsocket.h"
#include "qnetworkproxy.h"
@@ -111,13 +112,13 @@ public:
bool setMulticastInterface(const QNetworkInterface &iface) Q_DECL_OVERRIDE;
#endif // QT_NO_NETWORKINTERFACE
- qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader *,
- PacketHeaderOptions) Q_DECL_OVERRIDE;
- qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &) Q_DECL_OVERRIDE;
bool hasPendingDatagrams() const Q_DECL_OVERRIDE;
qint64 pendingDatagramSize() const Q_DECL_OVERRIDE;
#endif // QT_NO_UDPSOCKET
+ qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader *,
+ PacketHeaderOptions) Q_DECL_OVERRIDE;
+ qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &) Q_DECL_OVERRIDE;
qint64 bytesToWrite() const Q_DECL_OVERRIDE;
int option(SocketOption option) const Q_DECL_OVERRIDE;
diff --git a/src/network/socket/qlocalserver.h b/src/network/socket/qlocalserver.h
index 52f0813746..786885b6cd 100644
--- a/src/network/socket/qlocalserver.h
+++ b/src/network/socket/qlocalserver.h
@@ -40,6 +40,7 @@
#ifndef QLOCALSERVER_H
#define QLOCALSERVER_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtNetwork/qabstractsocket.h>
QT_BEGIN_NAMESPACE
diff --git a/src/network/socket/qlocalserver_p.h b/src/network/socket/qlocalserver_p.h
index 28d2b08902..988140c1a4 100644
--- a/src/network/socket/qlocalserver_p.h
+++ b/src/network/socket/qlocalserver_p.h
@@ -51,6 +51,8 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
#ifndef QT_NO_LOCALSERVER
#include "qlocalserver.h"
diff --git a/src/network/socket/qlocalsocket.h b/src/network/socket/qlocalsocket.h
index 0eecab206b..ea074db90e 100644
--- a/src/network/socket/qlocalsocket.h
+++ b/src/network/socket/qlocalsocket.h
@@ -40,6 +40,7 @@
#ifndef QLOCALSOCKET_H
#define QLOCALSOCKET_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qiodevice.h>
#include <QtNetwork/qabstractsocket.h>
diff --git a/src/network/socket/qlocalsocket_p.h b/src/network/socket/qlocalsocket_p.h
index 56f8b590f1..560d74328e 100644
--- a/src/network/socket/qlocalsocket_p.h
+++ b/src/network/socket/qlocalsocket_p.h
@@ -51,6 +51,8 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
#ifndef QT_NO_LOCALSOCKET
#include "qlocalsocket.h"
diff --git a/src/network/socket/qnativesocketengine.cpp b/src/network/socket/qnativesocketengine.cpp
index 9ba9d5317e..f2bc3cec94 100644
--- a/src/network/socket/qnativesocketengine.cpp
+++ b/src/network/socket/qnativesocketengine.cpp
@@ -111,7 +111,7 @@
\value WantAll this is a catch-all value to indicate the caller is
interested in all the available information
- \sa readDatagram()
+ \sa readDatagram(), QNetworkDatagram
*/
#include "qnativesocketengine_p.h"
@@ -174,6 +174,12 @@ QT_BEGIN_NAMESPACE
" socket other than "#type""); \
return (returnValue); \
} } while (0)
+#define Q_CHECK_TYPES(function, type1, type2, returnValue) do { \
+ if (d->socketType != (type1) && d->socketType != (type2)) { \
+ qWarning(#function" was called by a" \
+ " socket other than "#type1" or "#type2); \
+ return (returnValue); \
+ } } while (0)
#define Q_TR(a) QT_TRANSLATE_NOOP(QNativeSocketEngine, a)
/*! \internal
@@ -417,6 +423,7 @@ bool QNativeSocketEngine::initialize(QAbstractSocket::SocketType socketType, QAb
QString typeStr = QLatin1String("UnknownSocketType");
if (socketType == QAbstractSocket::TcpSocket) typeStr = QLatin1String("TcpSocket");
else if (socketType == QAbstractSocket::UdpSocket) typeStr = QLatin1String("UdpSocket");
+ else if (socketType == QAbstractSocket::SctpSocket) typeStr = QLatin1String("SctpSocket");
QString protocolStr = QLatin1String("UnknownProtocol");
if (protocol == QAbstractSocket::IPv4Protocol) protocolStr = QLatin1String("IPv4Protocol");
else if (protocol == QAbstractSocket::IPv6Protocol) protocolStr = QLatin1String("IPv6Protocol");
@@ -659,7 +666,12 @@ bool QNativeSocketEngine::listen()
Q_D(QNativeSocketEngine);
Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::listen(), false);
Q_CHECK_STATE(QNativeSocketEngine::listen(), QAbstractSocket::BoundState, false);
+#ifndef QT_NO_SCTP
+ Q_CHECK_TYPES(QNativeSocketEngine::listen(), QAbstractSocket::TcpSocket,
+ QAbstractSocket::SctpSocket, false);
+#else
Q_CHECK_TYPE(QNativeSocketEngine::listen(), QAbstractSocket::TcpSocket, false);
+#endif
// We're using a backlog of 50. Most modern kernels support TCP
// syncookies by default, and if they do, the backlog is ignored.
@@ -680,7 +692,12 @@ int QNativeSocketEngine::accept()
Q_D(QNativeSocketEngine);
Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::accept(), -1);
Q_CHECK_STATE(QNativeSocketEngine::accept(), QAbstractSocket::ListeningState, -1);
+#ifndef QT_NO_SCTP
+ Q_CHECK_TYPES(QNativeSocketEngine::accept(), QAbstractSocket::TcpSocket,
+ QAbstractSocket::SctpSocket, -1);
+#else
Q_CHECK_TYPE(QNativeSocketEngine::accept(), QAbstractSocket::TcpSocket, -1);
+#endif
return d->nativeAccept();
}
@@ -793,6 +810,7 @@ qint64 QNativeSocketEngine::pendingDatagramSize() const
return d->nativePendingDatagramSize();
}
+#endif // QT_NO_UDPSOCKET
/*!
Reads up to \a maxSize bytes of a datagram from the socket,
@@ -800,9 +818,10 @@ qint64 QNativeSocketEngine::pendingDatagramSize() const
address, port, and other IP header fields are stored in \a header
according to the request in \a options.
- To avoid unnecessarily loss of data, call pendingDatagramSize() to
- determine the size of the pending message before reading it. If \a
- maxSize is too small, the rest of the datagram will be lost.
+ For UDP sockets, to avoid unnecessarily loss of data, call
+ pendingDatagramSize() to determine the size of the pending message
+ before reading it. If \a maxSize is too small, the rest of the
+ datagram will be lost.
Returns -1 if an error occurred.
@@ -813,13 +832,14 @@ qint64 QNativeSocketEngine::readDatagram(char *data, qint64 maxSize, QIpPacketHe
{
Q_D(QNativeSocketEngine);
Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::readDatagram(), -1);
- Q_CHECK_TYPE(QNativeSocketEngine::readDatagram(), QAbstractSocket::UdpSocket, -1);
+ Q_CHECK_STATES(QNativeSocketEngine::readDatagram(), QAbstractSocket::BoundState,
+ QAbstractSocket::ConnectedState, -1);
return d->nativeReceiveDatagram(data, maxSize, header, options);
}
/*!
- Writes a UDP datagram of size \a size bytes to the socket from
+ Writes a datagram of size \a size bytes to the socket from
\a data to the destination contained in \a header, and returns the
number of bytes written, or -1 if an error occurred. If \a header
contains other settings like hop limit or source address, this function
@@ -844,11 +864,11 @@ qint64 QNativeSocketEngine::writeDatagram(const char *data, qint64 size, const Q
{
Q_D(QNativeSocketEngine);
Q_CHECK_VALID_SOCKETLAYER(QNativeSocketEngine::writeDatagram(), -1);
- Q_CHECK_TYPE(QNativeSocketEngine::writeDatagram(), QAbstractSocket::UdpSocket, -1);
+ Q_CHECK_STATES(QNativeSocketEngine::writeDatagram(), QAbstractSocket::BoundState,
+ QAbstractSocket::ConnectedState, -1);
return d->nativeSendDatagram(data, size, header);
}
-#endif // QT_NO_UDPSOCKET
/*!
Writes a block of \a size bytes from \a data to the socket.
@@ -881,7 +901,11 @@ qint64 QNativeSocketEngine::read(char *data, qint64 maxSize)
qint64 readBytes = d->nativeRead(data, maxSize);
// Handle remote close
- if (readBytes == 0 && d->socketType == QAbstractSocket::TcpSocket) {
+ if (readBytes == 0 && (d->socketType == QAbstractSocket::TcpSocket
+#ifndef QT_NO_SCTP
+ || d->socketType == QAbstractSocket::SctpSocket
+#endif
+ )) {
d->setError(QAbstractSocket::RemoteHostClosedError,
QNativeSocketEnginePrivate::RemoteHostClosedErrorString);
close();
@@ -1007,26 +1031,28 @@ bool QNativeSocketEngine::waitForWrite(int msecs, bool *timedOut)
// select(writable) is successful. In this case we should not
// issue a second call to WSAConnect()
#if defined (Q_OS_WIN)
- if (ret > 0) {
- setState(QAbstractSocket::ConnectedState);
- d_func()->fetchConnectionParameters();
- return true;
- } else {
- int value = 0;
- int valueSize = sizeof(value);
- if (::getsockopt(d->socketDescriptor, SOL_SOCKET, SO_ERROR, (char *) &value, &valueSize) == 0) {
- if (value == WSAECONNREFUSED) {
- d->setError(QAbstractSocket::ConnectionRefusedError, QNativeSocketEnginePrivate::ConnectionRefusedErrorString);
- d->socketState = QAbstractSocket::UnconnectedState;
- return false;
- } else if (value == WSAETIMEDOUT) {
- d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::ConnectionTimeOutErrorString);
- d->socketState = QAbstractSocket::UnconnectedState;
- return false;
- } else if (value == WSAEHOSTUNREACH) {
- d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::HostUnreachableErrorString);
- d->socketState = QAbstractSocket::UnconnectedState;
- return false;
+ if (state() == QAbstractSocket::ConnectingState) {
+ if (ret > 0) {
+ setState(QAbstractSocket::ConnectedState);
+ d_func()->fetchConnectionParameters();
+ return true;
+ } else {
+ int value = 0;
+ int valueSize = sizeof(value);
+ if (::getsockopt(d->socketDescriptor, SOL_SOCKET, SO_ERROR, (char *) &value, &valueSize) == 0) {
+ if (value == WSAECONNREFUSED) {
+ d->setError(QAbstractSocket::ConnectionRefusedError, QNativeSocketEnginePrivate::ConnectionRefusedErrorString);
+ d->socketState = QAbstractSocket::UnconnectedState;
+ return false;
+ } else if (value == WSAETIMEDOUT) {
+ d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::ConnectionTimeOutErrorString);
+ d->socketState = QAbstractSocket::UnconnectedState;
+ return false;
+ } else if (value == WSAEHOSTUNREACH) {
+ d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::HostUnreachableErrorString);
+ d->socketState = QAbstractSocket::UnconnectedState;
+ return false;
+ }
}
}
}
@@ -1060,26 +1086,28 @@ bool QNativeSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWri
// select(writable) is successful. In this case we should not
// issue a second call to WSAConnect()
#if defined (Q_OS_WIN)
- if (checkWrite && ((readyToWrite && *readyToWrite) || !readyToWrite) && ret > 0) {
- setState(QAbstractSocket::ConnectedState);
- d_func()->fetchConnectionParameters();
- return true;
- } else {
- int value = 0;
- int valueSize = sizeof(value);
- if (::getsockopt(d->socketDescriptor, SOL_SOCKET, SO_ERROR, (char *) &value, &valueSize) == 0) {
- if (value == WSAECONNREFUSED) {
- d->setError(QAbstractSocket::ConnectionRefusedError, QNativeSocketEnginePrivate::ConnectionRefusedErrorString);
- d->socketState = QAbstractSocket::UnconnectedState;
- return false;
- } else if (value == WSAETIMEDOUT) {
- d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::ConnectionTimeOutErrorString);
- d->socketState = QAbstractSocket::UnconnectedState;
- return false;
- } else if (value == WSAEHOSTUNREACH) {
- d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::HostUnreachableErrorString);
- d->socketState = QAbstractSocket::UnconnectedState;
- return false;
+ if (state() == QAbstractSocket::ConnectingState) {
+ if (checkWrite && ((readyToWrite && *readyToWrite) || !readyToWrite) && ret > 0) {
+ setState(QAbstractSocket::ConnectedState);
+ d_func()->fetchConnectionParameters();
+ return true;
+ } else {
+ int value = 0;
+ int valueSize = sizeof(value);
+ if (::getsockopt(d->socketDescriptor, SOL_SOCKET, SO_ERROR, (char *) &value, &valueSize) == 0) {
+ if (value == WSAECONNREFUSED) {
+ d->setError(QAbstractSocket::ConnectionRefusedError, QNativeSocketEnginePrivate::ConnectionRefusedErrorString);
+ d->socketState = QAbstractSocket::UnconnectedState;
+ return false;
+ } else if (value == WSAETIMEDOUT) {
+ d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::ConnectionTimeOutErrorString);
+ d->socketState = QAbstractSocket::UnconnectedState;
+ return false;
+ } else if (value == WSAEHOSTUNREACH) {
+ d->setError(QAbstractSocket::NetworkError, QNativeSocketEnginePrivate::HostUnreachableErrorString);
+ d->socketState = QAbstractSocket::UnconnectedState;
+ return false;
+ }
}
}
}
diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h
index 19e9e1d9b7..46c7ae5c55 100644
--- a/src/network/socket/qnativesocketengine_p.h
+++ b/src/network/socket/qnativesocketengine_p.h
@@ -45,12 +45,14 @@
// W A R N I N G
// -------------
//
-// This file is not part of the Qt API. It exists for the convenience
-// of the QLibrary class. This header file may change from
-// version to version without notice, or even be removed.
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
//
// We mean it.
//
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "QtNetwork/qhostaddress.h"
#include "QtNetwork/qnetworkinterface.h"
#include "private/qabstractsocketengine_p.h"
@@ -66,21 +68,20 @@
QT_BEGIN_NAMESPACE
#ifdef Q_OS_WIN
-#define QT_SOCKLEN_T int
-#define QT_SOCKOPTLEN_T int
+# define QT_SOCKLEN_T int
+# define QT_SOCKOPTLEN_T int
// The following definitions are copied from the MinGW header mswsock.h which
// was placed in the public domain. The WSASendMsg and WSARecvMsg functions
// were introduced with Windows Vista, so some Win32 headers are lacking them.
// There are no known versions of Windows CE or Embedded that contain them.
-#ifndef Q_OS_WINCE
# ifndef WSAID_WSARECVMSG
typedef INT (WINAPI *LPFN_WSARECVMSG)(SOCKET s, LPWSAMSG lpMsg,
LPDWORD lpdwNumberOfBytesRecvd,
LPWSAOVERLAPPED lpOverlapped,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
# define WSAID_WSARECVMSG {0xf689d7c8,0x6f1f,0x436b,{0x8a,0x53,0xe5,0x4f,0xe3,0x51,0xc3,0x22}}
-# endif
+# endif // !WSAID_WSARECVMSG
# ifndef WSAID_WSASENDMSG
typedef struct {
LPWSAMSG lpMsg;
@@ -96,9 +97,8 @@ typedef INT (WSAAPI *LPFN_WSASENDMSG)(SOCKET s, LPWSAMSG lpMsg, DWORD dwFlags,
LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine);
# define WSAID_WSASENDMSG {0xa441e712,0x754f,0x43ca,{0x84,0xa7,0x0d,0xee,0x44,0xcf,0x60,0x6d}}
-# endif
-#endif
-#endif
+# endif // !WSAID_WSASENDMSG
+#endif // Q_OS_WIN
union qt_sockaddr {
sockaddr a;
@@ -157,13 +157,13 @@ public:
bool setMulticastInterface(const QNetworkInterface &iface) Q_DECL_OVERRIDE;
#endif
- qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader * = 0,
- PacketHeaderOptions = WantNone) Q_DECL_OVERRIDE;
- qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &) Q_DECL_OVERRIDE;
bool hasPendingDatagrams() const Q_DECL_OVERRIDE;
qint64 pendingDatagramSize() const Q_DECL_OVERRIDE;
#endif // QT_NO_UDPSOCKET
+ qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader * = 0,
+ PacketHeaderOptions = WantNone) Q_DECL_OVERRIDE;
+ qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &) Q_DECL_OVERRIDE;
qint64 bytesToWrite() const Q_DECL_OVERRIDE;
qint64 receiveBufferSize() const;
@@ -210,7 +210,7 @@ public:
QSocketNotifier *readNotifier, *writeNotifier, *exceptNotifier;
-#if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
+#if defined(Q_OS_WIN)
LPFN_WSASENDMSG sendmsg;
LPFN_WSARECVMSG recvmsg;
# endif
diff --git a/src/network/socket/qnativesocketengine_unix.cpp b/src/network/socket/qnativesocketengine_unix.cpp
index 2d50cc1f8d..d1efc21e09 100644
--- a/src/network/socket/qnativesocketengine_unix.cpp
+++ b/src/network/socket/qnativesocketengine_unix.cpp
@@ -68,6 +68,11 @@
#endif
#include <netinet/tcp.h>
+#ifndef QT_NO_SCTP
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/sctp.h>
+#endif
QT_BEGIN_NAMESPACE
@@ -142,6 +147,7 @@ static void convertToLevelAndOption(QNativeSocketEngine::SocketOption opt,
switch (opt) {
case QNativeSocketEngine::NonBlockingSocketOption: // fcntl, not setsockopt
case QNativeSocketEngine::BindExclusively: // not handled on Unix
+ case QNativeSocketEngine::MaxStreamsSocketOption:
Q_UNREACHABLE();
case QNativeSocketEngine::BroadcastSocketOption:
@@ -229,13 +235,28 @@ static void convertToLevelAndOption(QNativeSocketEngine::SocketOption opt,
bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType socketType,
QAbstractSocket::NetworkLayerProtocol &socketProtocol)
{
- int protocol = (socketProtocol == QAbstractSocket::IPv6Protocol || socketProtocol == QAbstractSocket::AnyIPProtocol) ? AF_INET6 : AF_INET;
+#ifndef QT_NO_SCTP
+ int protocol = (socketType == QAbstractSocket::SctpSocket) ? IPPROTO_SCTP : 0;
+#else
+ if (socketType == QAbstractSocket::SctpSocket) {
+ setError(QAbstractSocket::UnsupportedSocketOperationError,
+ ProtocolUnsupportedErrorString);
+#if defined (QNATIVESOCKETENGINE_DEBUG)
+ qDebug("QNativeSocketEnginePrivate::createNewSocket(%d, %d): unsupported protocol",
+ socketType, socketProtocol);
+#endif
+ return false;
+ }
+ int protocol = 0;
+#endif // QT_NO_SCTP
+ int domain = (socketProtocol == QAbstractSocket::IPv6Protocol
+ || socketProtocol == QAbstractSocket::AnyIPProtocol) ? AF_INET6 : AF_INET;
int type = (socketType == QAbstractSocket::UdpSocket) ? SOCK_DGRAM : SOCK_STREAM;
- int socket = qt_safe_socket(protocol, type, 0, O_NONBLOCK);
+ int socket = qt_safe_socket(domain, type, protocol, O_NONBLOCK);
if (socket < 0 && socketProtocol == QAbstractSocket::AnyIPProtocol && errno == EAFNOSUPPORT) {
- protocol = AF_INET;
- socket = qt_safe_socket(protocol, type, 0, O_NONBLOCK);
+ domain = AF_INET;
+ socket = qt_safe_socket(domain, type, protocol, O_NONBLOCK);
socketProtocol = QAbstractSocket::IPv4Protocol;
}
@@ -291,10 +312,26 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co
if (!q->isValid())
return -1;
- // handle non-getsockopt cases first
- if (opt == QNativeSocketEngine::BindExclusively || opt == QNativeSocketEngine::NonBlockingSocketOption
- || opt == QNativeSocketEngine::BroadcastSocketOption)
+ // handle non-getsockopt and specific cases first
+ switch (opt) {
+ case QNativeSocketEngine::BindExclusively:
+ case QNativeSocketEngine::NonBlockingSocketOption:
+ case QNativeSocketEngine::BroadcastSocketOption:
+ return -1;
+ case QNativeSocketEngine::MaxStreamsSocketOption: {
+#ifndef QT_NO_SCTP
+ sctp_initmsg sctpInitMsg;
+ QT_SOCKOPTLEN_T sctpInitMsgSize = sizeof(sctpInitMsg);
+ if (::getsockopt(socketDescriptor, SOL_SCTP, SCTP_INITMSG, &sctpInitMsg,
+ &sctpInitMsgSize) == 0)
+ return int(qMin(sctpInitMsg.sinit_num_ostreams, sctpInitMsg.sinit_max_instreams));
+#endif
return -1;
+ }
+
+ default:
+ break;
+ }
int n, level;
int v = -1;
@@ -317,7 +354,7 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt
if (!q->isValid())
return false;
- // handle non-setsockopt cases first
+ // handle non-setsockopt and specific cases first
switch (opt) {
case QNativeSocketEngine::NonBlockingSocketOption: {
// Make the socket nonblocking.
@@ -351,6 +388,20 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt
case QNativeSocketEngine::BindExclusively:
return true;
+ case QNativeSocketEngine::MaxStreamsSocketOption: {
+#ifndef QT_NO_SCTP
+ sctp_initmsg sctpInitMsg;
+ QT_SOCKOPTLEN_T sctpInitMsgSize = sizeof(sctpInitMsg);
+ if (::getsockopt(socketDescriptor, SOL_SCTP, SCTP_INITMSG, &sctpInitMsg,
+ &sctpInitMsgSize) == 0) {
+ sctpInitMsg.sinit_num_ostreams = sctpInitMsg.sinit_max_instreams = uint16_t(v);
+ return ::setsockopt(socketDescriptor, SOL_SCTP, SCTP_INITMSG, &sctpInitMsg,
+ sctpInitMsgSize) == 0;
+ }
+#endif
+ return false;
+ }
+
default:
break;
}
@@ -830,6 +881,9 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS
#if !defined(IP_PKTINFO) && defined(IP_RECVIF) && defined(Q_OS_BSD4)
+ CMSG_SPACE(sizeof(sockaddr_dl))
#endif
+#ifndef QT_NO_SCTP
+ + CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))
+#endif
+ sizeof(quintptr) - 1) / sizeof(quintptr)];
struct msghdr msg;
@@ -848,7 +902,8 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS
msg.msg_name = &aa;
msg.msg_namelen = sizeof(aa);
}
- if (options & (QAbstractSocketEngine::WantDatagramHopLimit | QAbstractSocketEngine::WantDatagramDestination)) {
+ if (options & (QAbstractSocketEngine::WantDatagramHopLimit | QAbstractSocketEngine::WantDatagramDestination
+ | QAbstractSocketEngine::WantStreamNumber)) {
msg.msg_control = cbuf;
msg.msg_controllen = sizeof(cbuf);
}
@@ -859,13 +914,27 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS
} while (recvResult == -1 && errno == EINTR);
if (recvResult == -1) {
- setError(QAbstractSocket::NetworkError, ReceiveDatagramErrorString);
+ switch (errno) {
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ case EAGAIN:
+ // No datagram was available for reading
+ recvResult = -2;
+ break;
+ case ECONNREFUSED:
+ setError(QAbstractSocket::ConnectionRefusedError, ConnectionRefusedErrorString);
+ break;
+ default:
+ setError(QAbstractSocket::NetworkError, ReceiveDatagramErrorString);
+ }
if (header)
header->clear();
} else if (options != QAbstractSocketEngine::WantNone) {
Q_ASSERT(header);
qt_socket_getPortAndAddress(&aa, &header->senderPort, &header->senderAddress);
header->destinationPort = localPort;
+ header->endOfRecord = (msg.msg_flags & MSG_EOR) != 0;
// parse the ancillary data
struct cmsghdr *cmsgptr;
@@ -912,6 +981,15 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS
|| (cmsgptr->cmsg_level == IPPROTO_IP && cmsgptr->cmsg_type == IP_TTL))) {
header->hopLimit = *reinterpret_cast<int *>(CMSG_DATA(cmsgptr));
}
+
+#ifndef QT_NO_SCTP
+ if (cmsgptr->cmsg_level == IPPROTO_SCTP && cmsgptr->cmsg_type == SCTP_SNDRCV
+ && cmsgptr->cmsg_len >= CMSG_LEN(sizeof(sctp_sndrcvinfo))) {
+ sctp_sndrcvinfo *rcvInfo = reinterpret_cast<sctp_sndrcvinfo *>(CMSG_DATA(cmsgptr));
+
+ header->streamNumber = int(rcvInfo->sinfo_stream);
+ }
+#endif
}
}
@@ -924,13 +1002,17 @@ qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxS
? header->senderPort : 0, (qint64) recvResult);
#endif
- return qint64(maxSize ? recvResult : recvResult == -1 ? -1 : 0);
+ return qint64((maxSize || recvResult < 0) ? recvResult : Q_INT64_C(0));
}
qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 len, const QIpPacketHeader &header)
{
// we use quintptr to force the alignment
- quintptr cbuf[(CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int)) + sizeof(quintptr) - 1) / sizeof(quintptr)];
+ quintptr cbuf[(CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(sizeof(int))
+#ifndef QT_NO_SCTP
+ + CMSG_SPACE(sizeof(struct sctp_sndrcvinfo))
+#endif
+ + sizeof(quintptr) - 1) / sizeof(quintptr)];
struct cmsghdr *cmsgptr = reinterpret_cast<struct cmsghdr *>(cbuf);
struct msghdr msg;
@@ -943,10 +1025,13 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l
vec.iov_len = len;
msg.msg_iov = &vec;
msg.msg_iovlen = 1;
- msg.msg_name = &aa.a;
msg.msg_control = &cbuf;
- setPortAndAddress(header.destinationPort, header.destinationAddress, &aa, &msg.msg_namelen);
+ if (header.destinationPort != 0) {
+ msg.msg_name = &aa.a;
+ setPortAndAddress(header.destinationPort, header.destinationAddress,
+ &aa, &msg.msg_namelen);
+ }
if (msg.msg_namelen == sizeof(aa.a6)) {
if (header.hopLimit != -1) {
@@ -1001,15 +1086,37 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l
#endif
}
+#ifndef QT_NO_SCTP
+ if (header.streamNumber != -1) {
+ struct sctp_sndrcvinfo *data = reinterpret_cast<sctp_sndrcvinfo *>(CMSG_DATA(cmsgptr));
+ memset(data, 0, sizeof(*data));
+ msg.msg_controllen += CMSG_SPACE(sizeof(sctp_sndrcvinfo));
+ cmsgptr->cmsg_len = CMSG_LEN(sizeof(sctp_sndrcvinfo));
+ cmsgptr->cmsg_level = IPPROTO_SCTP;
+ cmsgptr->cmsg_type = SCTP_SNDRCV;
+ data->sinfo_stream = uint16_t(header.streamNumber);
+ cmsgptr = reinterpret_cast<cmsghdr *>(reinterpret_cast<char *>(cmsgptr) + CMSG_SPACE(sizeof(*data)));
+ }
+#endif
+
if (msg.msg_controllen == 0)
msg.msg_control = 0;
ssize_t sentBytes = qt_safe_sendmsg(socketDescriptor, &msg, 0);
if (sentBytes < 0) {
switch (errno) {
+#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
+ case EWOULDBLOCK:
+#endif
+ case EAGAIN:
+ sentBytes = -2;
+ break;
case EMSGSIZE:
setError(QAbstractSocket::DatagramTooLargeError, DatagramTooLargeErrorString);
break;
+ case ECONNRESET:
+ setError(QAbstractSocket::RemoteHostClosedError, RemoteHostClosedErrorString);
+ break;
default:
setError(QAbstractSocket::NetworkError, SendDatagramErrorString);
}
@@ -1082,21 +1189,51 @@ bool QNativeSocketEnginePrivate::fetchConnectionParameters()
#endif
// Determine the remote address
- if (!::getpeername(socketDescriptor, &sa.a, &sockAddrSize)) {
+ bool connected = ::getpeername(socketDescriptor, &sa.a, &sockAddrSize) == 0;
+ if (connected) {
qt_socket_getPortAndAddress(&sa, &peerPort, &peerAddress);
inboundStreamCount = outboundStreamCount = 1;
}
- // Determine the socket type (UDP/TCP)
+ // Determine the socket type (UDP/TCP/SCTP)
int value = 0;
QT_SOCKOPTLEN_T valueSize = sizeof(int);
if (::getsockopt(socketDescriptor, SOL_SOCKET, SO_TYPE, &value, &valueSize) == 0) {
- if (value == SOCK_STREAM)
- socketType = QAbstractSocket::TcpSocket;
- else if (value == SOCK_DGRAM)
- socketType = QAbstractSocket::UdpSocket;
- else
- socketType = QAbstractSocket::UnknownSocketType;
+ if (value == SOCK_STREAM) {
+#ifndef QT_NO_SCTP
+ if (option(QNativeSocketEngine::MaxStreamsSocketOption) != -1) {
+ socketType = QAbstractSocket::SctpSocket;
+ if (connected) {
+ sctp_status sctpStatus;
+ QT_SOCKOPTLEN_T sctpStatusSize = sizeof(sctpStatus);
+ sctp_event_subscribe sctpEvents;
+
+ memset(&sctpEvents, 0, sizeof(sctpEvents));
+ sctpEvents.sctp_data_io_event = 1;
+ if (::getsockopt(socketDescriptor, SOL_SCTP, SCTP_STATUS, &sctpStatus,
+ &sctpStatusSize) == 0 &&
+ ::setsockopt(socketDescriptor, SOL_SCTP, SCTP_EVENTS, &sctpEvents,
+ sizeof(sctpEvents)) == 0) {
+ inboundStreamCount = int(sctpStatus.sstat_instrms);
+ outboundStreamCount = int(sctpStatus.sstat_outstrms);
+ } else {
+ setError(QAbstractSocket::UnsupportedSocketOperationError,
+ InvalidSocketErrorString);
+ return false;
+ }
+ }
+ } else {
+ socketType = QAbstractSocket::TcpSocket;
+ }
+#else
+ socketType = QAbstractSocket::TcpSocket;
+#endif
+ } else {
+ if (value == SOCK_DGRAM)
+ socketType = QAbstractSocket::UdpSocket;
+ else
+ socketType = QAbstractSocket::UnknownSocketType;
+ }
}
#if defined (QNATIVESOCKETENGINE_DEBUG)
QString socketProtocolStr = QStringLiteral("UnknownProtocol");
@@ -1106,6 +1243,7 @@ bool QNativeSocketEnginePrivate::fetchConnectionParameters()
QString socketTypeStr = QStringLiteral("UnknownSocketType");
if (socketType == QAbstractSocket::TcpSocket) socketTypeStr = QStringLiteral("TcpSocket");
else if (socketType == QAbstractSocket::UdpSocket) socketTypeStr = QStringLiteral("UdpSocket");
+ else if (socketType == QAbstractSocket::SctpSocket) socketTypeStr = QStringLiteral("SctpSocket");
qDebug("QNativeSocketEnginePrivate::fetchConnectionParameters() local == %s:%i,"
" peer == %s:%i, socket == %s - %s, inboundStreamCount == %i, outboundStreamCount == %i",
diff --git a/src/network/socket/qnativesocketengine_win.cpp b/src/network/socket/qnativesocketengine_win.cpp
index 5ffe7b11b8..5a9641a9fe 100644
--- a/src/network/socket/qnativesocketengine_win.cpp
+++ b/src/network/socket/qnativesocketengine_win.cpp
@@ -214,6 +214,7 @@ static void convertToLevelAndOption(QNativeSocketEngine::SocketOption opt,
switch (opt) {
case QNativeSocketEngine::NonBlockingSocketOption: // WSAIoctl
case QNativeSocketEngine::TypeOfServiceOption: // not supported
+ case QNativeSocketEngine::MaxStreamsSocketOption:
Q_UNREACHABLE();
case QNativeSocketEngine::ReceiveBufferSocketOption:
@@ -325,6 +326,14 @@ bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType soc
return -1;
}
*/
+
+ //### SCTP not implemented
+ if (socketType == QAbstractSocket::SctpSocket) {
+ setError(QAbstractSocket::UnsupportedSocketOperationError,
+ ProtocolUnsupportedErrorString);
+ return false;
+ }
+
QSysInfo::WinVersion osver = QSysInfo::windowsVersion();
//Windows XP and 2003 support IPv6 but not dual stack sockets
@@ -387,7 +396,6 @@ bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType soc
return false;
}
-#if !defined(Q_OS_WINCE)
if (socketType == QAbstractSocket::UdpSocket) {
// enable new behavior using
// SIO_UDP_CONNRESET
@@ -414,7 +422,6 @@ bool QNativeSocketEnginePrivate::createNewSocket(QAbstractSocket::SocketType soc
&sendmsgguid, sizeof(sendmsgguid),
&sendmsg, sizeof(sendmsg), &bytesReturned, NULL, NULL) == SOCKET_ERROR)
sendmsg = 0;
-#endif
socketDescriptor = socket;
if (socket != INVALID_SOCKET) {
@@ -453,6 +460,7 @@ int QNativeSocketEnginePrivate::option(QNativeSocketEngine::SocketOption opt) co
break;
}
case QNativeSocketEngine::TypeOfServiceOption:
+ case QNativeSocketEngine::MaxStreamsSocketOption:
return -1;
default:
@@ -503,6 +511,7 @@ bool QNativeSocketEnginePrivate::setOption(QNativeSocketEngine::SocketOption opt
break;
}
case QNativeSocketEngine::TypeOfServiceOption:
+ case QNativeSocketEngine::MaxStreamsSocketOption:
return false;
default:
@@ -703,7 +712,7 @@ bool QNativeSocketEnginePrivate::nativeConnect(const QHostAddress &address, quin
if (errorDetected)
break;
- // fall through
+ Q_FALLTHROUGH();
}
case WSAEINPROGRESS:
setError(QAbstractSocket::UnfinishedSocketOperationError, InvalidSocketErrorString);
@@ -1091,7 +1100,6 @@ qint64 QNativeSocketEnginePrivate::nativeBytesAvailable() const
bool QNativeSocketEnginePrivate::nativeHasPendingDatagrams() const
{
-#if !defined(Q_OS_WINCE)
// Create a sockaddr struct and reset its port number.
qt_sockaddr storage;
QT_SOCKLEN_T storageSize = sizeof(storage);
@@ -1118,18 +1126,6 @@ bool QNativeSocketEnginePrivate::nativeHasPendingDatagrams() const
result = true;
}
-#else // Q_OS_WINCE
- bool result = false;
- fd_set readS;
- FD_ZERO(&readS);
- FD_SET((SOCKET)socketDescriptor, &readS);
- timeval timeout;
- timeout.tv_sec = 0;
- timeout.tv_usec = 5000;
- int available = ::select(1, &readS, 0, 0, &timeout);
- result = available > 0;
-#endif
-
#if defined (QNATIVESOCKETENGINE_DEBUG)
qDebug("QNativeSocketEnginePrivate::nativeHasPendingDatagrams() == %s",
result ? "true" : "false");
@@ -1141,7 +1137,6 @@ bool QNativeSocketEnginePrivate::nativeHasPendingDatagrams() const
qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const
{
qint64 ret = -1;
-#if !defined(Q_OS_WINCE)
int recvResult = 0;
DWORD flags;
DWORD bufferCount = 5;
@@ -1186,18 +1181,6 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const
if (buf)
delete[] buf;
-#else // Q_OS_WINCE
- DWORD size = -1;
- DWORD bytesReturned;
- int ioResult = WSAIoctl(socketDescriptor, FIONREAD, 0,0, &size, sizeof(size), &bytesReturned, 0, 0);
- if (ioResult == SOCKET_ERROR) {
- int err = WSAGetLastError();
- WS_ERROR_DEBUG(err);
- } else {
- ret = qint64(size);
- }
-#endif
-
#if defined (QNATIVESOCKETENGINE_DEBUG)
qDebug("QNativeSocketEnginePrivate::nativePendingDatagramSize() == %lli", ret);
#endif
@@ -1205,12 +1188,6 @@ qint64 QNativeSocketEnginePrivate::nativePendingDatagramSize() const
return ret;
}
-#ifdef Q_OS_WINCE
-// Windows CE has no support for sendmsg or recvmsg. We set it to null here to simplify the code below.
-static int (*const recvmsg)(...) = 0;
-static int (*const sendmsg)(...) = 0;
-#endif
-
qint64 QNativeSocketEnginePrivate::nativeReceiveDatagram(char *data, qint64 maxLength, QIpPacketHeader *header,
QAbstractSocketEngine::PacketHeaderOptions options)
{
@@ -1330,12 +1307,7 @@ qint64 QNativeSocketEnginePrivate::nativeSendDatagram(const char *data, qint64 l
memset(&msg, 0, sizeof(msg));
memset(&aa, 0, sizeof(aa));
-#if !defined(Q_OS_WINCE)
buf.buf = len ? (char*)data : 0;
-#else
- char tmp;
- buf.buf = len ? (char*)data : &tmp;
-#endif
msg.lpBuffers = &buf;
msg.dwBufferCount = 1;
msg.name = &aa.a;
@@ -1497,9 +1469,6 @@ qint64 QNativeSocketEnginePrivate::nativeRead(char *data, qint64 maxLength)
buf.len = maxLength;
DWORD flags = 0;
DWORD bytesRead = 0;
-#if defined(Q_OS_WINCE)
- WSASetLastError(0);
-#endif
if (::WSARecv(socketDescriptor, &buf, 1, &bytesRead, &flags, 0,0) == SOCKET_ERROR) {
int err = WSAGetLastError();
WS_ERROR_DEBUG(err);
@@ -1613,11 +1582,7 @@ int QNativeSocketEnginePrivate::nativeSelect(int timeout,
tv.tv_sec = timeout / 1000;
tv.tv_usec = (timeout % 1000) * 1000;
-#if !defined(Q_OS_WINCE)
ret = select(socketDescriptor + 1, &fdread, &fdwrite, &fdexception, timeout < 0 ? 0 : &tv);
-#else
- ret = select(1, &fdread, &fdwrite, &fdexception, timeout < 0 ? 0 : &tv);
-#endif
//... but if it is actually set, pretend it did not happen
if (ret > 0 && FD_ISSET((SOCKET)socketDescriptor, &fdexception))
diff --git a/src/network/socket/qnativesocketengine_winrt.cpp b/src/network/socket/qnativesocketengine_winrt.cpp
index 9817d4c57d..6b71912838 100644
--- a/src/network/socket/qnativesocketengine_winrt.cpp
+++ b/src/network/socket/qnativesocketengine_winrt.cpp
@@ -632,6 +632,7 @@ qint64 QNativeSocketEngine::write(const char *data, qint64 len)
qint64 QNativeSocketEngine::readDatagram(char *data, qint64 maxlen, QIpPacketHeader *header,
PacketHeaderOptions)
{
+#ifndef QT_NO_UDPSOCKET
Q_D(QNativeSocketEngine);
if (d->socketType != QAbstractSocket::UdpSocket || d->pendingDatagrams.isEmpty()) {
if (header)
@@ -654,10 +655,17 @@ qint64 QNativeSocketEngine::readDatagram(char *data, qint64 maxlen, QIpPacketHea
}
memcpy(data, readOrigin, qMin(maxlen, qint64(datagram.data.length())));
return readOrigin.length();
+#else
+ Q_UNUSED(data)
+ Q_UNUSED(maxlen)
+ Q_UNUSED(header)
+ return -1;
+#endif // QT_NO_UDPSOCKET
}
qint64 QNativeSocketEngine::writeDatagram(const char *data, qint64 len, const QIpPacketHeader &header)
{
+#ifndef QT_NO_UDPSOCKET
Q_D(QNativeSocketEngine);
if (d->socketType != QAbstractSocket::UdpSocket)
return -1;
@@ -684,6 +692,12 @@ qint64 QNativeSocketEngine::writeDatagram(const char *data, qint64 len, const QI
Q_ASSERT_SUCCEEDED(hr);
return writeIOStream(stream, data, len);
+#else
+ Q_UNUSED(data)
+ Q_UNUSED(len)
+ Q_UNUSED(header)
+ return -1;
+#endif // QT_NO_UDPSOCKET
}
bool QNativeSocketEngine::hasPendingDatagrams() const
@@ -1088,6 +1102,7 @@ int QNativeSocketEnginePrivate::option(QAbstractSocketEngine::SocketOption opt)
case QAbstractSocketEngine::MulticastTtlOption:
case QAbstractSocketEngine::MulticastLoopbackOption:
case QAbstractSocketEngine::TypeOfServiceOption:
+ case QAbstractSocketEngine::MaxStreamsSocketOption:
default:
return -1;
}
@@ -1146,6 +1161,7 @@ bool QNativeSocketEnginePrivate::setOption(QAbstractSocketEngine::SocketOption o
case QAbstractSocketEngine::MulticastTtlOption:
case QAbstractSocketEngine::MulticastLoopbackOption:
case QAbstractSocketEngine::TypeOfServiceOption:
+ case QAbstractSocketEngine::MaxStreamsSocketOption:
default:
return false;
}
@@ -1290,10 +1306,12 @@ HRESULT QNativeSocketEnginePrivate::handleConnectOpFinished(IAsyncAction *action
if (socketType != QAbstractSocket::TcpSocket)
return S_OK;
+#ifndef QT_NO_SSL
// Delay the reader so that the SSL socket can upgrade
if (sslSocket)
QObject::connect(qobject_cast<QSslSocket *>(sslSocket), &QSslSocket::encrypted, q, &QNativeSocketEngine::establishRead);
else
+#endif
q->establishRead();
return S_OK;
}
diff --git a/src/network/socket/qnativesocketengine_winrt_p.h b/src/network/socket/qnativesocketengine_winrt_p.h
index 5b76c2d223..ef219e61df 100644
--- a/src/network/socket/qnativesocketengine_winrt_p.h
+++ b/src/network/socket/qnativesocketengine_winrt_p.h
@@ -50,6 +50,8 @@
//
// We mean it.
//
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <QtCore/QEventLoop>
#include <QtCore/QBuffer>
#include <QtCore/QMutex>
diff --git a/src/network/socket/qnet_unix_p.h b/src/network/socket/qnet_unix_p.h
index 8d2a4ae852..46fbc9757a 100644
--- a/src/network/socket/qnet_unix_p.h
+++ b/src/network/socket/qnet_unix_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "private/qcore_unix_p.h"
#include <sys/types.h>
diff --git a/src/network/socket/qsctpserver.cpp b/src/network/socket/qsctpserver.cpp
new file mode 100644
index 0000000000..24f18e1ee8
--- /dev/null
+++ b/src/network/socket/qsctpserver.cpp
@@ -0,0 +1,250 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** 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$
+**
+****************************************************************************/
+
+//#define QSCTPSERVER_DEBUG
+
+/*!
+ \class QSctpServer
+ \since 5.8
+
+ \brief The QSctpServer class provides an SCTP-based server.
+
+ \ingroup network
+ \inmodule QtNetwork
+
+ SCTP (Stream Control Transmission Protocol) is a transport layer
+ protocol serving in a similar role as the popular protocols TCP
+ and UDP. Like UDP, SCTP is message-oriented, but it ensures reliable,
+ in-sequence transport of messages with congestion control like
+ TCP. See the QSctpSocket documentation for more protocol details.
+
+ QSctpServer is a convenience subclass of QTcpServer that allows
+ you to accept incoming SCTP socket connections either in TCP
+ emulation or in datagram mode.
+
+ The most common way to use QSctpServer is to construct an object
+ and set the maximum number of channels that the server is
+ prepared to support, by calling setMaxChannelCount(). You can set
+ the TCP emulation mode by passing a negative argument in this
+ call. Also, a special value of 0 (the default) indicates to use
+ the peer's value as the actual number of channels. The new incoming
+ connection inherits this number from the server socket descriptor
+ and adjusts it according to the remote endpoint settings.
+
+ In TCP emulation mode, accepted clients use a single continuous
+ byte stream for data transmission, and QSctpServer acts like a
+ plain QTcpServer. Call nextPendingConnection() to accept the
+ pending connection as a connected QTcpSocket. The function returns
+ a pointer to a QTcpSocket in QAbstractSocket::ConnectedState that
+ you can use for communicating with the client. This mode gives
+ access only to basic SCTP protocol features. The socket transmits SCTP
+ packets over IP at system level and interacts through the
+ QTcpSocket interface with the application.
+
+ In contrast, datagram mode is message-oriented and provides a
+ complete simultaneous transmission of multiple data streams
+ between endpoints. Call nextPendingDatagramConnection() to accept
+ the pending datagram-mode connection as a connected QSctpSocket.
+
+ \note This feature is not supported on the Windows platform.
+
+ \sa QTcpServer, QSctpSocket, QAbstractSocket
+*/
+
+#include "qsctpserver.h"
+#include "qsctpserver_p.h"
+
+#include "qsctpsocket.h"
+#include "qabstractsocketengine_p.h"
+
+#ifdef QSCTPSERVER_DEBUG
+#include <qdebug.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*! \internal
+*/
+QSctpServerPrivate::QSctpServerPrivate()
+ : maxChannelCount(0)
+{
+}
+
+/*! \internal
+*/
+QSctpServerPrivate::~QSctpServerPrivate()
+{
+}
+
+/*! \internal
+*/
+void QSctpServerPrivate::configureCreatedSocket()
+{
+ QTcpServerPrivate::configureCreatedSocket();
+ if (socketEngine)
+ socketEngine->setOption(QAbstractSocketEngine::MaxStreamsSocketOption,
+ maxChannelCount == -1 ? 1 : maxChannelCount);
+}
+
+/*!
+ Constructs a QSctpServer object.
+
+ Sets the datagram operation mode. The \a parent argument is passed
+ to QObject's constructor.
+
+ \sa setMaxChannelCount(), listen(), setSocketDescriptor()
+*/
+QSctpServer::QSctpServer(QObject *parent)
+ : QTcpServer(QAbstractSocket::SctpSocket, *new QSctpServerPrivate, parent)
+{
+#if defined(QSCTPSERVER_DEBUG)
+ qDebug("QSctpServer::QSctpServer()");
+#endif
+}
+
+/*!
+ Destroys the QSctpServer object. If the server is listening for
+ connections, the socket is automatically closed.
+
+ \sa close()
+*/
+QSctpServer::~QSctpServer()
+{
+#if defined(QSCTPSERVER_DEBUG)
+ qDebug("QSctpServer::~QSctpServer()");
+#endif
+}
+
+/*!
+ Sets the maximum number of channels that the server is prepared to
+ support in datagram mode, to \a count. If \a count is 0, endpoint
+ maximum number of channels value would be used. Negative \a count
+ sets a TCP emulation mode.
+
+ Call this method only when QSctpServer is in UnconnectedState.
+
+ \sa maxChannelCount(), QSctpSocket
+*/
+void QSctpServer::setMaxChannelCount(int count)
+{
+ Q_D(QSctpServer);
+ if (d->state != QAbstractSocket::UnconnectedState) {
+ qWarning("QSctpServer::setMaxChannelCount() is only allowed in UnconnectedState");
+ return;
+ }
+#if defined(QSCTPSERVER_DEBUG)
+ qDebug("QSctpServer::setMaxChannelCount(%i)", count);
+#endif
+ d->maxChannelCount = count;
+}
+
+/*!
+ Returns the maximum number of channels that the accepted sockets are
+ able to support.
+
+ A value of 0 (the default) means that the number of connection
+ channels would be set by the remote endpoint.
+
+ Returns -1, if QSctpServer running in TCP emulation mode.
+
+ \sa setMaxChannelCount()
+*/
+int QSctpServer::maxChannelCount() const
+{
+ return d_func()->maxChannelCount;
+}
+
+/*! \reimp
+*/
+void QSctpServer::incomingConnection(qintptr socketDescriptor)
+{
+#if defined (QSCTPSERVER_DEBUG)
+ qDebug("QSctpServer::incomingConnection(%i)", socketDescriptor);
+#endif
+
+ QSctpSocket *socket = new QSctpSocket(this);
+ socket->setMaxChannelCount(d_func()->maxChannelCount);
+ socket->setSocketDescriptor(socketDescriptor);
+ addPendingConnection(socket);
+}
+
+/*!
+ Returns the next pending datagram-mode connection as a connected
+ QSctpSocket object.
+
+ Datagram-mode connection provides a message-oriented, multi-stream
+ communication.
+
+ The socket is created as a child of the server, which means that
+ it is automatically deleted when the QSctpServer object is
+ destroyed. It is still a good idea to delete the object
+ explicitly when you are done with it, to avoid wasting memory.
+
+ This function returns null if there are no pending datagram-mode
+ connections.
+
+ \note The returned QSctpSocket object cannot be used from another
+ thread. If you want to use an incoming connection from another
+ thread, you need to override incomingConnection().
+
+ \sa hasPendingConnections(), nextPendingConnection(), QSctpSocket
+*/
+QSctpSocket *QSctpServer::nextPendingDatagramConnection()
+{
+ Q_D(QSctpServer);
+
+ QMutableListIterator<QTcpSocket *> i(d->pendingConnections);
+ while (i.hasNext()) {
+ QSctpSocket *socket = qobject_cast<QSctpSocket *>(i.next());
+ Q_ASSERT(socket);
+
+ if (socket->inDatagramMode()) {
+ i.remove();
+ Q_ASSERT(d->socketEngine);
+ d->socketEngine->setReadNotificationEnabled(true);
+ return socket;
+ }
+ }
+
+ return nullptr;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qsctpserver.cpp"
diff --git a/src/network/socket/qsctpserver.h b/src/network/socket/qsctpserver.h
new file mode 100644
index 0000000000..fb017a924d
--- /dev/null
+++ b/src/network/socket/qsctpserver.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** 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 QSCTPSERVER_H
+#define QSCTPSERVER_H
+
+#include <QtNetwork/qtcpserver.h>
+
+QT_BEGIN_NAMESPACE
+
+
+#ifndef QT_NO_SCTP
+
+class QSctpServerPrivate;
+class QSctpSocket;
+
+class Q_NETWORK_EXPORT QSctpServer : public QTcpServer
+{
+ Q_OBJECT
+public:
+ explicit QSctpServer(QObject *parent = nullptr);
+ virtual ~QSctpServer();
+
+ void setMaxChannelCount(int count);
+ int maxChannelCount() const;
+
+ QSctpSocket *nextPendingDatagramConnection();
+
+protected:
+ void incomingConnection(qintptr handle) Q_DECL_OVERRIDE;
+
+private:
+ Q_DISABLE_COPY(QSctpServer)
+ Q_DECLARE_PRIVATE(QSctpServer)
+};
+
+#endif // QT_NO_SCTP
+
+QT_END_NAMESPACE
+
+#endif // QSCTPSERVER_H
diff --git a/src/network/socket/qsctpserver_p.h b/src/network/socket/qsctpserver_p.h
new file mode 100644
index 0000000000..32760caffe
--- /dev/null
+++ b/src/network/socket/qsctpserver_p.h
@@ -0,0 +1,76 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** 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 QSCTPSERVER_P_H
+#define QSCTPSERVER_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 QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "private/qtcpserver_p.h"
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_SCTP
+
+class QSctpServerPrivate : public QTcpServerPrivate
+{
+ Q_DECLARE_PUBLIC(QSctpServer)
+public:
+ QSctpServerPrivate();
+ virtual ~QSctpServerPrivate();
+
+ int maxChannelCount;
+
+ void configureCreatedSocket() Q_DECL_OVERRIDE;
+};
+
+#endif // QT_NO_SCTP
+
+QT_END_NAMESPACE
+
+#endif // QSCTPSERVER_P_H
diff --git a/src/network/socket/qsctpsocket.cpp b/src/network/socket/qsctpsocket.cpp
new file mode 100644
index 0000000000..cb07e80299
--- /dev/null
+++ b/src/network/socket/qsctpsocket.cpp
@@ -0,0 +1,547 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** 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$
+**
+****************************************************************************/
+
+//#define QSCTPSOCKET_DEBUG
+
+/*!
+ \class QSctpSocket
+ \since 5.8
+
+ \brief The QSctpSocket class provides an SCTP socket.
+
+ \ingroup network
+ \inmodule QtNetwork
+
+ SCTP (Stream Control Transmission Protocol) is a transport layer
+ protocol serving in a similar role as the popular protocols TCP
+ and UDP. Like UDP, SCTP is message-oriented, but it ensures reliable,
+ in-sequence transport of messages with congestion control like
+ TCP.
+
+ SCTP is connection-oriented protocol, which provides the complete
+ simultaneous transmission of multiple data streams between
+ endpoints. This multi-streaming allows data to be delivered by
+ independent channels, so that if there is data loss in one stream,
+ delivery will not be affected for the other streams.
+
+ Being message-oriented, SCTP transports a sequence of messages,
+ rather than transporting an unbroken stream of bytes as does TCP.
+ Like in UDP, in SCTP a sender sends a message in one operation,
+ and that exact message is passed to the receiving application
+ process in one operation. But unlike UDP, the delivery is
+ guaranteed.
+
+ It also supports multi-homing, meaning that a connected endpoint
+ can have alternate IP addresses associated with it in order to
+ route around network failure or changing conditions.
+
+ QSctpSocket is a convenience subclass of QTcpSocket that allows
+ you to emulate TCP data stream over SCTP or establish an SCTP
+ connection for reliable datagram service.
+
+ QSctpSocket can operate in one of two possible modes:
+
+ \list
+ \li Continuous byte stream (TCP emulation).
+ \li Multi-streamed datagram mode.
+ \endlist
+
+ To set a continuous byte stream mode, instantiate QSctpSocket and
+ call setMaxChannelCount() with a negative value. This gives the
+ ability to use QSctpSocket as a regular buffered QTcpSocket. You
+ can call connectToHost() to initiate connection with endpoint,
+ write() to transmit and read() to receive data from the peer, but
+ you cannot distinguish message boundaries.
+
+ By default, QSctpSocket operates in datagram mode. Before
+ connecting, call setMaxChannelCount() to set the maximum number of
+ channels that the application is prepared to support. This number
+ is a parameter negotiated with the remote endpoint and its value
+ can be bounded by the operating system. The default value of 0
+ indicates to use the peer's value. If both endpoints have default
+ values, then number of connection channels is system-dependent.
+ After establishing a connection, you can fetch the actual number
+ of channels by calling readChannelCount() and writeChannelCount().
+
+ \snippet code/src_network_socket_qsctpsocket.cpp 0
+
+ In datagram mode, QSctpSocket performs the buffering of datagrams
+ independently for each channel. You can queue a datagram to the
+ buffer of the current channel by calling writeDatagram() and read
+ a pending datagram by calling readDatagram() respectively.
+
+ Using the standard QIODevice functions read(), readLine(), write(),
+ etc. is allowed in datagram mode with the same limitations as in
+ continuous byte stream mode.
+
+ \note This feature is not supported on the Windows platform.
+
+ \sa QSctpServer, QTcpSocket, QAbstractSocket
+*/
+
+#include "qsctpsocket.h"
+#include "qsctpsocket_p.h"
+
+#include "qabstractsocketengine_p.h"
+#include "private/qbytearray_p.h"
+
+#ifdef QSCTPSOCKET_DEBUG
+#include <qdebug.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+/*! \internal
+*/
+QSctpSocketPrivate::QSctpSocketPrivate()
+ : maxChannelCount(0)
+{
+}
+
+/*! \internal
+*/
+QSctpSocketPrivate::~QSctpSocketPrivate()
+{
+}
+
+/*! \internal
+*/
+bool QSctpSocketPrivate::canReadNotification()
+{
+ Q_Q(QSctpSocket);
+#if defined (QSCTPSOCKET_DEBUG)
+ qDebug("QSctpSocketPrivate::canReadNotification()");
+#endif
+
+ // Handle TCP emulation mode in the base implementation.
+ if (!q->inDatagramMode())
+ return QTcpSocketPrivate::canReadNotification();
+
+ const int savedCurrentChannel = currentReadChannel;
+ bool currentChannelRead = false;
+ do {
+ int datagramSize = incomingDatagram.size();
+ QIpPacketHeader header;
+
+ do {
+ // Determine the size of the pending datagram.
+ qint64 bytesToRead = socketEngine->bytesAvailable();
+ if (bytesToRead == 0) {
+ // As a corner case, if we can't determine the size of the pending datagram,
+ // try to read 4K of data from the socket. Subsequent ::recvmsg call either
+ // fails or returns the actual length of the datagram.
+ bytesToRead = 4096;
+ }
+
+ Q_ASSERT((datagramSize + int(bytesToRead)) < MaxByteArraySize);
+ incomingDatagram.resize(datagramSize + int(bytesToRead));
+
+#if defined (QSCTPSOCKET_DEBUG)
+ qDebug("QSctpSocketPrivate::canReadNotification() about to read %lli bytes",
+ bytesToRead);
+#endif
+ qint64 readBytes = socketEngine->readDatagram(
+ incomingDatagram.data() + datagramSize, bytesToRead, &header,
+ QAbstractSocketEngine::WantAll);
+ if (readBytes <= 0) {
+ if (readBytes == -2) { // no data available for reading
+ incomingDatagram.resize(datagramSize);
+ return currentChannelRead;
+ }
+
+ socketEngine->close();
+ if (readBytes == 0) {
+ setErrorAndEmit(QAbstractSocket::RemoteHostClosedError,
+ QSctpSocket::tr("The remote host closed the connection"));
+ } else {
+#if defined (QSCTPSOCKET_DEBUG)
+ qDebug("QSctpSocketPrivate::canReadNotification() read failed: %s",
+ socketEngine->errorString().toLatin1().constData());
+#endif
+ setErrorAndEmit(socketEngine->error(), socketEngine->errorString());
+ }
+
+#if defined (QSCTPSOCKET_DEBUG)
+ qDebug("QSctpSocketPrivate::canReadNotification() disconnecting socket");
+#endif
+ q->disconnectFromHost();
+ return currentChannelRead;
+ }
+ datagramSize += int(readBytes); // update datagram size
+ } while (!header.endOfRecord);
+
+#if defined (QSCTPSOCKET_DEBUG)
+ qDebug("QSctpSocketPrivate::canReadNotification() got datagram from channel %i, size = %i",
+ header.streamNumber, datagramSize);
+#endif
+
+ // Drop the datagram, if opened only for writing
+ if (!q->isReadable()) {
+ incomingDatagram.clear();
+ continue;
+ }
+
+ // Store datagram in the channel buffer
+ Q_ASSERT(header.streamNumber < readBuffers.size());
+ incomingDatagram.resize(datagramSize);
+ readBuffers[header.streamNumber].setChunkSize(0); // set packet mode on channel buffer
+ readBuffers[header.streamNumber].append(incomingDatagram);
+ incomingDatagram = QByteArray();
+
+ if (readHeaders.size() != readBuffers.size())
+ readHeaders.resize(readBuffers.size());
+ readHeaders[header.streamNumber].push_back(header);
+
+ // Emit notifications.
+ if (header.streamNumber == savedCurrentChannel)
+ currentChannelRead = true;
+ emitReadyRead(header.streamNumber);
+
+ } while (state == QAbstractSocket::ConnectedState);
+
+ return currentChannelRead;
+}
+
+/*! \internal
+*/
+bool QSctpSocketPrivate::writeToSocket()
+{
+ Q_Q(QSctpSocket);
+#if defined (QSCTPSOCKET_DEBUG)
+ qDebug("QSctpSocketPrivate::writeToSocket()");
+#endif
+
+ // Handle TCP emulation mode in the base implementation.
+ if (!q->inDatagramMode())
+ return QTcpSocketPrivate::writeToSocket();
+
+ if (!socketEngine)
+ return false;
+
+ QIpPacketHeader defaultHeader;
+ const int savedCurrentChannel = currentWriteChannel;
+ bool currentChannelWritten = false;
+ bool transmitting;
+ do {
+ transmitting = false;
+
+ for (int channel = 0; channel < writeBuffers.size(); ++channel) {
+ QRingBuffer &channelBuffer = writeBuffers[channel];
+
+ if (channelBuffer.isEmpty())
+ continue;
+
+ const bool hasHeader = (channel < writeHeaders.size())
+ && !writeHeaders[channel].empty();
+ QIpPacketHeader &header = hasHeader ? writeHeaders[channel].front() : defaultHeader;
+ header.streamNumber = channel;
+ qint64 sent = socketEngine->writeDatagram(channelBuffer.readPointer(),
+ channelBuffer.nextDataBlockSize(),
+ header);
+ if (sent < 0) {
+ if (sent == -2) // temporary error in writeDatagram
+ return currentChannelWritten;
+
+ socketEngine->close();
+#if defined (QSCTPSOCKET_DEBUG)
+ qDebug("QSctpSocketPrivate::writeToSocket() write error, aborting. %s",
+ socketEngine->errorString().toLatin1().constData());
+#endif
+ setErrorAndEmit(socketEngine->error(), socketEngine->errorString());
+ // An unexpected error so close the socket.
+ q->disconnectFromHost();
+ return currentChannelWritten;
+ }
+ Q_ASSERT(sent == channelBuffer.nextDataBlockSize());
+#if defined (QSCTPSOCKET_DEBUG)
+ qDebug("QSctpSocketPrivate::writeToSocket() sent datagram of size %lli to channel %i",
+ sent, channel);
+#endif
+ transmitting = true;
+
+ // Remove datagram from the buffer
+ channelBuffer.read();
+ if (hasHeader)
+ writeHeaders[channel].pop_front();
+
+ // Emit notifications.
+ if (channel == savedCurrentChannel)
+ currentChannelWritten = true;
+ emitBytesWritten(sent, channel);
+
+ // If we were closed as a result of the bytesWritten() signal, return.
+ if (state == QAbstractSocket::UnconnectedState) {
+#if defined (QSCTPSOCKET_DEBUG)
+ qDebug("QSctpSocketPrivate::writeToSocket() socket is closing - returning");
+#endif
+ return currentChannelWritten;
+ }
+ }
+ } while (transmitting);
+
+ // At this point socket is either in Connected or Closing state,
+ // write buffers are empty.
+ if (state == QAbstractSocket::ClosingState)
+ q->disconnectFromHost();
+ else
+ socketEngine->setWriteNotificationEnabled(false);
+
+ return currentChannelWritten;
+}
+
+/*! \internal
+*/
+void QSctpSocketPrivate::configureCreatedSocket()
+{
+ if (socketEngine)
+ socketEngine->setOption(QAbstractSocketEngine::MaxStreamsSocketOption,
+ maxChannelCount < 0 ? 1 : maxChannelCount);
+}
+
+/*!
+ Creates a QSctpSocket object in state \c UnconnectedState.
+
+ Sets the datagram operation mode. The \a parent argument is passed
+ to QObject's constructor.
+
+ \sa socketType(), setMaxChannelCount()
+*/
+QSctpSocket::QSctpSocket(QObject *parent)
+ : QTcpSocket(SctpSocket, *new QSctpSocketPrivate, parent)
+{
+#if defined(QSCTPSOCKET_DEBUG)
+ qDebug("QSctpSocket::QSctpSocket()");
+#endif
+ d_func()->isBuffered = true;
+}
+
+/*!
+ Destroys the socket, closing the connection if necessary.
+
+ \sa close()
+*/
+QSctpSocket::~QSctpSocket()
+{
+#if defined(QSCTPSOCKET_DEBUG)
+ qDebug("QSctpSocket::~QSctpSocket()");
+#endif
+}
+
+/*! \reimp
+*/
+qint64 QSctpSocket::readData(char *data, qint64 maxSize)
+{
+ Q_D(QSctpSocket);
+
+ // Cleanup headers, if the user calls the standard QIODevice functions
+ if (d->currentReadChannel < d->readHeaders.size())
+ d->readHeaders[d->currentReadChannel].clear();
+
+ return QTcpSocket::readData(data, maxSize);
+}
+
+/*! \reimp
+*/
+qint64 QSctpSocket::readLineData(char *data, qint64 maxlen)
+{
+ Q_D(QSctpSocket);
+
+ // Cleanup headers, if the user calls the standard QIODevice functions
+ if (d->currentReadChannel < d->readHeaders.size())
+ d->readHeaders[d->currentReadChannel].clear();
+
+ return QTcpSocket::readLineData(data, maxlen);
+}
+
+/*! \reimp
+*/
+void QSctpSocket::close()
+{
+ QTcpSocket::close();
+ d_func()->readHeaders.clear();
+}
+
+/*! \reimp
+*/
+void QSctpSocket::disconnectFromHost()
+{
+ Q_D(QSctpSocket);
+
+ QTcpSocket::disconnectFromHost();
+ if (d->state == QAbstractSocket::UnconnectedState) {
+ d->incomingDatagram.clear();
+ d->writeHeaders.clear();
+ }
+}
+
+/*!
+ Sets the maximum number of channels that the application is
+ prepared to support in datagram mode, to \a count. If \a count
+ is 0, endpoint's value for maximum number of channels is used.
+ Negative \a count sets a continuous byte stream mode.
+
+ Call this method only when QSctpSocket is in UnconnectedState.
+
+ \sa maxChannelCount(), readChannelCount(), writeChannelCount()
+*/
+void QSctpSocket::setMaxChannelCount(int count)
+{
+ Q_D(QSctpSocket);
+ if (d->state != QAbstractSocket::UnconnectedState) {
+ qWarning("QSctpSocket::setMaxChannelCount() is only allowed in UnconnectedState");
+ return;
+ }
+#if defined(QSCTPSOCKET_DEBUG)
+ qDebug("QSctpSocket::setMaxChannelCount(%i)", count);
+#endif
+ d->maxChannelCount = qMax(count, -1);
+}
+
+/*!
+ Returns the maximum number of channels that QSctpSocket is able to
+ support.
+
+ A value of 0 (the default) means that the number of connection
+ channels would be set by the remote endpoint.
+
+ Returns -1 if QSctpSocket is running in continuous byte stream
+ mode.
+
+ \sa setMaxChannelCount(), readChannelCount(), writeChannelCount()
+*/
+int QSctpSocket::maxChannelCount() const
+{
+ return d_func()->maxChannelCount;
+}
+
+/*!
+ Returns \c true if the socket is running in datagram mode.
+
+ \sa setMaxChannelCount()
+*/
+bool QSctpSocket::inDatagramMode() const
+{
+ Q_D(const QSctpSocket);
+ return d->maxChannelCount != -1 && d->isBuffered;
+}
+
+/*!
+ Reads a datagram from the buffer of the current read channel, and
+ returns it as a QNetworkDatagram object, along with the sender's
+ host address and port. If possible, this function will also try to
+ determine the datagram's destination address, port, and the number
+ of hop counts at reception time.
+
+ On failure, returns a QNetworkDatagram that reports \l
+ {QNetworkDatagram::isValid()}{not valid}.
+
+ \sa writeDatagram(), inDatagramMode(), currentReadChannel()
+*/
+QNetworkDatagram QSctpSocket::readDatagram()
+{
+ Q_D(QSctpSocket);
+
+ if (!isReadable() || !inDatagramMode()) {
+ qWarning("QSctpSocket::readDatagram(): operation is not permitted");
+ return QNetworkDatagram();
+ }
+
+ if (d->currentReadChannel >= d->readHeaders.size()
+ || d->readHeaders[d->currentReadChannel].size() == 0) {
+ Q_ASSERT(d->buffer.isEmpty());
+ return QNetworkDatagram();
+ }
+
+ QNetworkDatagram result(*new QNetworkDatagramPrivate(d->buffer.read(),
+ d->readHeaders[d->currentReadChannel].front()));
+ d->readHeaders[d->currentReadChannel].pop_front();
+
+#if defined (QSCTPSOCKET_DEBUG)
+ qDebug("QSctpSocket::readDatagram() returning datagram (%p, %i, \"%s\", %i)",
+ result.d->data.constData(),
+ result.d->data.size(),
+ result.senderAddress().toString().toLatin1().constData(),
+ result.senderPort());
+#endif
+
+ return result;
+}
+
+/*!
+ Writes a \a datagram to the buffer of the current write channel.
+ Returns true on success; otherwise returns false.
+
+ \sa readDatagram(), inDatagramMode(), currentWriteChannel()
+*/
+bool QSctpSocket::writeDatagram(const QNetworkDatagram &datagram)
+{
+ Q_D(QSctpSocket);
+
+ if (!isWritable() || d->state != QAbstractSocket::ConnectedState || !d->socketEngine
+ || !d->socketEngine->isValid() || !inDatagramMode()) {
+ qWarning("QSctpSocket::writeDatagram(): operation is not permitted");
+ return false;
+ }
+
+ if (datagram.d->data.isEmpty()) {
+ qWarning("QSctpSocket::writeDatagram() is called with empty datagram");
+ return false;
+ }
+
+
+#if defined QSCTPSOCKET_DEBUG
+ qDebug("QSctpSocket::writeDatagram(%p, %i, \"%s\", %i)",
+ datagram.d->data.constData(),
+ datagram.d->data.size(),
+ datagram.destinationAddress().toString().toLatin1().constData(),
+ datagram.destinationPort());
+#endif
+
+ if (d->writeHeaders.size() != d->writeBuffers.size())
+ d->writeHeaders.resize(d->writeBuffers.size());
+ Q_ASSERT(d->currentWriteChannel < d->writeHeaders.size());
+ d->writeHeaders[d->currentWriteChannel].push_back(datagram.d->header);
+ d->writeBuffer.setChunkSize(0); // set packet mode on channel buffer
+ d->writeBuffer.append(datagram.d->data);
+
+ d->socketEngine->setWriteNotificationEnabled(true);
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/socket/qsctpsocket.h b/src/network/socket/qsctpsocket.h
new file mode 100644
index 0000000000..aaa4e1ecca
--- /dev/null
+++ b/src/network/socket/qsctpsocket.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** 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 QSCTPSOCKET_H
+#define QSCTPSOCKET_H
+
+#include <QtNetwork/qtcpsocket.h>
+#include <QtNetwork/qnetworkdatagram.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_SCTP
+
+class QSctpSocketPrivate;
+
+class Q_NETWORK_EXPORT QSctpSocket : public QTcpSocket
+{
+ Q_OBJECT
+public:
+ explicit QSctpSocket(QObject *parent = nullptr);
+ virtual ~QSctpSocket();
+
+ void close() Q_DECL_OVERRIDE;
+ void disconnectFromHost() Q_DECL_OVERRIDE;
+
+ void setMaxChannelCount(int count);
+ int maxChannelCount() const;
+ bool inDatagramMode() const;
+
+ QNetworkDatagram readDatagram();
+ bool writeDatagram(const QNetworkDatagram &datagram);
+
+protected:
+ qint64 readData(char *data, qint64 maxlen) Q_DECL_OVERRIDE;
+ qint64 readLineData(char *data, qint64 maxlen) Q_DECL_OVERRIDE;
+
+private:
+ Q_DISABLE_COPY(QSctpSocket)
+ Q_DECLARE_PRIVATE(QSctpSocket)
+};
+
+#endif // QT_NO_SCTP
+
+QT_END_NAMESPACE
+
+#endif // QSCTPSOCKET_H
diff --git a/src/network/socket/qsctpsocket_p.h b/src/network/socket/qsctpsocket_p.h
new file mode 100644
index 0000000000..f38095330f
--- /dev/null
+++ b/src/network/socket/qsctpsocket_p.h
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
+** 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 QSCTPSOCKET_P_H
+#define QSCTPSOCKET_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 QLibrary class. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNetwork/qsctpsocket.h>
+#include <private/qtcpsocket_p.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qvector.h>
+#include <private/qnetworkdatagram_p.h>
+
+#include <deque>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_SCTP
+
+class QSctpSocketPrivate : public QTcpSocketPrivate
+{
+ Q_DECLARE_PUBLIC(QSctpSocket)
+public:
+ QSctpSocketPrivate();
+ virtual ~QSctpSocketPrivate();
+
+ bool canReadNotification() Q_DECL_OVERRIDE;
+ bool writeToSocket() Q_DECL_OVERRIDE;
+
+ QByteArray incomingDatagram;
+ int maxChannelCount;
+
+ typedef std::deque<QIpPacketHeader> IpHeaderList;
+ QVector<IpHeaderList> readHeaders;
+ QVector<IpHeaderList> writeHeaders;
+
+ void configureCreatedSocket() Q_DECL_OVERRIDE;
+};
+
+#endif // QT_NO_SCTP
+
+QT_END_NAMESPACE
+
+#endif // QSCTPSOCKET_P_H
diff --git a/src/network/socket/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp
index a57a1dca2c..a1a8e4649d 100644
--- a/src/network/socket/qsocks5socketengine.cpp
+++ b/src/network/socket/qsocks5socketengine.cpp
@@ -64,11 +64,7 @@ static const int MaxWriteBufferSize = 128*1024;
//#define QSOCKS5SOCKETLAYER_DEBUG
#define MAX_DATA_DUMP 256
-#if !defined(Q_OS_WINCE)
#define SOCKS5_BLOCKING_BIND_TIMEOUT 5000
-#else
-#define SOCKS5_BLOCKING_BIND_TIMEOUT 10000
-#endif
#define Q_INIT_CHECK(returnValue) do { \
if (!d->data) { \
@@ -622,7 +618,7 @@ void QSocks5SocketEnginePrivate::setErrorState(Socks5State state, const QString
QSocks5SocketEngine::tr("Connection to proxy timed out"));
break;
}
- /* fall through */
+ Q_FALLTHROUGH();
default:
q->setError(controlSocketError, data->controlSocket->errorString());
break;
@@ -1209,7 +1205,7 @@ void QSocks5SocketEnginePrivate::_q_controlSocketReadNotification()
break;
}
- // fall through
+ Q_FALLTHROUGH();
default:
qWarning("QSocks5SocketEnginePrivate::_q_controlSocketReadNotification: "
"Unexpectedly received data while in state=%d and mode=%d",
@@ -1609,8 +1605,31 @@ bool QSocks5SocketEngine::setMulticastInterface(const QNetworkInterface &)
}
#endif // QT_NO_NETWORKINTERFACE
+bool QSocks5SocketEngine::hasPendingDatagrams() const
+{
+ Q_D(const QSocks5SocketEngine);
+ Q_INIT_CHECK(false);
+
+ d->checkForDatagrams();
+
+ return !d->udpData->pendingDatagrams.isEmpty();
+}
+
+qint64 QSocks5SocketEngine::pendingDatagramSize() const
+{
+ Q_D(const QSocks5SocketEngine);
+
+ d->checkForDatagrams();
+
+ if (!d->udpData->pendingDatagrams.isEmpty())
+ return d->udpData->pendingDatagrams.head().data.size();
+ return 0;
+}
+#endif // QT_NO_UDPSOCKET
+
qint64 QSocks5SocketEngine::readDatagram(char *data, qint64 maxlen, QIpPacketHeader *header, PacketHeaderOptions)
{
+#ifndef QT_NO_UDPSOCKET
Q_D(QSocks5SocketEngine);
d->checkForDatagrams();
@@ -1624,10 +1643,17 @@ qint64 QSocks5SocketEngine::readDatagram(char *data, qint64 maxlen, QIpPacketHea
header->senderAddress = datagram.address;
header->senderPort = datagram.port;
return copyLen;
+#else
+ Q_UNUSED(data)
+ Q_UNUSED(maxlen)
+ Q_UNUSED(header)
+ return -1;
+#endif // QT_NO_UDPSOCKET
}
qint64 QSocks5SocketEngine::writeDatagram(const char *data, qint64 len, const QIpPacketHeader &header)
{
+#ifndef QT_NO_UDPSOCKET
Q_D(QSocks5SocketEngine);
// it is possible to send with out first binding with udp, but socks5 requires a bind.
@@ -1664,29 +1690,13 @@ qint64 QSocks5SocketEngine::writeDatagram(const char *data, qint64 len, const QI
}
return len;
-}
-
-bool QSocks5SocketEngine::hasPendingDatagrams() const
-{
- Q_D(const QSocks5SocketEngine);
- Q_INIT_CHECK(false);
-
- d->checkForDatagrams();
-
- return !d->udpData->pendingDatagrams.isEmpty();
-}
-
-qint64 QSocks5SocketEngine::pendingDatagramSize() const
-{
- Q_D(const QSocks5SocketEngine);
-
- d->checkForDatagrams();
-
- if (!d->udpData->pendingDatagrams.isEmpty())
- return d->udpData->pendingDatagrams.head().data.size();
- return 0;
-}
+#else
+ Q_UNUSED(data)
+ Q_UNUSED(len)
+ Q_UNUSED(header)
+ return -1;
#endif // QT_NO_UDPSOCKET
+}
qint64 QSocks5SocketEngine::bytesToWrite() const
{
diff --git a/src/network/socket/qsocks5socketengine_p.h b/src/network/socket/qsocks5socketengine_p.h
index 30caa4b50c..864b163489 100644
--- a/src/network/socket/qsocks5socketengine_p.h
+++ b/src/network/socket/qsocks5socketengine_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qabstractsocketengine_p.h"
#include "qnetworkproxy.h"
@@ -99,13 +100,13 @@ public:
bool setMulticastInterface(const QNetworkInterface &iface) Q_DECL_OVERRIDE;
#endif // QT_NO_NETWORKINTERFACE
- qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader * = 0,
- PacketHeaderOptions = WantNone) Q_DECL_OVERRIDE;
- qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &) Q_DECL_OVERRIDE;
bool hasPendingDatagrams() const Q_DECL_OVERRIDE;
qint64 pendingDatagramSize() const Q_DECL_OVERRIDE;
#endif // QT_NO_UDPSOCKET
+ qint64 readDatagram(char *data, qint64 maxlen, QIpPacketHeader * = 0,
+ PacketHeaderOptions = WantNone) Q_DECL_OVERRIDE;
+ qint64 writeDatagram(const char *data, qint64 len, const QIpPacketHeader &) Q_DECL_OVERRIDE;
qint64 bytesToWrite() const Q_DECL_OVERRIDE;
int option(SocketOption option) const Q_DECL_OVERRIDE;
diff --git a/src/network/socket/qtcpserver.cpp b/src/network/socket/qtcpserver.cpp
index de1dc29cfb..eddf789921 100644
--- a/src/network/socket/qtcpserver.cpp
+++ b/src/network/socket/qtcpserver.cpp
@@ -119,6 +119,7 @@ QT_BEGIN_NAMESPACE
*/
QTcpServerPrivate::QTcpServerPrivate()
: port(0)
+ , socketType(QAbstractSocket::UnknownSocketType)
, state(QAbstractSocket::UnconnectedState)
, socketEngine(0)
, serverSocketError(QAbstractSocket::UnknownSocketError)
@@ -148,13 +149,21 @@ QNetworkProxy QTcpServerPrivate::resolveProxy(const QHostAddress &address, quint
proxies << proxy;
} else {
// try the application settings instead
- QNetworkProxyQuery query(port, QString(), QNetworkProxyQuery::TcpServer);
+ QNetworkProxyQuery query(port, QString(),
+ socketType == QAbstractSocket::SctpSocket ?
+ QNetworkProxyQuery::SctpServer :
+ QNetworkProxyQuery::TcpServer);
proxies = QNetworkProxyFactory::proxyForQuery(query);
}
// return the first that we can use
for (const QNetworkProxy &p : qAsConst(proxies)) {
- if (p.capabilities() & QNetworkProxy::ListeningCapability)
+ if (socketType == QAbstractSocket::TcpSocket &&
+ (p.capabilities() & QNetworkProxy::ListeningCapability) != 0)
+ return p;
+
+ if (socketType == QAbstractSocket::SctpSocket &&
+ (p.capabilities() & QNetworkProxy::SctpListeningCapability) != 0)
return p;
}
@@ -228,9 +237,11 @@ void QTcpServerPrivate::readNotification()
QTcpServer::QTcpServer(QObject *parent)
: QObject(*new QTcpServerPrivate, parent)
{
+ Q_D(QTcpServer);
#if defined(QTCPSERVER_DEBUG)
qDebug("QTcpServer::QTcpServer(%p)", parent);
#endif
+ d->socketType = QAbstractSocket::TcpSocket;
}
/*!
@@ -251,13 +262,22 @@ QTcpServer::~QTcpServer()
}
/*! \internal
+
+ Constructs a new server object with socket of type \a socketType. The \a
+ parent argument is passed to QObject's constructor.
*/
-QTcpServer::QTcpServer(QTcpServerPrivate &dd, QObject *parent)
- : QObject(dd, parent)
+QTcpServer::QTcpServer(QAbstractSocket::SocketType socketType, QTcpServerPrivate &dd,
+ QObject *parent) : QObject(dd, parent)
{
+ Q_D(QTcpServer);
#if defined(QTCPSERVER_DEBUG)
- qDebug("QTcpServer::QTcpServer(QTcpServerPrivate == %p, parent == %p)", &dd, parent);
+ qDebug("QTcpServer::QTcpServer(%sSocket, QTcpServerPrivate == %p, parent == %p)",
+ socketType == QAbstractSocket::TcpSocket ? "Tcp"
+ : socketType == QAbstractSocket::UdpSocket ? "Udp"
+ : socketType == QAbstractSocket::SctpSocket ? "Sctp"
+ : "Unknown", &dd, parent);
#endif
+ d->socketType = socketType;
}
/*!
@@ -288,7 +308,7 @@ bool QTcpServer::listen(const QHostAddress &address, quint16 port)
#endif
delete d->socketEngine;
- d->socketEngine = QAbstractSocketEngine::createSocketEngine(QAbstractSocket::TcpSocket, proxy, this);
+ d->socketEngine = QAbstractSocketEngine::createSocketEngine(d->socketType, proxy, this);
if (!d->socketEngine) {
d->serverSocketError = QAbstractSocket::UnsupportedSocketOperationError;
d->serverSocketErrorString = tr("Operation on socket is not supported");
@@ -298,7 +318,7 @@ bool QTcpServer::listen(const QHostAddress &address, quint16 port)
//copy network session down to the socket engine (if it has been set)
d->socketEngine->setProperty("_q_networksession", property("_q_networksession"));
#endif
- if (!d->socketEngine->initialize(QAbstractSocket::TcpSocket, proto)) {
+ if (!d->socketEngine->initialize(d->socketType, proto)) {
d->serverSocketError = d->socketEngine->error();
d->serverSocketErrorString = d->socketEngine->errorString();
return false;
@@ -543,8 +563,11 @@ QTcpSocket *QTcpServer::nextPendingConnection()
if (d->pendingConnections.isEmpty())
return 0;
- if (!d->socketEngine->isReadNotificationEnabled())
+ if (!d->socketEngine) {
+ qWarning("QTcpServer::nextPendingConnection() called while not listening");
+ } else if (!d->socketEngine->isReadNotificationEnabled()) {
d->socketEngine->setReadNotificationEnabled(true);
+ }
return d->pendingConnections.takeFirst();
}
diff --git a/src/network/socket/qtcpserver.h b/src/network/socket/qtcpserver.h
index 90781564b4..192cbce54c 100644
--- a/src/network/socket/qtcpserver.h
+++ b/src/network/socket/qtcpserver.h
@@ -40,6 +40,7 @@
#ifndef QTCPSERVER_H
#define QTCPSERVER_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qobject.h>
#include <QtNetwork/qabstractsocket.h>
#include <QtNetwork/qhostaddress.h>
@@ -93,7 +94,8 @@ protected:
virtual void incomingConnection(qintptr handle);
void addPendingConnection(QTcpSocket* socket);
- QTcpServer(QTcpServerPrivate &dd, QObject *parent = Q_NULLPTR);
+ QTcpServer(QAbstractSocket::SocketType socketType, QTcpServerPrivate &dd,
+ QObject *parent = Q_NULLPTR);
Q_SIGNALS:
void newConnection();
diff --git a/src/network/socket/qtcpserver_p.h b/src/network/socket/qtcpserver_p.h
index b53f53b921..71dc4d985f 100644
--- a/src/network/socket/qtcpserver_p.h
+++ b/src/network/socket/qtcpserver_p.h
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
+** Copyright (C) 2016 Alex Trotsenko <alex1973tr@gmail.com>
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtNetwork module of the Qt Toolkit.
@@ -44,13 +45,14 @@
// W A R N I N G
// -------------
//
-// This file is not part of the Qt API. It exists for the convenience
-// of the QLibrary class. This header file may change from
-// version to version without notice, or even be removed.
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
//
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "QtNetwork/qtcpserver.h"
#include "private/qobject_p.h"
#include "private/qabstractsocketengine_p.h"
@@ -73,6 +75,7 @@ public:
quint16 port;
QHostAddress address;
+ QAbstractSocket::SocketType socketType;
QAbstractSocket::SocketState state;
QAbstractSocketEngine *socketEngine;
diff --git a/src/network/socket/qtcpsocket.h b/src/network/socket/qtcpsocket.h
index b2c89dcad2..3c3e3b69fd 100644
--- a/src/network/socket/qtcpsocket.h
+++ b/src/network/socket/qtcpsocket.h
@@ -40,6 +40,7 @@
#ifndef QTCPSOCKET_H
#define QTCPSOCKET_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtNetwork/qabstractsocket.h>
#include <QtCore/qvariant.h>
diff --git a/src/network/socket/qtcpsocket_p.h b/src/network/socket/qtcpsocket_p.h
index dfa55a6109..ba1a0aa920 100644
--- a/src/network/socket/qtcpsocket_p.h
+++ b/src/network/socket/qtcpsocket_p.h
@@ -44,13 +44,14 @@
// W A R N I N G
// -------------
//
-// This file is not part of the Qt API. It exists for the convenience
-// of the QLibrary class. This header file may change from
-// version to version without notice, or even be removed.
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
//
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <QtNetwork/qtcpsocket.h>
#include <private/qabstractsocket_p.h>
diff --git a/src/network/socket/qudpsocket.cpp b/src/network/socket/qudpsocket.cpp
index c406009069..37b385dfb5 100644
--- a/src/network/socket/qudpsocket.cpp
+++ b/src/network/socket/qudpsocket.cpp
@@ -55,10 +55,10 @@
datagrams.
The most common way to use this class is to bind to an address and port
- using bind(), then call writeDatagram() and readDatagram() to transfer
- data. If you want to use the standard QIODevice functions read(),
- readLine(), write(), etc., you must first connect the socket directly to a
- peer by calling connectToHost().
+ using bind(), then call writeDatagram() and readDatagram() /
+ receiveDatagram() to transfer data. If you want to use the standard
+ QIODevice functions read(), readLine(), write(), etc., you must first
+ connect the socket directly to a peer by calling connectToHost().
The socket emits the bytesWritten() signal every time a datagram
is written to the network. If you just want to send datagrams,
@@ -67,7 +67,7 @@
The readyRead() signal is emitted whenever datagrams arrive. In
that case, hasPendingDatagrams() returns \c true. Call
pendingDatagramSize() to obtain the size of the first pending
- datagram, and readDatagram() to read it.
+ datagram, and readDatagram() or receiveDatagram() to read it.
\note An incoming datagram should be read when you receive the readyRead()
signal, otherwise this signal will not be emitted for the next datagram.
@@ -94,11 +94,12 @@
\l{multicastreceiver}{Multicast Receiver} examples illustrate how
to use QUdpSocket in applications.
- \sa QTcpSocket
+ \sa QTcpSocket, QNetworkDatagram
*/
#include "qudpsocket.h"
#include "qhostaddress.h"
+#include "qnetworkdatagram.h"
#include "qnetworkinterface.h"
#include "qabstractsocket_p.h"
@@ -346,6 +347,12 @@ qint64 QUdpSocket::writeDatagram(const char *data, qint64 size, const QHostAddre
if (sent >= 0) {
emit bytesWritten(sent);
} else {
+ if (sent == -2) {
+ // Socket engine reports EAGAIN. Treat as a temporary error.
+ d->setErrorAndEmit(QAbstractSocket::TemporaryError,
+ tr("Unable to send a datagram"));
+ return -1;
+ }
d->setErrorAndEmit(d->socketEngine->error(), d->socketEngine->errorString());
}
return sent;
@@ -358,7 +365,101 @@ qint64 QUdpSocket::writeDatagram(const char *data, qint64 size, const QHostAddre
Sends the datagram \a datagram to the host address \a host and at
port \a port.
+
+ The function returns the number of bytes sent if it succeeded or -1 if it
+ encountered an error.
+*/
+
+/*!
+ \overload
+
+ Sends the datagram \a datagram to the host address and port numbers
+ contained in \a datagram, using the network interface and hop count limits
+ also set there. If the destination address and port numbers are unset, this
+ function will send to the address that was passed to connectToHost().
+
+ If the destination address is IPv6 with a non-empty
+ \l{QHostAddress::scopeId()}{scope id} but differs from the interface index
+ in \a datagram, it is undefined which interface the operating system will
+ choose to send on.
+
+ The function returns the number of bytes sent if it succeeded or -1 if it
+ encountered an error.
+
+ \warning Calling this function on a connected UDP socket may
+ result in an error and no packet being sent. If you are using a
+ connected socket, use write() to send datagrams.
+
+ \sa QNetworkDatagram::setDestination(), QNetworkDatagram::setHopLimit(), QNetworkDatagram::setInterfaceIndex()
+*/
+qint64 QUdpSocket::writeDatagram(const QNetworkDatagram &datagram)
+{
+ Q_D(QUdpSocket);
+#if defined QUDPSOCKET_DEBUG
+ qDebug("QUdpSocket::writeDatagram(%p, %i, \"%s\", %i)",
+ datagram.d->data.constData(),
+ datagram.d->data.size(),
+ datagram.destinationAddress().toString().toLatin1().constData(),
+ datagram.destinationPort());
+#endif
+ if (!d->doEnsureInitialized(QHostAddress::Any, 0, datagram.destinationAddress()))
+ return -1;
+ if (state() == UnconnectedState)
+ bind();
+
+ qint64 sent = d->socketEngine->writeDatagram(datagram.d->data,
+ datagram.d->data.size(),
+ datagram.d->header);
+ d->cachedSocketDescriptor = d->socketEngine->socketDescriptor();
+
+ if (sent >= 0) {
+ emit bytesWritten(sent);
+ } else {
+ d->setErrorAndEmit(d->socketEngine->error(), d->socketEngine->errorString());
+ }
+ return sent;
+}
+
+/*!
+ Receives a datagram no larger than \a maxSize bytes and returns it in the
+ QNetworkDatagram object, along with the sender's host address and port. If
+ possible, this function will also try to determine the datagram's
+ destination address, port, and the number of hop counts at reception time.
+
+ On failure, returns a QNetworkDatagram that reports \l
+ {QNetworkDatagram::isValid()}{not valid}.
+
+ If \a maxSize is too small, the rest of the datagram will be lost. If \a
+ maxSize is 0, the datagram will be discarded. If \a maxSize is -1 (the
+ default), this function will attempt to read the entire datagram.
+
+ \sa writeDatagram(), hasPendingDatagrams(), pendingDatagramSize()
*/
+QNetworkDatagram QUdpSocket::receiveDatagram(qint64 maxSize)
+{
+ Q_D(QUdpSocket);
+
+#if defined QUDPSOCKET_DEBUG
+ qDebug("QUdpSocket::receiveDatagram(%lld)", maxSize);
+#endif
+ QT_CHECK_BOUND("QUdpSocket::receiveDatagram()", QNetworkDatagram());
+
+ if (maxSize < 0)
+ maxSize = d->socketEngine->pendingDatagramSize();
+ if (maxSize < 0)
+ return QNetworkDatagram();
+
+ QNetworkDatagram result(QByteArray(maxSize, Qt::Uninitialized));
+ qint64 readBytes = d->socketEngine->readDatagram(result.d->data.data(), maxSize, &result.d->header,
+ QAbstractSocketEngine::WantAll);
+ d->hasPendingData = false;
+ d->socketEngine->setReadNotificationEnabled(true);
+ if (readBytes < 0)
+ d->setErrorAndEmit(d->socketEngine->error(), d->socketEngine->errorString());
+ else if (readBytes != result.d->data.size())
+ result.d->data.truncate(readBytes);
+ return result;
+}
/*!
Receives a datagram no larger than \a maxSize bytes and stores
@@ -398,11 +499,20 @@ qint64 QUdpSocket::readDatagram(char *data, qint64 maxSize, QHostAddress *addres
readBytes = d->socketEngine->readDatagram(data, maxSize);
}
- d_func()->socketEngine->setReadNotificationEnabled(true);
- if (readBytes < 0)
+ d->hasPendingData = false;
+ d->socketEngine->setReadNotificationEnabled(true);
+ if (readBytes < 0) {
+ if (readBytes == -2) {
+ // No pending datagram. Treat as a temporary error.
+ d->setErrorAndEmit(QAbstractSocket::TemporaryError,
+ tr("No datagram available for reading"));
+ return -1;
+ }
d->setErrorAndEmit(d->socketEngine->error(), d->socketEngine->errorString());
+ }
return readBytes;
}
+
#endif // QT_NO_UDPSOCKET
QT_END_NAMESPACE
diff --git a/src/network/socket/qudpsocket.h b/src/network/socket/qudpsocket.h
index cf2f05c86d..6ef10e2edb 100644
--- a/src/network/socket/qudpsocket.h
+++ b/src/network/socket/qudpsocket.h
@@ -40,6 +40,7 @@
#ifndef QUDPSOCKET_H
#define QUDPSOCKET_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtNetwork/qabstractsocket.h>
#include <QtNetwork/qhostaddress.h>
@@ -48,6 +49,7 @@ QT_BEGIN_NAMESPACE
#ifndef QT_NO_UDPSOCKET
+class QNetworkDatagram;
class QNetworkInterface;
class QUdpSocketPrivate;
@@ -72,7 +74,10 @@ public:
bool hasPendingDatagrams() const;
qint64 pendingDatagramSize() const;
+ QNetworkDatagram receiveDatagram(qint64 maxSize = -1);
qint64 readDatagram(char *data, qint64 maxlen, QHostAddress *host = Q_NULLPTR, quint16 *port = Q_NULLPTR);
+
+ qint64 writeDatagram(const QNetworkDatagram &datagram);
qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &host, quint16 port);
inline qint64 writeDatagram(const QByteArray &datagram, const QHostAddress &host, quint16 port)
{ return writeDatagram(datagram.constData(), datagram.size(), host, port); }
diff --git a/src/network/socket/socket.pri b/src/network/socket/socket.pri
index f50a7b1229..793ea60b07 100644
--- a/src/network/socket/socket.pri
+++ b/src/network/socket/socket.pri
@@ -25,6 +25,18 @@ SOURCES += socket/qabstractsocketengine.cpp \
socket/qlocalsocket.cpp \
socket/qlocalserver.cpp
+# SCTP support.
+
+qtConfig(sctp) {
+ HEADERS += socket/qsctpserver.h \
+ socket/qsctpserver_p.h \
+ socket/qsctpsocket.h \
+ socket/qsctpsocket_p.h
+
+ SOURCES += socket/qsctpserver.cpp \
+ socket/qsctpsocket.cpp
+}
+
!winrt {
SOURCES += socket/qnativesocketengine.cpp
HEADERS += socket/qnativesocketengine_p.h
@@ -39,11 +51,15 @@ unix: {
unix:HEADERS += \
socket/qnet_unix_p.h
+# Suppress deprecation warnings with moc because MS headers have
+# invalid C/C++ code otherwise.
+msvc: QMAKE_MOC_OPTIONS += -D_WINSOCK_DEPRECATED_NO_WARNINGS
+
win32:!winrt:SOURCES += socket/qnativesocketengine_win.cpp \
socket/qlocalsocket_win.cpp \
socket/qlocalserver_win.cpp
-win32:!wince:!winrt:LIBS_PRIVATE += -ladvapi32
+win32:!winrt:LIBS_PRIVATE += -ladvapi32
winrt {
SOURCES += socket/qnativesocketengine_winrt.cpp \
@@ -54,15 +70,6 @@ winrt {
DEFINES += QT_LOCALSOCKET_TCP
}
-wince {
- SOURCES -= socket/qlocalsocket_win.cpp \
- socket/qlocalserver_win.cpp
- SOURCES += socket/qlocalsocket_tcp.cpp \
- socket/qlocalserver_tcp.cpp
-
- DEFINES += QT_LOCALSOCKET_TCP
-}
-
integrity: {
SOURCES -= socket/qlocalsocket_unix.cpp \
socket/qlocalserver_unix.cpp
@@ -73,6 +80,6 @@ integrity: {
DEFINES += QT_LOCALSOCKET_TCP
}
-contains(QT_CONFIG, system-proxies) {
+qtConfig(system-proxies) {
DEFINES += QT_USE_SYSTEM_PROXIES
}
diff --git a/src/network/ssl/qasn1element_p.h b/src/network/ssl/qasn1element_p.h
index c636351bf9..2c5019b4f7 100644
--- a/src/network/ssl/qasn1element_p.h
+++ b/src/network/ssl/qasn1element_p.h
@@ -45,13 +45,14 @@
// W A R N I N G
// -------------
//
-// This file is not part of the Qt API. It exists for the convenience
-// of the QLibrary class. This header file may change from
-// version to version without notice, or even be removed.
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
//
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <QtCore/qdatetime.h>
#include <QtCore/qmap.h>
diff --git a/src/network/ssl/qssl.h b/src/network/ssl/qssl.h
index 7742844246..c2a468c97c 100644
--- a/src/network/ssl/qssl.h
+++ b/src/network/ssl/qssl.h
@@ -41,7 +41,7 @@
#ifndef QSSL_H
#define QSSL_H
-#include <QtCore/qglobal.h>
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/QFlags>
QT_BEGIN_NAMESPACE
diff --git a/src/network/ssl/qssl_p.h b/src/network/ssl/qssl_p.h
index 85eb59ae35..83ccdc7fc3 100644
--- a/src/network/ssl/qssl_p.h
+++ b/src/network/ssl/qssl_p.h
@@ -53,6 +53,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <QtCore/QLoggingCategory>
QT_BEGIN_NAMESPACE
diff --git a/src/network/ssl/qsslcertificate.cpp b/src/network/ssl/qsslcertificate.cpp
index 96ba68089d..ce78399e01 100644
--- a/src/network/ssl/qsslcertificate.cpp
+++ b/src/network/ssl/qsslcertificate.cpp
@@ -111,7 +111,7 @@
\value EmailAddress The email address associated with the certificate
*/
-#include <QtCore/qglobal.h>
+#include <QtNetwork/qtnetworkglobal.h>
#ifndef QT_NO_OPENSSL
#include "qsslsocket_openssl_symbols_p.h"
#endif
diff --git a/src/network/ssl/qsslcertificate.h b/src/network/ssl/qsslcertificate.h
index 814c6a701d..8b051a5c88 100644
--- a/src/network/ssl/qsslcertificate.h
+++ b/src/network/ssl/qsslcertificate.h
@@ -45,6 +45,7 @@
#undef verify
#endif
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qnamespace.h>
#include <QtCore/qbytearray.h>
#include <QtCore/qcryptographichash.h>
diff --git a/src/network/ssl/qsslcertificate_p.h b/src/network/ssl/qsslcertificate_p.h
index 8b83f754e4..0397845f8d 100644
--- a/src/network/ssl/qsslcertificate_p.h
+++ b/src/network/ssl/qsslcertificate_p.h
@@ -41,15 +41,16 @@
#ifndef QSSLCERTIFICATE_OPENSSL_P_H
#define QSSLCERTIFICATE_OPENSSL_P_H
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qsslcertificate.h"
//
// W A R N I N G
// -------------
//
-// This file is not part of the Qt API. It exists for the convenience
-// of the QLibrary class. This header file may change from
-// version to version without notice, or even be removed.
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
//
// We mean it.
//
diff --git a/src/network/ssl/qsslcertificateextension.h b/src/network/ssl/qsslcertificateextension.h
index 795dd0ce80..2ce2112687 100644
--- a/src/network/ssl/qsslcertificateextension.h
+++ b/src/network/ssl/qsslcertificateextension.h
@@ -40,6 +40,7 @@
#ifndef QSSLCERTIFICATEEXTENSION_H
#define QSSLCERTIFICATEEXTENSION_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qnamespace.h>
#include <QtCore/qshareddata.h>
#include <QtCore/qstring.h>
diff --git a/src/network/ssl/qsslcertificateextension_p.h b/src/network/ssl/qsslcertificateextension_p.h
index a7ff53d4a3..373f92a5cf 100644
--- a/src/network/ssl/qsslcertificateextension_p.h
+++ b/src/network/ssl/qsslcertificateextension_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qsslcertificateextension.h"
QT_BEGIN_NAMESPACE
diff --git a/src/network/ssl/qsslcipher.h b/src/network/ssl/qsslcipher.h
index 8bb404ffa7..c6328e0169 100644
--- a/src/network/ssl/qsslcipher.h
+++ b/src/network/ssl/qsslcipher.h
@@ -41,6 +41,7 @@
#ifndef QSSLCIPHER_H
#define QSSLCIPHER_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qstring.h>
#include <QtCore/qscopedpointer.h>
#include <QtNetwork/qssl.h>
diff --git a/src/network/ssl/qsslcipher_p.h b/src/network/ssl/qsslcipher_p.h
index 1714617361..b8629f9f96 100644
--- a/src/network/ssl/qsslcipher_p.h
+++ b/src/network/ssl/qsslcipher_p.h
@@ -40,6 +40,7 @@
#ifndef QSSLCIPHER_P_H
#define QSSLCIPHER_P_H
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qsslcipher.h"
QT_BEGIN_NAMESPACE
diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp
index c8040dec7f..75a880f115 100644
--- a/src/network/ssl/qsslconfiguration.cpp
+++ b/src/network/ssl/qsslconfiguration.cpp
@@ -53,6 +53,7 @@ const QSsl::SslOptions QSslConfigurationPrivate::defaultSslOptions = QSsl::SslOp
|QSsl::SslOptionDisableCompression
|QSsl::SslOptionDisableSessionPersistence;
+const char QSslConfiguration::ALPNProtocolHTTP2[] = "h2";
const char QSslConfiguration::NextProtocolSpdy3_0[] = "spdy/3";
const char QSslConfiguration::NextProtocolHttp1_1[] = "http/1.1";
@@ -119,7 +120,8 @@ const char QSslConfiguration::NextProtocolHttp1_1[] = "http/1.1";
/*!
\enum QSslConfiguration::NextProtocolNegotiationStatus
- Describes the status of the Next Protocol Negotiation (NPN).
+ Describes the status of the Next Protocol Negotiation (NPN) or
+ Application-Layer Protocol Negotiation (ALPN).
\value NextProtocolNegotiationNone No application protocol
has been negotiated (yet).
@@ -209,9 +211,11 @@ bool QSslConfiguration::operator==(const QSslConfiguration &other) const
d->privateKey == other.d->privateKey &&
d->sessionCipher == other.d->sessionCipher &&
d->sessionProtocol == other.d->sessionProtocol &&
+ d->preSharedKeyIdentityHint == other.d->preSharedKeyIdentityHint &&
d->ciphers == other.d->ciphers &&
d->ellipticCurves == other.d->ellipticCurves &&
d->ephemeralServerKey == other.d->ephemeralServerKey &&
+ d->dhParams == other.d->dhParams &&
d->caCertificates == other.d->caCertificates &&
d->protocol == other.d->protocol &&
d->peerVerifyMode == other.d->peerVerifyMode &&
@@ -254,6 +258,7 @@ bool QSslConfiguration::isNull() const
d->ciphers.count() == 0 &&
d->ellipticCurves.isEmpty() &&
d->ephemeralServerKey.isNull() &&
+ d->dhParams == QSslDiffieHellmanParameters::defaultParameters() &&
d->localCertificateChain.isEmpty() &&
d->privateKey.isNull() &&
d->peerCertificate.isNull() &&
@@ -261,6 +266,7 @@ bool QSslConfiguration::isNull() const
d->sslOptions == QSslConfigurationPrivate::defaultSslOptions &&
d->sslSession.isNull() &&
d->sslSessionTicketLifeTimeHint == -1 &&
+ d->preSharedKeyIdentityHint.isNull() &&
d->nextAllowedProtocols.isEmpty() &&
d->nextNegotiatedProtocol.isNull() &&
d->nextProtocolNegotiationStatus == QSslConfiguration::NextProtocolNegotiationNone);
@@ -811,11 +817,65 @@ QVector<QSslEllipticCurve> QSslConfiguration::supportedEllipticCurves()
}
/*!
+ \since 5.8
+
+ Returns the identity hint.
+
+ \sa setPreSharedKeyIdentityHint()
+*/
+QByteArray QSslConfiguration::preSharedKeyIdentityHint() const
+{
+ return d->preSharedKeyIdentityHint;
+}
+
+/*!
+ \since 5.8
+
+ Sets the identity hint for a preshared key authentication to \a hint. This will
+ affect the next initiated handshake; calling this function on an already-encrypted
+ socket will not affect the socket's identity hint.
+
+ The identity hint is used in QSslSocket::SslServerMode only!
+*/
+void QSslConfiguration::setPreSharedKeyIdentityHint(const QByteArray &hint)
+{
+ d->preSharedKeyIdentityHint = hint;
+}
+
+/*!
+ \since 5.8
+
+ Retrieves the current set of Diffie-Hellman parameters.
+
+ If no Diffie-Hellman parameters have been set, the QSslConfiguration object
+ defaults to using the 1024-bit MODP group from RFC 2409.
+ */
+QSslDiffieHellmanParameters QSslConfiguration::diffieHellmanParameters() const
+{
+ return d->dhParams;
+}
+
+/*!
+ \since 5.8
+
+ Sets a custom set of Diffie-Hellman parameters to be used by this socket when functioning as
+ a server to \a dhparams.
+
+ If no Diffie-Hellman parameters have been set, the QSslConfiguration object
+ defaults to using the 1024-bit MODP group from RFC 2409.
+ */
+void QSslConfiguration::setDiffieHellmanParameters(const QSslDiffieHellmanParameters &dhparams)
+{
+ d->dhParams = dhparams;
+}
+
+/*!
\since 5.3
This function returns the protocol negotiated with the server
- if the Next Protocol Negotiation (NPN) TLS extension was enabled.
- In order for the NPN extension to be enabled, setAllowedNextProtocols()
+ if the Next Protocol Negotiation (NPN) or Application-Layer Protocol
+ Negotiation (ALPN) TLS extension was enabled.
+ In order for the NPN/ALPN extension to be enabled, setAllowedNextProtocols()
needs to be called explicitly before connecting to the server.
If no protocol could be negotiated or the extension was not enabled,
@@ -832,9 +892,10 @@ QByteArray QSslConfiguration::nextNegotiatedProtocol() const
\since 5.3
This function sets the allowed \a protocols to be negotiated with the
- server through the Next Protocol Negotiation (NPN) TLS extension; each
+ server through the Next Protocol Negotiation (NPN) or Application-Layer
+ Protocol Negotiation (ALPN) TLS extension; each
element in \a protocols must define one allowed protocol.
- The function must be called explicitly before connecting to send the NPN
+ The function must be called explicitly before connecting to send the NPN/ALPN
extension in the SSL handshake.
Whether or not the negotiation succeeded can be queried through
nextProtocolNegotiationStatus().
@@ -854,8 +915,8 @@ void QSslConfiguration::setAllowedNextProtocols(QList<QByteArray> protocols)
\since 5.3
This function returns the allowed protocols to be negotiated with the
- server through the Next Protocol Negotiation (NPN) TLS extension, as set
- by setAllowedNextProtocols().
+ server through the Next Protocol Negotiation (NPN) or Application-Layer
+ Protocol Negotiation (ALPN) TLS extension, as set by setAllowedNextProtocols().
\sa nextNegotiatedProtocol(), nextProtocolNegotiationStatus(), setAllowedNextProtocols(), QSslConfiguration::NextProtocolSpdy3_0, QSslConfiguration::NextProtocolHttp1_1
*/
@@ -867,7 +928,8 @@ QList<QByteArray> QSslConfiguration::allowedNextProtocols() const
/*!
\since 5.3
- This function returns the status of the Next Protocol Negotiation (NPN).
+ This function returns the status of the Next Protocol Negotiation (NPN)
+ or Application-Layer Protocol Negotiation (ALPN).
If the feature has not been enabled through setAllowedNextProtocols(),
this function returns NextProtocolNegotiationNone.
The status will be set before emitting the encrypted() signal.
diff --git a/src/network/ssl/qsslconfiguration.h b/src/network/ssl/qsslconfiguration.h
index f0754d7ef5..1c57bebd65 100644
--- a/src/network/ssl/qsslconfiguration.h
+++ b/src/network/ssl/qsslconfiguration.h
@@ -56,6 +56,7 @@
#ifndef QSSLCONFIGURATION_H
#define QSSLCONFIGURATION_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qshareddata.h>
#include <QtNetwork/qsslsocket.h>
#include <QtNetwork/qssl.h>
@@ -69,6 +70,7 @@ class QSslCertificate;
class QSslCipher;
class QSslKey;
class QSslEllipticCurve;
+class QSslDiffieHellmanParameters;
class QSslConfigurationPrivate;
class Q_NETWORK_EXPORT QSslConfiguration
@@ -141,6 +143,12 @@ public:
void setEllipticCurves(const QVector<QSslEllipticCurve> &curves);
static QVector<QSslEllipticCurve> supportedEllipticCurves();
+ QByteArray preSharedKeyIdentityHint() const;
+ void setPreSharedKeyIdentityHint(const QByteArray &hint);
+
+ QSslDiffieHellmanParameters diffieHellmanParameters() const;
+ void setDiffieHellmanParameters(const QSslDiffieHellmanParameters &dhparams);
+
static QSslConfiguration defaultConfiguration();
static void setDefaultConfiguration(const QSslConfiguration &configuration);
@@ -160,6 +168,7 @@ public:
QByteArray nextNegotiatedProtocol() const;
NextProtocolNegotiationStatus nextProtocolNegotiationStatus() const;
+ static const char ALPNProtocolHTTP2[];
static const char NextProtocolSpdy3_0[];
static const char NextProtocolHttp1_1[];
diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h
index 364bba92ec..6adf2c9b54 100644
--- a/src/network/ssl/qsslconfiguration_p.h
+++ b/src/network/ssl/qsslconfiguration_p.h
@@ -67,12 +67,14 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qsslconfiguration.h"
#include "qlist.h"
#include "qsslcertificate.h"
#include "qsslcipher.h"
#include "qsslkey.h"
#include "qsslellipticcurve.h"
+#include "qssldiffiehellmanparameters.h"
QT_BEGIN_NAMESPACE
@@ -87,8 +89,10 @@ public:
allowRootCertOnDemandLoading(true),
peerSessionShared(false),
sslOptions(QSslConfigurationPrivate::defaultSslOptions),
+ dhParams(QSslDiffieHellmanParameters::defaultParameters()),
sslSessionTicketLifeTimeHint(-1),
ephemeralServerKey(),
+ preSharedKeyIdentityHint(),
nextProtocolNegotiationStatus(QSslConfiguration::NextProtocolNegotiationNone)
{ }
@@ -117,11 +121,15 @@ public:
QVector<QSslEllipticCurve> ellipticCurves;
+ QSslDiffieHellmanParameters dhParams;
+
QByteArray sslSession;
int sslSessionTicketLifeTimeHint;
QSslKey ephemeralServerKey;
+ QByteArray preSharedKeyIdentityHint;
+
QList<QByteArray> nextAllowedProtocols;
QByteArray nextNegotiatedProtocol;
QSslConfiguration::NextProtocolNegotiationStatus nextProtocolNegotiationStatus;
diff --git a/src/network/ssl/qsslcontext_openssl.cpp b/src/network/ssl/qsslcontext_openssl.cpp
index f132d0228c..c92d8fc3f8 100644
--- a/src/network/ssl/qsslcontext_openssl.cpp
+++ b/src/network/ssl/qsslcontext_openssl.cpp
@@ -41,6 +41,7 @@
#include <QtNetwork/qsslsocket.h>
+#include <QtNetwork/qssldiffiehellmanparameters.h>
#include <QtCore/qmutex.h>
#include "private/qssl_p.h"
@@ -48,6 +49,7 @@
#include "private/qsslsocket_p.h"
#include "private/qsslsocket_openssl_p.h"
#include "private/qsslsocket_openssl_symbols_p.h"
+#include "private/qssldiffiehellmanparameters_p.h"
QT_BEGIN_NAMESPACE
@@ -55,22 +57,6 @@ QT_BEGIN_NAMESPACE
extern int q_X509Callback(int ok, X509_STORE_CTX *ctx);
extern QString getErrorsFromOpenSsl();
-static DH *get_dh1024()
-{
- // Default DH params
- // 1024-bit MODP Group
- // From RFC 2409
- QByteArray params = QByteArray::fromBase64(
- QByteArrayLiteral("MIGHAoGBAP//////////yQ/aoiFowjTExmKLgNwc0SkCTgiKZ8x0Agu+pjsTmyJR" \
- "Sgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHCReSFtXZiXn7G9ExC6aY37WsL" \
- "/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR7OZTgf//////////AgEC"));
-
- const char *ptr = params.constData();
- DH *dh = q_d2i_DHparams(NULL, reinterpret_cast<const unsigned char **>(&ptr), params.length());
-
- return dh;
-}
-
QSslContext::QSslContext()
: ctx(0),
pkey(0),
@@ -325,10 +311,23 @@ init_context:
sslContext->setSessionASN1(configuration.sessionTicket());
// Set temp DH params
- DH *dh = 0;
- dh = get_dh1024();
- q_SSL_CTX_set_tmp_dh(sslContext->ctx, dh);
- q_DH_free(dh);
+ QSslDiffieHellmanParameters dhparams = configuration.diffieHellmanParameters();
+
+ if (!dhparams.isValid()) {
+ sslContext->errorStr = QSslSocket::tr("Diffie-Hellman parameters are not valid");
+ sslContext->errorCode = QSslError::UnspecifiedError;
+ return;
+ }
+
+ if (!dhparams.isEmpty()) {
+ const QByteArray &params = dhparams.d->derData;
+ const char *ptr = params.constData();
+ DH *dh = q_d2i_DHparams(NULL, reinterpret_cast<const unsigned char **>(&ptr), params.length());
+ if (dh == NULL)
+ qFatal("q_d2i_DHparams failed to convert QSslDiffieHellmanParameters to DER form");
+ q_SSL_CTX_set_tmp_dh(sslContext->ctx, dh);
+ q_DH_free(dh);
+ }
#ifndef OPENSSL_NO_EC
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
@@ -345,6 +344,11 @@ init_context:
}
#endif // OPENSSL_NO_EC
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK)
+ if (!client)
+ q_SSL_CTX_use_psk_identity_hint(sslContext->ctx, sslContext->sslConfiguration.preSharedKeyIdentityHint().constData());
+#endif // OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK)
+
const QVector<QSslEllipticCurve> qcurves = sslContext->sslConfiguration.ellipticCurves();
if (!qcurves.isEmpty()) {
#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(OPENSSL_NO_EC)
@@ -458,6 +462,23 @@ SSL* QSslContext::createSsl()
m_npnContext.data = reinterpret_cast<unsigned char *>(m_supportedNPNVersions.data());
m_npnContext.len = m_supportedNPNVersions.count();
m_npnContext.status = QSslConfiguration::NextProtocolNegotiationNone;
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ if (q_SSLeay() >= 0x10002000L) {
+ // Callback's type has a parameter 'const unsigned char ** out'
+ // since it was introduced in 1.0.2. Internally, OpenSSL's own code
+ // (tests/examples) cast it to unsigned char * (since it's 'out').
+ // We just re-use our NPN callback and cast here:
+ typedef int (*alpn_callback_t) (SSL *, const unsigned char **, unsigned char *,
+ const unsigned char *, unsigned int, void *);
+ // With ALPN callback is for a server side only, for a client m_npnContext.status
+ // will stay in NextProtocolNegotiationNone.
+ q_SSL_CTX_set_alpn_select_cb(ctx, alpn_callback_t(next_proto_cb), &m_npnContext);
+ // Client:
+ q_SSL_set_alpn_protos(ssl, m_npnContext.data, m_npnContext.len);
+ }
+#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L ...
+
+ // And in case our peer does not support ALPN, but supports NPN:
q_SSL_CTX_set_next_proto_select_cb(ctx, next_proto_cb, &m_npnContext);
}
#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
diff --git a/src/network/ssl/qsslcontext_openssl_p.h b/src/network/ssl/qsslcontext_openssl_p.h
index ef4dc6f815..06a31af5e5 100644
--- a/src/network/ssl/qsslcontext_openssl_p.h
+++ b/src/network/ssl/qsslcontext_openssl_p.h
@@ -53,6 +53,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <QtCore/qvariant.h>
#include <QtNetwork/qsslcertificate.h>
#include <QtNetwork/qsslconfiguration.h>
diff --git a/src/network/ssl/qssldiffiehellmanparameters.cpp b/src/network/ssl/qssldiffiehellmanparameters.cpp
new file mode 100644
index 0000000000..d0fcb3189a
--- /dev/null
+++ b/src/network/ssl/qssldiffiehellmanparameters.cpp
@@ -0,0 +1,324 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk>
+** Contact: http://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$
+**
+****************************************************************************/
+
+
+/*!
+ \class QSslDiffieHellmanParameters
+ \brief The QSslDiffieHellmanParameters class provides an interface for Diffie-Hellman parameters for servers.
+ \since 5.8
+
+ \reentrant
+ \ingroup network
+ \ingroup ssl
+ \ingroup shared
+ \inmodule QtNetwork
+
+ QSslDiffieHellmanParameters provides an interface for setting Diffie-Hellman parameters to servers based on QSslSocket.
+
+ \sa QSslSocket, QSslCipher, QSslConfiguration
+*/
+
+#include "qssldiffiehellmanparameters.h"
+#include "qssldiffiehellmanparameters_p.h"
+#include "qsslsocket.h"
+#include "qsslsocket_p.h"
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qatomic.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qbytearraymatcher.h>
+#include <QtCore/qiodevice.h>
+#include <QtCore/qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ Returns the default QSslDiffieHellmanParameters used by QSslSocket.
+
+ This is currently the 1024-bit MODP group from RFC 2459, also
+ known as the Second Oakley Group.
+*/
+QSslDiffieHellmanParameters QSslDiffieHellmanParameters::defaultParameters()
+{
+ // The 1024-bit MODP group from RFC 2459 (Second Oakley Group)
+ return fromEncoded(
+ QByteArray::fromBase64(QByteArrayLiteral(
+ "MIGHAoGBAP//////////yQ/aoiFowjTExmKLgNwc0SkCTgiKZ8x0Agu+pjsTmyJR"
+ "Sgh5jjQE3e+VGbPNOkMbMCsKbfJfFDdP4TVtbVHCReSFtXZiXn7G9ExC6aY37WsL"
+ "/1y29Aa37e44a/taiZ+lrp8kEXxLH+ZJKGZR7OZTgf//////////AgEC"
+ )),
+ QSsl::Der
+ );
+}
+
+/*!
+ Constructs an empty QSslDiffieHellmanParameters instance.
+
+ If an empty QSslDiffieHellmanParameters instance is set on a
+ QSslConfiguration object, Diffie-Hellman negotiation will
+ be disabled.
+
+ \sa isValid()
+ \sa QSslConfiguration
+*/
+QSslDiffieHellmanParameters::QSslDiffieHellmanParameters()
+ : d(new QSslDiffieHellmanParametersPrivate)
+{
+ d->ref.ref();
+}
+
+/*!
+ Constructs a QSslDiffieHellmanParameters object using
+ the byte array \a encoded in either PEM or DER form as specified by \a encoding.
+
+ Use the isValid() method on the returned object to
+ check whether the Diffie-Hellman parameters were valid and
+ loaded correctly.
+
+ \sa isValid()
+ \sa QSslConfiguration
+*/
+QSslDiffieHellmanParameters QSslDiffieHellmanParameters::fromEncoded(const QByteArray &encoded, QSsl::EncodingFormat encoding)
+{
+ QSslDiffieHellmanParameters result;
+ switch (encoding) {
+ case QSsl::Der:
+ result.d->decodeDer(encoded);
+ break;
+ case QSsl::Pem:
+ result.d->decodePem(encoded);
+ break;
+ }
+ return result;
+}
+
+/*!
+ Constructs a QSslDiffieHellmanParameters object by
+ reading from \a device in either PEM or DER form as specified by \a encoding.
+
+ Use the isValid() method on the returned object
+ to check whether the Diffie-Hellman parameters were valid
+ and loaded correctly.
+
+ In particular, if \a device is \c nullptr or not open for reading, an invalid
+ object will be returned.
+
+ \sa isValid()
+ \sa QSslConfiguration
+*/
+QSslDiffieHellmanParameters QSslDiffieHellmanParameters::fromEncoded(QIODevice *device, QSsl::EncodingFormat encoding)
+{
+ if (device)
+ return fromEncoded(device->readAll(), encoding);
+ else
+ return QSslDiffieHellmanParameters();
+}
+
+/*!
+ Constructs an identical copy of \a other.
+*/
+QSslDiffieHellmanParameters::QSslDiffieHellmanParameters(const QSslDiffieHellmanParameters &other)
+ : d(other.d)
+{
+ if (d)
+ d->ref.ref();
+}
+
+/*!
+ \fn QSslDiffieHellmanParameters(QSslDiffieHellmanParameters &&other)
+
+ Move-constructs from \a other.
+
+ \note The moved-from object \a other is placed in a partially-formed state, in which
+ the only valid operations are destruction and assignment of a new value.
+*/
+
+/*!
+ Destroys the QSslDiffieHellmanParameters object.
+*/
+QSslDiffieHellmanParameters::~QSslDiffieHellmanParameters()
+{
+ if (d && !d->ref.deref())
+ delete d;
+}
+
+/*!
+ Copies the contents of \a other into this QSslDiffieHellmanParameters, making the two QSslDiffieHellmanParameters
+ identical.
+
+ Returns a reference to this QSslDiffieHellmanParameters.
+*/
+QSslDiffieHellmanParameters &QSslDiffieHellmanParameters::operator=(const QSslDiffieHellmanParameters &other)
+{
+ QSslDiffieHellmanParameters copy(other);
+ swap(copy);
+ return *this;
+}
+
+/*!
+ \fn QSslDiffieHellmanParameters &QSslDiffieHellmanParameters::operator=(QSslDiffieHellmanParameters &&other)
+
+ Move-assigns \a other to this QSslDiffieHellmanParameters instance.
+
+ \note The moved-from object \a other is placed in a partially-formed state, in which
+ the only valid operations are destruction and assignment of a new value.
+*/
+
+/*!
+ \fn void QSslDiffieHellmanParameters::swap(QSslDiffieHellmanParameters &other)
+
+ Swaps this QSslDiffieHellmanParameters with \a other. This function is very fast and
+ never fails.
+*/
+
+/*!
+ Returns \c true if this is a an empty QSslDiffieHellmanParameters instance.
+
+ Setting an empty QSslDiffieHellmanParameters instance on a QSslSocket-based
+ server will disable Diffie-Hellman key exchange.
+*/
+bool QSslDiffieHellmanParameters::isEmpty() const Q_DECL_NOTHROW
+{
+ return d->derData.isNull() && d->error == QSslDiffieHellmanParameters::NoError;
+}
+
+/*!
+ Returns \c true if this is a valid QSslDiffieHellmanParameters; otherwise false.
+
+ This method should be used after constructing a QSslDiffieHellmanParameters
+ object to determine its validity.
+
+ If a QSslDiffieHellmanParameters object is not valid, you can use the error()
+ method to determine what error prevented the object from being constructed.
+
+ \sa error()
+*/
+bool QSslDiffieHellmanParameters::isValid() const Q_DECL_NOTHROW
+{
+ return d->error == QSslDiffieHellmanParameters::NoError;
+}
+
+/*!
+ \enum QSslDiffieHellmanParameters::Error
+
+ Describes a QSslDiffieHellmanParameters error.
+
+ \value NoError No error occurred.
+
+ \value InvalidInputDataError The given input data could not be used to
+ construct a QSslDiffieHellmanParameters
+ object.
+
+ \value UnsafeParametersError The Diffie-Hellman parameters are unsafe
+ and should not be used.
+*/
+
+/*!
+ Returns the error that caused the QSslDiffieHellmanParameters object
+ to be invalid.
+*/
+QSslDiffieHellmanParameters::Error QSslDiffieHellmanParameters::error() const Q_DECL_NOTHROW
+{
+ return d->error;
+}
+
+/*!
+ Returns a human-readable description of the error that caused the
+ QSslDiffieHellmanParameters object to be invalid.
+*/
+QString QSslDiffieHellmanParameters::errorString() const Q_DECL_NOTHROW
+{
+ switch (d->error) {
+ case QSslDiffieHellmanParameters::NoError:
+ return QCoreApplication::translate("QSslDiffieHellmanParameter", "No error");
+ case QSslDiffieHellmanParameters::InvalidInputDataError:
+ return QCoreApplication::translate("QSslDiffieHellmanParameter", "Invalid input data");
+ case QSslDiffieHellmanParameters::UnsafeParametersError:
+ return QCoreApplication::translate("QSslDiffieHellmanParameter", "The given Diffie-Hellman parameters are deemed unsafe");
+ }
+
+ Q_UNREACHABLE();
+ return QString();
+}
+
+/*!
+ \since 5.8
+ \relates QSslDiffieHellmanParameters
+
+ Returns \c true if \a lhs is equal to \a rhs; otherwise returns \c false.
+*/
+bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) Q_DECL_NOTHROW
+{
+ return lhs.d->derData == rhs.d->derData;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+/*!
+ \since 5.8
+ \relates QSslDiffieHellmanParameters
+
+ Writes the set of Diffie-Hellman parameters in \a dhparam into the debug object \a debug for
+ debugging purposes.
+
+ The Diffie-Hellman parameters will be represented in Base64-encoded DER form.
+
+ \sa {Debugging Techniques}
+*/
+QDebug operator<<(QDebug debug, const QSslDiffieHellmanParameters &dhparam)
+{
+ QDebugStateSaver saver(debug);
+ debug.resetFormat().nospace();
+ debug << "QSslDiffieHellmanParameters(" << dhparam.d->derData.toBase64() << ')';
+ return debug;
+}
+#endif
+
+/*!
+ \since 5.8
+ \relates QSslDiffieHellmanParameters
+
+ Returns an hash value for \a dhparam, using \a seed to seed
+ the calculation.
+*/
+uint qHash(const QSslDiffieHellmanParameters &dhparam, uint seed) Q_DECL_NOTHROW
+{
+ return qHash(dhparam.d->derData, seed);
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qssldiffiehellmanparameters.h b/src/network/ssl/qssldiffiehellmanparameters.h
new file mode 100644
index 0000000000..4533ea4ed2
--- /dev/null
+++ b/src/network/ssl/qssldiffiehellmanparameters.h
@@ -0,0 +1,118 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk>
+** Contact: http://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 QSSLDIFFIEHELLMANPARAMETERS_H
+#define QSSLDIFFIEHELLMANPARAMETERS_H
+
+#include <QtNetwork/qssl.h>
+#include <QtCore/qnamespace.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qshareddata.h>
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_SSL
+
+class QIODevice;
+class QSslContext;
+class QSslDiffieHellmanParametersPrivate;
+
+class QSslDiffieHellmanParameters;
+// qHash is a friend, but we can't use default arguments for friends (§8.3.6.4)
+Q_NETWORK_EXPORT uint qHash(const QSslDiffieHellmanParameters &dhparam, uint seed = 0) Q_DECL_NOTHROW;
+
+#ifndef QT_NO_DEBUG_STREAM
+class QDebug;
+Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslDiffieHellmanParameters &dhparams);
+#endif
+
+Q_NETWORK_EXPORT bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) Q_DECL_NOTHROW;
+
+inline bool operator!=(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) Q_DECL_NOTHROW
+{
+ return !operator==(lhs, rhs);
+}
+
+class QSslDiffieHellmanParameters
+{
+public:
+ enum Error {
+ NoError,
+ InvalidInputDataError,
+ UnsafeParametersError
+ };
+
+ Q_NETWORK_EXPORT static QSslDiffieHellmanParameters defaultParameters();
+
+ Q_NETWORK_EXPORT QSslDiffieHellmanParameters();
+ Q_NETWORK_EXPORT QSslDiffieHellmanParameters(const QSslDiffieHellmanParameters &other);
+ QSslDiffieHellmanParameters(QSslDiffieHellmanParameters &&other) Q_DECL_NOTHROW : d(other.d) { other.d = nullptr; }
+ Q_NETWORK_EXPORT ~QSslDiffieHellmanParameters();
+
+ Q_NETWORK_EXPORT QSslDiffieHellmanParameters &operator=(const QSslDiffieHellmanParameters &other);
+ QSslDiffieHellmanParameters &operator=(QSslDiffieHellmanParameters &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+
+ void swap(QSslDiffieHellmanParameters &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+
+ Q_NETWORK_EXPORT static QSslDiffieHellmanParameters fromEncoded(const QByteArray &encoded, QSsl::EncodingFormat format = QSsl::Pem);
+ Q_NETWORK_EXPORT static QSslDiffieHellmanParameters fromEncoded(QIODevice *device, QSsl::EncodingFormat format = QSsl::Pem);
+
+ Q_NETWORK_EXPORT bool isEmpty() const Q_DECL_NOTHROW;
+ Q_NETWORK_EXPORT bool isValid() const Q_DECL_NOTHROW;
+ Q_NETWORK_EXPORT Error error() const Q_DECL_NOTHROW;
+ Q_NETWORK_EXPORT QString errorString() const Q_DECL_NOTHROW;
+
+private:
+ QSslDiffieHellmanParametersPrivate *d;
+ friend class QSslContext;
+ friend Q_NETWORK_EXPORT bool operator==(const QSslDiffieHellmanParameters &lhs, const QSslDiffieHellmanParameters &rhs) Q_DECL_NOTHROW;
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QSslDiffieHellmanParameters &dhparam);
+#endif
+ friend Q_NETWORK_EXPORT uint qHash(const QSslDiffieHellmanParameters &dhparam, uint seed) Q_DECL_NOTHROW;
+};
+
+Q_DECLARE_SHARED(QSslDiffieHellmanParameters)
+
+#endif // QT_NO_SSL
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/ssl/qssldiffiehellmanparameters_dummy.cpp b/src/network/ssl/qssldiffiehellmanparameters_dummy.cpp
new file mode 100644
index 0000000000..220c017f4c
--- /dev/null
+++ b/src/network/ssl/qssldiffiehellmanparameters_dummy.cpp
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk>
+** Contact: http://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 "qssldiffiehellmanparameters.h"
+#include "qssldiffiehellmanparameters_p.h"
+
+#include <QtCore/qglobal.h>
+#include <QtCore/qbytearray.h>
+
+QT_BEGIN_NAMESPACE
+
+void QSslDiffieHellmanParametersPrivate::decodeDer(const QByteArray &)
+{
+}
+
+void QSslDiffieHellmanParametersPrivate::decodePem(const QByteArray &)
+{
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp b/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp
new file mode 100644
index 0000000000..949da1b7df
--- /dev/null
+++ b/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp
@@ -0,0 +1,164 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk>
+** Contact: http://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 "qssldiffiehellmanparameters.h"
+#include "qssldiffiehellmanparameters_p.h"
+#include "qsslsocket_openssl_symbols_p.h"
+#include "qsslsocket.h"
+#include "qsslsocket_p.h"
+
+#include <QtCore/qatomic.h>
+#include <QtCore/qbytearray.h>
+#include <QtCore/qiodevice.h>
+#ifndef QT_NO_DEBUG_STREAM
+#include <QtCore/qdebug.h>
+#endif
+
+// For q_BN_is_word.
+#include <openssl/bn.h>
+
+QT_BEGIN_NAMESPACE
+
+static bool isSafeDH(DH *dh)
+{
+ int status = 0;
+ int bad = 0;
+
+ QSslSocketPrivate::ensureInitialized();
+
+ // Mark p < 1024 bits as unsafe.
+ if (q_BN_num_bits(dh->p) < 1024) {
+ return false;
+ }
+
+ if (q_DH_check(dh, &status) != 1)
+ return false;
+
+ // From https://wiki.openssl.org/index.php/Diffie-Hellman_parameters:
+ //
+ // The additional call to BN_mod_word(dh->p, 24)
+ // (and unmasking of DH_NOT_SUITABLE_GENERATOR)
+ // is performed to ensure your program accepts
+ // IETF group parameters. OpenSSL checks the prime
+ // is congruent to 11 when g = 2; while the IETF's
+ // primes are congruent to 23 when g = 2.
+ // Without the test, the IETF parameters would
+ // fail validation. For details, see Diffie-Hellman
+ // Parameter Check (when g = 2, must p mod 24 == 11?).
+ if (q_BN_is_word(dh->g, DH_GENERATOR_2)) {
+ long residue = q_BN_mod_word(dh->p, 24);
+ if (residue == 11 || residue == 23)
+ status &= ~DH_NOT_SUITABLE_GENERATOR;
+ }
+
+ bad |= DH_CHECK_P_NOT_PRIME;
+ bad |= DH_CHECK_P_NOT_SAFE_PRIME;
+ bad |= DH_NOT_SUITABLE_GENERATOR;
+
+ return !(status & bad);
+}
+
+void QSslDiffieHellmanParametersPrivate::decodeDer(const QByteArray &der)
+{
+ if (der.isEmpty()) {
+ error = QSslDiffieHellmanParameters::InvalidInputDataError;
+ return;
+ }
+
+ const unsigned char *data = reinterpret_cast<const unsigned char *>(der.data());
+ int len = der.size();
+
+ QSslSocketPrivate::ensureInitialized();
+
+ DH *dh = q_d2i_DHparams(NULL, &data, len);
+ if (dh) {
+ if (isSafeDH(dh))
+ derData = der;
+ else
+ error = QSslDiffieHellmanParameters::UnsafeParametersError;
+ } else {
+ error = QSslDiffieHellmanParameters::InvalidInputDataError;
+ }
+
+ q_DH_free(dh);
+}
+
+void QSslDiffieHellmanParametersPrivate::decodePem(const QByteArray &pem)
+{
+ if (pem.isEmpty()) {
+ error = QSslDiffieHellmanParameters::InvalidInputDataError;
+ return;
+ }
+
+ if (!QSslSocket::supportsSsl()) {
+ error = QSslDiffieHellmanParameters::InvalidInputDataError;
+ return;
+ }
+
+ QSslSocketPrivate::ensureInitialized();
+
+ BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pem.data()), pem.size());
+ if (!bio) {
+ error = QSslDiffieHellmanParameters::InvalidInputDataError;
+ return;
+ }
+
+ DH *dh = Q_NULLPTR;
+ q_PEM_read_bio_DHparams(bio, &dh, 0, 0);
+
+ if (dh) {
+ if (isSafeDH(dh)) {
+ char *buf = Q_NULLPTR;
+ int len = q_i2d_DHparams(dh, reinterpret_cast<unsigned char **>(&buf));
+ if (len > 0)
+ derData = QByteArray(buf, len);
+ else
+ error = QSslDiffieHellmanParameters::InvalidInputDataError;
+ } else {
+ error = QSslDiffieHellmanParameters::UnsafeParametersError;
+ }
+ } else {
+ error = QSslDiffieHellmanParameters::InvalidInputDataError;
+ }
+
+ q_DH_free(dh);
+ q_BIO_free(bio);
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/ssl/qssldiffiehellmanparameters_p.h b/src/network/ssl/qssldiffiehellmanparameters_p.h
new file mode 100644
index 0000000000..06ecf292ff
--- /dev/null
+++ b/src/network/ssl/qssldiffiehellmanparameters_p.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+**
+** Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk>
+** Contact: http://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 QSSLDIFFIEHELLMANPARAMETERS_P_H
+#define QSSLDIFFIEHELLMANPARAMETERS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of qssldiffiehellmanparameters.cpp. This header file may change from version to version
+// without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+#include <QSharedData>
+
+#include "qsslkey.h"
+#include "qssldiffiehellmanparameters.h"
+#include "qsslsocket_p.h" // includes wincrypt.h
+
+QT_BEGIN_NAMESPACE
+
+class QSslDiffieHellmanParametersPrivate : public QSharedData
+{
+public:
+ QSslDiffieHellmanParametersPrivate() : error(QSslDiffieHellmanParameters::NoError) {};
+
+ void decodeDer(const QByteArray &der);
+ void decodePem(const QByteArray &pem);
+
+ QSslDiffieHellmanParameters::Error error;
+ QByteArray derData;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSSLDIFFIEHELLMANPARAMETERS_P_H
diff --git a/src/network/ssl/qsslellipticcurve.h b/src/network/ssl/qsslellipticcurve.h
index 6ee66d1ec1..0e7cc60e31 100644
--- a/src/network/ssl/qsslellipticcurve.h
+++ b/src/network/ssl/qsslellipticcurve.h
@@ -40,7 +40,7 @@
#ifndef QSSLELLIPTICCURVE_H
#define QSSLELLIPTICCURVE_H
-#include <QtCore/QtGlobal>
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/QString>
#include <QtCore/QMetaType>
#if QT_DEPRECATED_SINCE(5, 6)
diff --git a/src/network/ssl/qsslerror.h b/src/network/ssl/qsslerror.h
index 82db2e58e4..d7c959423d 100644
--- a/src/network/ssl/qsslerror.h
+++ b/src/network/ssl/qsslerror.h
@@ -41,6 +41,7 @@
#ifndef QSSLERROR_H
#define QSSLERROR_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qvariant.h>
#include <QtNetwork/qsslcertificate.h>
diff --git a/src/network/ssl/qsslkey.h b/src/network/ssl/qsslkey.h
index 39c3659fb8..6de02b1e44 100644
--- a/src/network/ssl/qsslkey.h
+++ b/src/network/ssl/qsslkey.h
@@ -41,6 +41,7 @@
#ifndef QSSLKEY_H
#define QSSLKEY_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qnamespace.h>
#include <QtCore/qbytearray.h>
#include <QtCore/qsharedpointer.h>
diff --git a/src/network/ssl/qsslkey_p.cpp b/src/network/ssl/qsslkey_p.cpp
index 34f664093c..e66ec953a0 100644
--- a/src/network/ssl/qsslkey_p.cpp
+++ b/src/network/ssl/qsslkey_p.cpp
@@ -56,6 +56,9 @@
#include "qsslkey.h"
#include "qsslkey_p.h"
+#ifndef QT_NO_OPENSSL
+#include "qsslsocket_openssl_symbols_p.h"
+#endif
#include "qsslsocket.h"
#include "qsslsocket_p.h"
@@ -277,18 +280,23 @@ QSslKey::QSslKey(QIODevice *device, QSsl::KeyAlgorithm algorithm, QSsl::Encoding
\a type specifies whether the key is public or private.
QSslKey will take ownership for this key and you must not
- free the key using the native library. The algorithm used
- when creating a key from a handle will always be QSsl::Opaque.
+ free the key using the native library.
*/
QSslKey::QSslKey(Qt::HANDLE handle, QSsl::KeyType type)
: d(new QSslKeyPrivate)
{
#ifndef QT_NO_OPENSSL
- d->opaque = reinterpret_cast<EVP_PKEY *>(handle);
+ EVP_PKEY *evpKey = reinterpret_cast<EVP_PKEY *>(handle);
+ if (!evpKey || !d->fromEVP_PKEY(evpKey)) {
+ d->opaque = evpKey;
+ d->algorithm = QSsl::Opaque;
+ } else {
+ q_EVP_PKEY_free(evpKey);
+ }
#else
d->opaque = handle;
-#endif
d->algorithm = QSsl::Opaque;
+#endif
d->type = type;
d->isNull = !d->opaque;
}
diff --git a/src/network/ssl/qsslkey_p.h b/src/network/ssl/qsslkey_p.h
index c17183a059..c93941c198 100644
--- a/src/network/ssl/qsslkey_p.h
+++ b/src/network/ssl/qsslkey_p.h
@@ -41,8 +41,6 @@
#ifndef QSSLKEY_OPENSSL_P_H
#define QSSLKEY_OPENSSL_P_H
-#include "qsslkey.h"
-
//
// W A R N I N G
// -------------
@@ -54,6 +52,8 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+#include "qsslkey.h"
#include "qsslsocket_p.h" // includes wincrypt.h
#ifndef QT_NO_OPENSSL
diff --git a/src/network/ssl/qsslpresharedkeyauthenticator.h b/src/network/ssl/qsslpresharedkeyauthenticator.h
index f34a2cee75..a012ff489a 100644
--- a/src/network/ssl/qsslpresharedkeyauthenticator.h
+++ b/src/network/ssl/qsslpresharedkeyauthenticator.h
@@ -40,7 +40,7 @@
#ifndef QSSLPRESHAREDKEYAUTHENTICATOR_H
#define QSSLPRESHAREDKEYAUTHENTICATOR_H
-#include <QtCore/QtGlobal>
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/QString>
#include <QtCore/QSharedDataPointer>
#include <QtCore/QMetaType>
diff --git a/src/network/ssl/qsslpresharedkeyauthenticator_p.h b/src/network/ssl/qsslpresharedkeyauthenticator_p.h
index b9aa6eb28c..e5566c3b3c 100644
--- a/src/network/ssl/qsslpresharedkeyauthenticator_p.h
+++ b/src/network/ssl/qsslpresharedkeyauthenticator_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <QSharedData>
QT_BEGIN_NAMESPACE
diff --git a/src/network/ssl/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index fc60f48d93..29e1f32815 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -837,15 +837,7 @@ bool QSslSocket::atEnd() const
// Note! docs copied from QAbstractSocket::flush()
bool QSslSocket::flush()
{
- Q_D(QSslSocket);
-#ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << "QSslSocket::flush()";
-#endif
- if (d->mode != UnencryptedMode)
- // encrypt any unencrypted bytes in our buffer
- d->transmit();
-
- return d->plainSocket ? d->plainSocket->flush() : false;
+ return d_func()->flush();
}
/*!
@@ -924,6 +916,8 @@ void QSslSocket::setSslConfiguration(const QSslConfiguration &configuration)
d->configuration.privateKey = configuration.privateKey();
d->configuration.ciphers = configuration.ciphers();
d->configuration.ellipticCurves = configuration.ellipticCurves();
+ d->configuration.preSharedKeyIdentityHint = configuration.preSharedKeyIdentityHint();
+ d->configuration.dhParams = configuration.diffieHellmanParameters();
d->configuration.caCertificates = configuration.caCertificates();
d->configuration.peerVerifyDepth = configuration.peerVerifyDepth();
d->configuration.peerVerifyMode = configuration.peerVerifyMode();
@@ -2623,6 +2617,22 @@ QByteArray QSslSocketPrivate::peek(qint64 maxSize)
/*!
\internal
*/
+bool QSslSocketPrivate::flush()
+{
+#ifdef QSSLSOCKET_DEBUG
+ qCDebug(lcSsl) << "QSslSocketPrivate::flush()";
+#endif
+ if (mode != QSslSocket::UnencryptedMode) {
+ // encrypt any unencrypted bytes in our buffer
+ transmit();
+ }
+
+ return plainSocket && plainSocket->flush();
+}
+
+/*!
+ \internal
+*/
bool QSslSocketPrivate::rootCertOnDemandLoadingSupported()
{
return s_loadRootCertsOnDemand;
diff --git a/src/network/ssl/qsslsocket.h b/src/network/ssl/qsslsocket.h
index c069ff2f9d..1b29cd4637 100644
--- a/src/network/ssl/qsslsocket.h
+++ b/src/network/ssl/qsslsocket.h
@@ -41,6 +41,7 @@
#ifndef QSSLSOCKET_H
#define QSSLSOCKET_H
+#include <QtNetwork/qtnetworkglobal.h>
#include <QtCore/qlist.h>
#include <QtCore/qregexp.h>
#ifndef QT_NO_SSL
@@ -116,7 +117,7 @@ public:
bool canReadLine() const Q_DECL_OVERRIDE;
void close() Q_DECL_OVERRIDE;
bool atEnd() const Q_DECL_OVERRIDE;
- bool flush();
+ bool flush(); // ### Qt6: remove me (implementation moved to private flush())
void abort();
// From QAbstractSocket:
diff --git a/src/network/ssl/qsslsocket_mac.cpp b/src/network/ssl/qsslsocket_mac.cpp
index fd82d53fa8..4c06404415 100644
--- a/src/network/ssl/qsslsocket_mac.cpp
+++ b/src/network/ssl/qsslsocket_mac.cpp
@@ -68,57 +68,18 @@ QT_BEGIN_NAMESPACE
static SSLContextRef qt_createSecureTransportContext(QSslSocket::SslMode mode)
{
const bool isServer = mode == QSslSocket::SslServerMode;
- SSLContextRef context = Q_NULLPTR;
-
-#ifndef Q_OS_OSX
const SSLProtocolSide side = isServer ? kSSLServerSide : kSSLClientSide;
// We never use kSSLDatagramType, so it's kSSLStreamType unconditionally.
- context = SSLCreateContext(Q_NULLPTR, side, kSSLStreamType);
+ SSLContextRef context = SSLCreateContext(Q_NULLPTR, side, kSSLStreamType);
if (!context)
qCWarning(lcSsl) << "SSLCreateContext failed";
-#else // Q_OS_OSX
-
-#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_8, __IPHONE_NA)
- if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_8) {
- const SSLProtocolSide side = isServer ? kSSLServerSide : kSSLClientSide;
- // We never use kSSLDatagramType, so it's kSSLStreamType unconditionally.
- context = SSLCreateContext(Q_NULLPTR, side, kSSLStreamType);
- if (!context)
- qCWarning(lcSsl) << "SSLCreateContext failed";
- } else {
-#else
- {
-#endif
- const OSStatus errCode = SSLNewContext(isServer, &context);
- if (errCode != noErr || !context)
- qCWarning(lcSsl) << "SSLNewContext failed with error:" << errCode;
- }
-#endif // !Q_OS_OSX
-
return context;
}
static void qt_releaseSecureTransportContext(SSLContextRef context)
{
- if (!context)
- return;
-
-#ifndef Q_OS_OSX
- CFRelease(context);
-#else
-
-#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_8, __IPHONE_NA)
- if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_8) {
+ if (context)
CFRelease(context);
- } else {
-#else
- {
-#endif // QT_MAC_PLATFORM_...
- const OSStatus errCode = SSLDisposeContext(context);
- if (errCode != noErr)
- qCWarning(lcSsl) << "SSLDisposeContext failed with error:" << errCode;
- }
-#endif // !Q_OS_OSX
}
static bool qt_setSessionProtocol(SSLContextRef context, const QSslConfigurationPrivate &configuration,
@@ -132,7 +93,6 @@ static bool qt_setSessionProtocol(SSLContextRef context, const QSslConfiguration
OSStatus err = noErr;
-#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_8, __IPHONE_5_0)
if (configuration.protocol == QSsl::SslV3) {
#ifdef QSSLSOCKET_DEBUG
qCDebug(lcSsl) << plainSocket << "requesting : SSLv3";
@@ -210,117 +170,10 @@ static bool qt_setSessionProtocol(SSLContextRef context, const QSslConfiguration
#endif
return false;
}
-#endif
return err == noErr;
}
-#ifdef Q_OS_OSX
-
-static bool qt_setSessionProtocolOSX(SSLContextRef context, const QSslConfigurationPrivate &configuration,
- QTcpSocket *plainSocket)
-{
- // This function works with (now) deprecated API that does not even exist on
- // iOS but is the only API we have on OS X below 10.8
-
- // Without SSLSetProtocolVersionMin/Max functions it's quite difficult
- // to have the required result:
- // If we use SSLSetProtocolVersion - any constant except the ones with 'Only' suffix -
- // allows a negotiation and we can not set the lower limit.
- // SSLSetProtocolVersionEnabled supports only a limited subset of constants, if you believe their docs:
- // kSSLProtocol2
- // kSSLProtocol3
- // kTLSProtocol1
- // kSSLProtocolAll
- // Here we can only have a look into the SecureTransport's code and hope that what we see there
- // and what we have on 10.7 is similar:
- // SSLSetProtocoLVersionEnabled actually accepts other constants also,
- // called twice with two different protocols it sets a range,
- // called once with a protocol (when all protocols were disabled)
- // - only this protocol is enabled (without a lower limit negotiation).
-
- Q_ASSERT(context);
-
-#ifndef QSSLSOCKET_DEBUG
- Q_UNUSED(plainSocket)
-#endif
-
- OSStatus err = noErr;
-
- // First, disable ALL:
- if (SSLSetProtocolVersionEnabled(context, kSSLProtocolAll, false) != noErr)
- return false;
-
- if (configuration.protocol == QSsl::SslV3) {
- #ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "requesting : SSLv3";
- #endif
- err = SSLSetProtocolVersion(context, kSSLProtocol3Only);
- } else if (configuration.protocol == QSsl::TlsV1_0) {
- #ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.0";
- #endif
- err = SSLSetProtocolVersion(context, kTLSProtocol1Only);
- } else if (configuration.protocol == QSsl::TlsV1_1) {
- #ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.1";
- #endif
- err = SSLSetProtocolVersionEnabled(context, kTLSProtocol11, true);
- } else if (configuration.protocol == QSsl::TlsV1_2) {
- #ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.2";
- #endif
- err = SSLSetProtocolVersionEnabled(context, kTLSProtocol12, true);
- } else if (configuration.protocol == QSsl::AnyProtocol) {
- #ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "requesting : any";
- #endif
- err = SSLSetProtocolVersionEnabled(context, kSSLProtocolAll, true);
- } else if (configuration.protocol == QSsl::TlsV1SslV3) {
- #ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "requesting : SSLv3 - TLSv1.2";
- #endif
- err = SSLSetProtocolVersionEnabled(context, kTLSProtocol12, true);
- if (err == noErr)
- err = SSLSetProtocolVersionEnabled(context, kSSLProtocol3, true);
- } else if (configuration.protocol == QSsl::SecureProtocols) {
- #ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "requesting : TLSv1 - TLSv1.2";
- #endif
- err = SSLSetProtocolVersionEnabled(context, kTLSProtocol12, true);
- if (err == noErr)
- err = SSLSetProtocolVersionEnabled(context, kTLSProtocol1, true);
- } else if (configuration.protocol == QSsl::TlsV1_0OrLater) {
- #ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "requesting : TLSv1 - TLSv1.2";
- #endif
- err = SSLSetProtocolVersionEnabled(context, kTLSProtocol12, true);
- if (err == noErr)
- err = SSLSetProtocolVersionEnabled(context, kTLSProtocol1, true);
- } else if (configuration.protocol == QSsl::TlsV1_1OrLater) {
- #ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.1 - TLSv1.2";
- #endif
- err = SSLSetProtocolVersionEnabled(context, kTLSProtocol12, true);
- if (err == noErr)
- err = SSLSetProtocolVersionEnabled(context, kTLSProtocol11, true);
- } else if (configuration.protocol == QSsl::TlsV1_2OrLater) {
- #ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "requesting : TLSv1.2";
- #endif
- err = SSLSetProtocolVersionEnabled(context, kTLSProtocol12, true);
- } else {
- #ifdef QSSLSOCKET_DEBUG
- qCDebug(lcSsl) << plainSocket << "no protocol version found in the configuration";
- #endif
- return false;
- }
-
- return err == noErr;
-}
-
-#endif // Q_OS_OSX
-
QSecureTransportContext::QSecureTransportContext(SSLContextRef c)
: context(c)
{
@@ -351,7 +204,7 @@ bool QSslSocketPrivate::s_loadedCiphersAndCerts = false;
bool QSslSocketPrivate::s_loadRootCertsOnDemand = false;
-#ifndef Q_OS_IOS // dhparam is not used on iOS. (see the SSLSetDiffieHellmanParams call below)
+#if !defined(QT_PLATFORM_UIKIT) // dhparam is only used on macOS. (see the SSLSetDiffieHellmanParams call below)
static const uint8_t dhparam[] =
"\x30\x82\x01\x08\x02\x82\x01\x01\x00\x97\xea\xd0\x46\xf7\xae\xa7\x76\x80"
"\x9c\x74\x56\x98\xd8\x56\x97\x2b\x20\x6c\x77\xe2\x82\xbb\xc8\x84\xbe\xe7"
@@ -370,8 +223,8 @@ static const uint8_t dhparam[] =
"\x90\x0b\x35\x64\xff\xd9\xe3\xac\xf2\xf2\xeb\x3a\x63\x02\x01\x02";
#endif
-// No ioErr on iOS. (defined in MacErrors.h on OS X)
-#ifdef Q_OS_IOS
+// No ioErr on iOS/tvOS/watchOS. (defined in MacErrors.h on macOS)
+#if defined(QT_PLATFORM_UIKIT)
# define ioErr -36
#endif
@@ -959,21 +812,6 @@ bool QSslSocketBackendPrivate::initSslContext()
return false;
}
-#ifdef Q_OS_OSX
- if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_8) {
- // Starting from OS X 10.8 SSLSetSessionOption with kSSLSessionOptionBreakOnServerAuth/
- // kSSLSessionOptionBreakOnClientAuth disables automatic certificate validation.
- // But for OS X versions below 10.8 we have to do it explicitly:
- const OSStatus err = SSLSetEnableCertVerify(context, false);
- if (err != noErr) {
- destroySslContext();
- setErrorAndEmit(QSslSocket::SslInternalError,
- QStringLiteral("SSLSetEnableCertVerify failed: %1").arg(err));
- return false;
- }
- }
-#endif
-
if (mode == QSslSocket::SslClientMode) {
// enable Server Name Indication (SNI)
QString tlsHostName(verificationPeerName.isEmpty() ? q->peerName() : verificationPeerName);
@@ -1011,7 +849,7 @@ bool QSslSocketBackendPrivate::initSslContext()
return false;
}
}
-#ifndef Q_OS_IOS
+#if !defined(QT_PLATFORM_UIKIT)
// No SSLSetDiffieHellmanParams on iOS; calling it is optional according to docs.
SSLSetDiffieHellmanParams(context, dhparam, sizeof(dhparam));
#endif
@@ -1128,20 +966,7 @@ bool QSslSocketBackendPrivate::setSessionProtocol()
return false;
}
-#ifndef Q_OS_OSX
return qt_setSessionProtocol(context, configuration, plainSocket);
-#else
-
-#if QT_MAC_PLATFORM_SDK_EQUAL_OR_ABOVE(__MAC_10_8, __IPHONE_NA)
- if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_8) {
- return qt_setSessionProtocol(context, configuration, plainSocket);
- } else {
-#else
- {
-#endif
- return qt_setSessionProtocolOSX(context, configuration, plainSocket);
- }
-#endif
}
bool QSslSocketBackendPrivate::canIgnoreTrustVerificationFailure() const
diff --git a/src/network/ssl/qsslsocket_mac_p.h b/src/network/ssl/qsslsocket_mac_p.h
index 09b6f392cd..9e1d18981e 100644
--- a/src/network/ssl/qsslsocket_mac_p.h
+++ b/src/network/ssl/qsslsocket_mac_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <QtCore/qstring.h>
#include <QtCore/qglobal.h>
#include <QtCore/qlist.h>
diff --git a/src/network/ssl/qsslsocket_openssl.cpp b/src/network/ssl/qsslsocket_openssl.cpp
index b3820aea97..aca7507d13 100644
--- a/src/network/ssl/qsslsocket_openssl.cpp
+++ b/src/network/ssl/qsslsocket_openssl.cpp
@@ -201,6 +201,15 @@ static unsigned int q_ssl_psk_client_callback(SSL *ssl,
Q_ASSERT(d);
return d->tlsPskClientCallback(hint, identity, max_identity_len, psk, max_psk_len);
}
+
+static unsigned int q_ssl_psk_server_callback(SSL *ssl,
+ const char *identity,
+ unsigned char *psk, unsigned int max_psk_len)
+{
+ QSslSocketBackendPrivate *d = reinterpret_cast<QSslSocketBackendPrivate *>(q_SSL_get_ex_data(ssl, QSslSocketBackendPrivate::s_indexForSSLExtraData));
+ Q_ASSERT(d);
+ return d->tlsPskServerCallback(identity, psk, max_psk_len);
+}
#endif
} // extern "C"
@@ -436,8 +445,12 @@ bool QSslSocketBackendPrivate::initSslContext()
#if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK)
// Set the client callback for PSK
- if (q_SSLeay() >= 0x10001000L && mode == QSslSocket::SslClientMode)
- q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_client_callback);
+ if (q_SSLeay() >= 0x10001000L) {
+ if (mode == QSslSocket::SslClientMode)
+ q_SSL_set_psk_client_callback(ssl, &q_ssl_psk_client_callback);
+ else if (mode == QSslSocket::SslServerMode)
+ q_SSL_set_psk_server_callback(ssl, &q_ssl_psk_server_callback);
+ }
#endif
return true;
@@ -522,15 +535,9 @@ void QSslSocketPrivate::ensureCiphersAndCertsLoaded()
#if defined(Q_OS_WIN)
HINSTANCE hLib = LoadLibraryW(L"Crypt32");
if (hLib) {
-#if defined(Q_OS_WINCE)
- ptrCertOpenSystemStoreW = (PtrCertOpenSystemStoreW)GetProcAddress(hLib, L"CertOpenStore");
- ptrCertFindCertificateInStore = (PtrCertFindCertificateInStore)GetProcAddress(hLib, L"CertFindCertificateInStore");
- ptrCertCloseStore = (PtrCertCloseStore)GetProcAddress(hLib, L"CertCloseStore");
-#else
ptrCertOpenSystemStoreW = (PtrCertOpenSystemStoreW)GetProcAddress(hLib, "CertOpenSystemStoreW");
ptrCertFindCertificateInStore = (PtrCertFindCertificateInStore)GetProcAddress(hLib, "CertFindCertificateInStore");
ptrCertCloseStore = (PtrCertCloseStore)GetProcAddress(hLib, "CertCloseStore");
-#endif
if (!ptrCertOpenSystemStoreW || !ptrCertFindCertificateInStore || !ptrCertCloseStore)
qCWarning(lcSsl, "could not resolve symbols in crypt32 library"); // should never happen
} else {
@@ -691,15 +698,7 @@ QList<QSslCertificate> QSslSocketPrivate::systemCaCertificates()
#if defined(Q_OS_WIN)
if (ptrCertOpenSystemStoreW && ptrCertFindCertificateInStore && ptrCertCloseStore) {
HCERTSTORE hSystemStore;
-#if defined(Q_OS_WINCE)
- hSystemStore = ptrCertOpenSystemStoreW(CERT_STORE_PROV_SYSTEM_W,
- 0,
- 0,
- CERT_STORE_NO_CRYPT_RELEASE_FLAG|CERT_SYSTEM_STORE_CURRENT_USER,
- L"ROOT");
-#else
hSystemStore = ptrCertOpenSystemStoreW(0, L"ROOT");
-#endif
if(hSystemStore) {
PCCERT_CONTEXT pc = NULL;
while(1) {
@@ -1278,6 +1277,31 @@ unsigned int QSslSocketBackendPrivate::tlsPskClientCallback(const char *hint,
return pskLength;
}
+unsigned int QSslSocketBackendPrivate::tlsPskServerCallback(const char *identity,
+ unsigned char *psk, unsigned int max_psk_len)
+{
+ QSslPreSharedKeyAuthenticator authenticator;
+
+ // Fill in some read-only fields (for the user)
+ authenticator.d->identityHint = configuration.preSharedKeyIdentityHint;
+ authenticator.d->identity = identity;
+ authenticator.d->maximumIdentityLength = 0; // user cannot set an identity
+ authenticator.d->maximumPreSharedKeyLength = int(max_psk_len);
+
+ // Let the client provide the remaining bits...
+ Q_Q(QSslSocket);
+ emit q->preSharedKeyAuthenticationRequired(&authenticator);
+
+ // No PSK set? Return now to make the handshake fail
+ if (authenticator.preSharedKey().isEmpty())
+ return 0;
+
+ // Copy data back into OpenSSL
+ const int pskLength = qMin(authenticator.preSharedKey().length(), authenticator.maximumPreSharedKeyLength());
+ ::memcpy(psk, authenticator.preSharedKey().constData(), pskLength);
+ return pskLength;
+}
+
#ifdef Q_OS_WIN
void QSslSocketBackendPrivate::fetchCaRootForCert(const QSslCertificate &cert)
@@ -1579,7 +1603,22 @@ void QSslSocketBackendPrivate::continueHandshake()
} else {
const unsigned char *proto = 0;
unsigned int proto_len = 0;
- q_SSL_get0_next_proto_negotiated(ssl, &proto, &proto_len);
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ if (q_SSLeay() >= 0x10002000L) {
+ q_SSL_get0_alpn_selected(ssl, &proto, &proto_len);
+ if (proto_len && mode == QSslSocket::SslClientMode) {
+ // Client does not have a callback that sets it ...
+ configuration.nextProtocolNegotiationStatus = QSslConfiguration::NextProtocolNegotiationNegotiated;
+ }
+ }
+
+ if (!proto_len) { // Test if NPN was more lucky ...
+#else
+ {
+#endif
+ q_SSL_get0_next_proto_negotiated(ssl, &proto, &proto_len);
+ }
+
if (proto_len)
configuration.nextNegotiatedProtocol = QByteArray(reinterpret_cast<const char *>(proto), proto_len);
else
diff --git a/src/network/ssl/qsslsocket_openssl_android.cpp b/src/network/ssl/qsslsocket_openssl_android.cpp
index d73ed8995e..b5d2458d56 100644
--- a/src/network/ssl/qsslsocket_openssl_android.cpp
+++ b/src/network/ssl/qsslsocket_openssl_android.cpp
@@ -70,6 +70,7 @@ QList<QByteArray> QSslSocketPrivate::fetchSslCertificateData()
QJNIEnvironmentPrivate env;
jobjectArray jcertificates = static_cast<jobjectArray>(certificates.object());
const jint nCertificates = env->GetArrayLength(jcertificates);
+ certificateData.reserve(static_cast<int>(nCertificates));
for (int i = 0; i < nCertificates; ++i) {
jbyteArray jCert = static_cast<jbyteArray>(env->GetObjectArrayElement(jcertificates, i));
diff --git a/src/network/ssl/qsslsocket_openssl_p.h b/src/network/ssl/qsslsocket_openssl_p.h
index 0674c05d71..b2adb3e547 100644
--- a/src/network/ssl/qsslsocket_openssl_p.h
+++ b/src/network/ssl/qsslsocket_openssl_p.h
@@ -59,13 +59,14 @@
// W A R N I N G
// -------------
//
-// This file is not part of the Qt API. It exists for the convenience
-// of the QLibrary class. This header file may change from
-// version to version without notice, or even be removed.
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
//
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qsslsocket_p.h"
#ifdef Q_OS_WIN
@@ -143,6 +144,7 @@ public:
bool checkSslErrors();
void storePeerCertificates();
unsigned int tlsPskClientCallback(const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len);
+ unsigned int tlsPskServerCallback(const char *identity, unsigned char *psk, unsigned int max_psk_len);
#ifdef Q_OS_WIN
void fetchCaRootForCert(const QSslCertificate &cert);
void _q_caRootLoaded(QSslCertificate,QSslCertificate) Q_DECL_OVERRIDE;
diff --git a/src/network/ssl/qsslsocket_openssl_symbols.cpp b/src/network/ssl/qsslsocket_openssl_symbols.cpp
index d420f78dc7..88fed69d3a 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols.cpp
+++ b/src/network/ssl/qsslsocket_openssl_symbols.cpp
@@ -151,6 +151,10 @@ DEFINEFUNC3(int, BIO_read, BIO *a, a, void *b, b, int c, c, return -1, return)
DEFINEFUNC(BIO_METHOD *, BIO_s_mem, void, DUMMYARG, return 0, return)
DEFINEFUNC3(int, BIO_write, BIO *a, a, const void *b, b, int c, c, return -1, return)
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)
#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)
@@ -202,17 +206,20 @@ DEFINEFUNC(int, OBJ_obj2nid, const ASN1_OBJECT *a, a, return NID_undef, return)
DEFINEFUNC6(void *, PEM_ASN1_read_bio, d2i_of_void *a, a, const char *b, b, BIO *c, c, void **d, d, pem_password_cb *e, e, void *f, f, return 0, return)
DEFINEFUNC6(void *, PEM_ASN1_write_bio, d2i_of_void *a, a, const char *b, b, BIO *c, c, void **d, d, pem_password_cb *e, e, void *f, f, return 0, return)
#else
+DEFINEFUNC4(EVP_PKEY *, PEM_read_bio_PrivateKey, BIO *a, a, EVP_PKEY **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
DEFINEFUNC4(DSA *, PEM_read_bio_DSAPrivateKey, BIO *a, a, DSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
DEFINEFUNC4(RSA *, PEM_read_bio_RSAPrivateKey, BIO *a, a, RSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
#ifndef OPENSSL_NO_EC
DEFINEFUNC4(EC_KEY *, PEM_read_bio_ECPrivateKey, BIO *a, a, EC_KEY **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
#endif
+DEFINEFUNC4(DH *, PEM_read_bio_DHparams, BIO *a, a, DH **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
DEFINEFUNC7(int, PEM_write_bio_DSAPrivateKey, BIO *a, a, DSA *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
DEFINEFUNC7(int, PEM_write_bio_RSAPrivateKey, BIO *a, a, RSA *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
#ifndef OPENSSL_NO_EC
DEFINEFUNC7(int, PEM_write_bio_ECPrivateKey, BIO *a, a, EC_KEY *b, b, const EVP_CIPHER *c, c, unsigned char *d, d, int e, e, pem_password_cb *f, f, void *g, g, return 0, return)
#endif
#endif
+DEFINEFUNC4(EVP_PKEY *, PEM_read_bio_PUBKEY, BIO *a, a, EVP_PKEY **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
DEFINEFUNC4(DSA *, PEM_read_bio_DSA_PUBKEY, BIO *a, a, DSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
DEFINEFUNC4(RSA *, PEM_read_bio_RSA_PUBKEY, BIO *a, a, RSA **b, b, pem_password_cb *c, c, void *d, d, return 0, return)
#ifndef OPENSSL_NO_EC
@@ -300,6 +307,8 @@ DEFINEFUNC2(void *, SSL_get_ex_data, const SSL *ssl, ssl, int idx, idx, return N
#endif
#if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK)
DEFINEFUNC2(void, SSL_set_psk_client_callback, SSL* ssl, ssl, q_psk_client_callback_t callback, callback, return, DUMMYARG)
+DEFINEFUNC2(void, SSL_set_psk_server_callback, SSL* ssl, ssl, q_psk_server_callback_t callback, callback, return, DUMMYARG)
+DEFINEFUNC2(int, SSL_CTX_use_psk_identity_hint, SSL_CTX* ctx, ctx, const char *hint, hint, return 0, return)
#endif
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
#ifndef OPENSSL_NO_SSL2
@@ -418,10 +427,24 @@ DEFINEFUNC3(void, SSL_CTX_set_next_proto_select_cb, SSL_CTX *s, s,
void *arg, arg, return, DUMMYARG)
DEFINEFUNC3(void, SSL_get0_next_proto_negotiated, const SSL *s, s,
const unsigned char **data, data, unsigned *len, len, return, DUMMYARG)
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+DEFINEFUNC3(int, SSL_set_alpn_protos, SSL *s, s, const unsigned char *protos, protos,
+ unsigned protos_len, protos_len, return -1, return)
+DEFINEFUNC3(void, SSL_CTX_set_alpn_select_cb, SSL_CTX *s, s,
+ int (*cb) (SSL *ssl, const unsigned char **out,
+ unsigned char *outlen,
+ const unsigned char *in,
+ unsigned int inlen, void *arg), cb,
+ void *arg, arg, return, DUMMYARG)
+DEFINEFUNC3(void, SSL_get0_alpn_selected, const SSL *s, s, const unsigned char **data, data,
+ unsigned *len, len, return, DUMMYARG)
+#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L ...
#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
DEFINEFUNC(DH *, DH_new, DUMMYARG, DUMMYARG, return 0, return)
DEFINEFUNC(void, DH_free, DH *dh, dh, return, DUMMYARG)
DEFINEFUNC3(DH *, d2i_DHparams, DH**a, a, const unsigned char **pp, pp, long length, length, return 0, return)
+DEFINEFUNC2(int, i2d_DHparams, DH *a, a, unsigned char **p, p, return -1, return)
+DEFINEFUNC2(int, DH_check, DH *dh, dh, int *codes, codes, return 0, return)
DEFINEFUNC3(BIGNUM *, BN_bin2bn, const unsigned char *s, s, int len, len, BIGNUM *ret, ret, return 0, return)
#ifndef OPENSSL_NO_EC
DEFINEFUNC(EC_KEY *, EC_KEY_dup, const EC_KEY *ec, ec, return 0, return)
@@ -768,6 +791,10 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(EC_GROUP_get_degree)
#endif
RESOLVEFUNC(BN_num_bits)
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ RESOLVEFUNC(BN_is_word)
+#endif
+ RESOLVEFUNC(BN_mod_word)
RESOLVEFUNC(CRYPTO_free)
RESOLVEFUNC(CRYPTO_num_locks)
RESOLVEFUNC(CRYPTO_set_id_callback)
@@ -811,17 +838,20 @@ bool q_resolveOpenSslSymbols()
#ifdef SSLEAY_MACROS // ### verify
RESOLVEFUNC(PEM_ASN1_read_bio)
#else
+ RESOLVEFUNC(PEM_read_bio_PrivateKey)
RESOLVEFUNC(PEM_read_bio_DSAPrivateKey)
RESOLVEFUNC(PEM_read_bio_RSAPrivateKey)
#ifndef OPENSSL_NO_EC
RESOLVEFUNC(PEM_read_bio_ECPrivateKey)
#endif
+ RESOLVEFUNC(PEM_read_bio_DHparams)
RESOLVEFUNC(PEM_write_bio_DSAPrivateKey)
RESOLVEFUNC(PEM_write_bio_RSAPrivateKey)
#ifndef OPENSSL_NO_EC
RESOLVEFUNC(PEM_write_bio_ECPrivateKey)
#endif
#endif
+ RESOLVEFUNC(PEM_read_bio_PUBKEY)
RESOLVEFUNC(PEM_read_bio_DSA_PUBKEY)
RESOLVEFUNC(PEM_read_bio_RSA_PUBKEY)
#ifndef OPENSSL_NO_EC
@@ -889,6 +919,8 @@ bool q_resolveOpenSslSymbols()
#endif
#if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK)
RESOLVEFUNC(SSL_set_psk_client_callback)
+ RESOLVEFUNC(SSL_set_psk_server_callback)
+ RESOLVEFUNC(SSL_CTX_use_psk_identity_hint)
#endif
RESOLVEFUNC(SSL_write)
#ifndef OPENSSL_NO_SSL2
@@ -972,9 +1004,16 @@ bool q_resolveOpenSslSymbols()
RESOLVEFUNC(SSL_CTX_set_next_proto_select_cb)
RESOLVEFUNC(SSL_get0_next_proto_negotiated)
#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ RESOLVEFUNC(SSL_set_alpn_protos)
+ RESOLVEFUNC(SSL_CTX_set_alpn_select_cb)
+ RESOLVEFUNC(SSL_get0_alpn_selected)
+#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L ...
RESOLVEFUNC(DH_new)
RESOLVEFUNC(DH_free)
RESOLVEFUNC(d2i_DHparams)
+ RESOLVEFUNC(i2d_DHparams)
+ RESOLVEFUNC(DH_check)
RESOLVEFUNC(BN_bin2bn)
#ifndef OPENSSL_NO_EC
RESOLVEFUNC(EC_KEY_dup)
diff --git a/src/network/ssl/qsslsocket_openssl_symbols_p.h b/src/network/ssl/qsslsocket_openssl_symbols_p.h
index df17fd8a9c..b35a895d38 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols_p.h
+++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h
@@ -60,13 +60,14 @@
// W A R N I N G
// -------------
//
-// This file is not part of the Qt API. It exists for the convenience
-// of the QLibrary class. This header file may change from
-// version to version without notice, or even be removed.
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
//
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qsslsocket_openssl_p.h"
#include <QtCore/qglobal.h>
@@ -220,13 +221,28 @@ unsigned char * q_ASN1_STRING_data(ASN1_STRING *a);
int q_ASN1_STRING_length(ASN1_STRING *a);
int q_ASN1_STRING_to_UTF8(unsigned char **a, ASN1_STRING *b);
long q_BIO_ctrl(BIO *a, int b, long c, void *d);
-int q_BIO_free(BIO *a);
-BIO *q_BIO_new(BIO_METHOD *a);
+Q_AUTOTEST_EXPORT int q_BIO_free(BIO *a);
+Q_AUTOTEST_EXPORT BIO *q_BIO_new(BIO_METHOD *a);
BIO *q_BIO_new_mem_buf(void *a, int b);
int q_BIO_read(BIO *a, void *b, int c);
-BIO_METHOD *q_BIO_s_mem();
-int q_BIO_write(BIO *a, const void *b, int c);
+Q_AUTOTEST_EXPORT BIO_METHOD *q_BIO_s_mem();
+Q_AUTOTEST_EXPORT int q_BIO_write(BIO *a, const void *b, int c);
int q_BN_num_bits(const BIGNUM *a);
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+int q_BN_is_word(BIGNUM *a, BN_ULONG w);
+#else
+// BN_is_word is implemented purely as a
+// macro in OpenSSL < 1.1. It doesn't
+// call any functions.
+//
+// The implementation of BN_is_word is
+// 100% the same between 1.0.0, 1.0.1
+// and 1.0.2.
+//
+// Users are required to include <openssl/bn.h>.
+#define q_BN_is_word BN_is_word
+#endif // OPENSSL_VERSION_NUMBER >= 0x10100000L
+BN_ULONG q_BN_mod_word(const BIGNUM *a, BN_ULONG w);
#ifndef OPENSSL_NO_EC
const EC_GROUP* q_EC_KEY_get0_group(const EC_KEY* k);
int q_EC_GROUP_get_degree(const EC_GROUP* g);
@@ -279,11 +295,13 @@ void *q_PEM_ASN1_read_bio(d2i_of_void *a, const char *b, BIO *c, void **d, pem_p
void *f);
// ### ditto for write
#else
+Q_AUTOTEST_EXPORT EVP_PKEY *q_PEM_read_bio_PrivateKey(BIO *a, EVP_PKEY **b, pem_password_cb *c, void *d);
DSA *q_PEM_read_bio_DSAPrivateKey(BIO *a, DSA **b, pem_password_cb *c, void *d);
RSA *q_PEM_read_bio_RSAPrivateKey(BIO *a, RSA **b, pem_password_cb *c, void *d);
#ifndef OPENSSL_NO_EC
EC_KEY *q_PEM_read_bio_ECPrivateKey(BIO *a, EC_KEY **b, pem_password_cb *c, void *d);
#endif
+DH *q_PEM_read_bio_DHparams(BIO *a, DH **b, pem_password_cb *c, void *d);
int q_PEM_write_bio_DSAPrivateKey(BIO *a, DSA *b, const EVP_CIPHER *c, unsigned char *d,
int e, pem_password_cb *f, void *g);
int q_PEM_write_bio_RSAPrivateKey(BIO *a, RSA *b, const EVP_CIPHER *c, unsigned char *d,
@@ -293,6 +311,7 @@ int q_PEM_write_bio_ECPrivateKey(BIO *a, EC_KEY *b, const EVP_CIPHER *c, unsigne
int e, pem_password_cb *f, void *g);
#endif
#endif
+Q_AUTOTEST_EXPORT EVP_PKEY *q_PEM_read_bio_PUBKEY(BIO *a, EVP_PKEY **b, pem_password_cb *c, void *d);
DSA *q_PEM_read_bio_DSA_PUBKEY(BIO *a, DSA **b, pem_password_cb *c, void *d);
RSA *q_PEM_read_bio_RSA_PUBKEY(BIO *a, RSA **b, pem_password_cb *c, void *d);
#ifndef OPENSSL_NO_EC
@@ -376,6 +395,9 @@ void *q_SSL_get_ex_data(const SSL *ssl, int idx);
#if OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK)
typedef unsigned int (*q_psk_client_callback_t)(SSL *ssl, const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len);
void q_SSL_set_psk_client_callback(SSL *ssl, q_psk_client_callback_t callback);
+typedef unsigned int (*q_psk_server_callback_t)(SSL *ssl, const char *identity, unsigned char *psk, unsigned int max_psk_len);
+void q_SSL_set_psk_server_callback(SSL *ssl, q_psk_server_callback_t callback);
+int q_SSL_CTX_use_psk_identity_hint(SSL_CTX *ctx, const char *hint);
#endif // OPENSSL_VERSION_NUMBER >= 0x10001000L && !defined(OPENSSL_NO_PSK)
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
#ifndef OPENSSL_NO_SSL2
@@ -472,6 +494,8 @@ STACK_OF(X509) *q_X509_STORE_CTX_get_chain(X509_STORE_CTX *ctx);
DH *q_DH_new();
void q_DH_free(DH *dh);
DH *q_d2i_DHparams(DH **a, const unsigned char **pp, long length);
+int q_i2d_DHparams(DH *a, unsigned char **p);
+int q_DH_check(DH *dh, int *codes);
BIGNUM *q_BN_bin2bn(const unsigned char *s, int len, BIGNUM *ret);
#define q_SSL_CTX_set_tmp_dh(ctx, dh) q_SSL_CTX_ctrl((ctx), SSL_CTRL_SET_TMP_DH, 0, (char *)dh)
@@ -518,6 +542,9 @@ DSA *q_d2i_DSAPrivateKey(DSA **a, unsigned char **pp, long length);
#define q_PEM_write_bio_DSAPrivateKey(bp,x,enc,kstr,klen,cb,u) \
PEM_ASN1_write_bio((int (*)(void*, unsigned char**))q_i2d_DSAPrivateKey,PEM_STRING_DSA,\
bp,(char *)x,enc,kstr,klen,cb,u)
+#define q_PEM_read_bio_DHparams(bp, dh, cb, u) \
+ (DH *)q_PEM_ASN1_read_bio( \
+ (void *(*)(void**, const unsigned char**, long int))q_d2i_DHparams, PEM_STRING_DHPARAMS, bp, (void **)x, cb, u)
#endif
#define q_SSL_CTX_set_options(ctx,op) q_SSL_CTX_ctrl((ctx),SSL_CTRL_OPTIONS,(op),NULL)
#define q_SSL_CTX_set_mode(ctx,op) q_SSL_CTX_ctrl((ctx),SSL_CTRL_MODE,(op),NULL)
@@ -558,6 +585,19 @@ void q_SSL_CTX_set_next_proto_select_cb(SSL_CTX *s,
void *arg);
void q_SSL_get0_next_proto_negotiated(const SSL *s, const unsigned char **data,
unsigned *len);
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+int q_SSL_set_alpn_protos(SSL *ssl, const unsigned char *protos,
+ unsigned protos_len);
+void q_SSL_CTX_set_alpn_select_cb(SSL_CTX *ctx,
+ int (*cb) (SSL *ssl,
+ const unsigned char **out,
+ unsigned char *outlen,
+ const unsigned char *in,
+ unsigned int inlen,
+ void *arg), void *arg);
+void q_SSL_get0_alpn_selected(const SSL *ssl, const unsigned char **data,
+ unsigned *len);
+#endif
#endif // OPENSSL_VERSION_NUMBER >= 0x1000100fL ...
// Helper function
diff --git a/src/network/ssl/qsslsocket_p.h b/src/network/ssl/qsslsocket_p.h
index 5d4d52cd6d..cec61d07c1 100644
--- a/src/network/ssl/qsslsocket_p.h
+++ b/src/network/ssl/qsslsocket_p.h
@@ -47,13 +47,14 @@
// W A R N I N G
// -------------
//
-// This file is not part of the Qt API. It exists for the convenience
-// of the QLibrary class. This header file may change from
-// version to version without notice, or even be removed.
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
//
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <private/qtcpsocket_p.h>
#include "qsslkey.h"
#include "qsslconfiguration_p.h"
@@ -89,11 +90,7 @@ QT_BEGIN_NAMESPACE
#endif
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
-#if defined(Q_OS_WINCE)
- typedef HCERTSTORE (WINAPI *PtrCertOpenSystemStoreW)(LPCSTR, DWORD, HCRYPTPROV_LEGACY, DWORD, const void*);
-#else
typedef HCERTSTORE (WINAPI *PtrCertOpenSystemStoreW)(HCRYPTPROV_LEGACY, LPCWSTR);
-#endif
typedef PCCERT_CONTEXT (WINAPI *PtrCertFindCertificateInStore)(HCERTSTORE, DWORD, DWORD, DWORD, const void*, PCCERT_CONTEXT);
typedef BOOL (WINAPI *PtrCertCloseStore)(HCERTSTORE, DWORD);
#endif // Q_OS_WIN && !Q_OS_WINRT
@@ -193,6 +190,7 @@ public:
virtual qint64 peek(char *data, qint64 maxSize) Q_DECL_OVERRIDE;
virtual QByteArray peek(qint64 maxSize) Q_DECL_OVERRIDE;
+ bool flush() Q_DECL_OVERRIDE;
// Platform specific functions
virtual void startClientEncryption() = 0;
diff --git a/src/network/ssl/qsslsocket_winrt.cpp b/src/network/ssl/qsslsocket_winrt.cpp
index edc8ca0bbb..f5dc9fcdcd 100644
--- a/src/network/ssl/qsslsocket_winrt.cpp
+++ b/src/network/ssl/qsslsocket_winrt.cpp
@@ -215,7 +215,9 @@ QList<QSslCipher> QSslSocketBackendPrivate::defaultCiphers()
const QString protocolStrings[] = { QStringLiteral("SSLv3"), QStringLiteral("TLSv1"),
QStringLiteral("TLSv1.1"), QStringLiteral("TLSv1.2") };
const QSsl::SslProtocol protocols[] = { QSsl::SslV3, QSsl::TlsV1_0, QSsl::TlsV1_1, QSsl::TlsV1_2 };
- for (int i = 0; i < ARRAYSIZE(protocols); ++i) {
+ const int size = static_cast<int>(ARRAYSIZE(protocols));
+ ciphers.reserve(size);
+ for (int i = 0; i < size; ++i) {
QSslCipher cipher;
cipher.d->isNull = false;
cipher.d->name = QStringLiteral("WINRT");
diff --git a/src/network/ssl/qsslsocket_winrt_p.h b/src/network/ssl/qsslsocket_winrt_p.h
index 3946a0e60b..1043aeee11 100644
--- a/src/network/ssl/qsslsocket_winrt_p.h
+++ b/src/network/ssl/qsslsocket_winrt_p.h
@@ -51,6 +51,7 @@
// We mean it.
//
+#include <QtNetwork/private/qtnetworkglobal_p.h>
#include "qsslsocket_p.h"
#include <wrl.h>
diff --git a/src/network/ssl/ssl.pri b/src/network/ssl/ssl.pri
index c70664ef9b..8139af50af 100644
--- a/src/network/ssl/ssl.pri
+++ b/src/network/ssl/ssl.pri
@@ -1,5 +1,5 @@
# OpenSSL support; compile in QSslSocket.
-contains(QT_CONFIG, ssl) | contains(QT_CONFIG, openssl) | contains(QT_CONFIG, openssl-linked) {
+qtConfig(ssl) {
HEADERS += ssl/qasn1element_p.h \
ssl/qssl.h \
ssl/qssl_p.h \
@@ -9,6 +9,8 @@ contains(QT_CONFIG, ssl) | contains(QT_CONFIG, openssl) | contains(QT_CONFIG, op
ssl/qsslconfiguration_p.h \
ssl/qsslcipher.h \
ssl/qsslcipher_p.h \
+ ssl/qssldiffiehellmanparameters.h \
+ ssl/qssldiffiehellmanparameters_p.h \
ssl/qsslellipticcurve.h \
ssl/qsslerror.h \
ssl/qsslkey.h \
@@ -24,6 +26,7 @@ contains(QT_CONFIG, ssl) | contains(QT_CONFIG, openssl) | contains(QT_CONFIG, op
ssl/qsslcertificate.cpp \
ssl/qsslconfiguration.cpp \
ssl/qsslcipher.cpp \
+ ssl/qssldiffiehellmanparameters.cpp \
ssl/qsslellipticcurve.cpp \
ssl/qsslkey_p.cpp \
ssl/qsslerror.cpp \
@@ -35,52 +38,55 @@ contains(QT_CONFIG, ssl) | contains(QT_CONFIG, openssl) | contains(QT_CONFIG, op
HEADERS += ssl/qsslsocket_winrt_p.h
SOURCES += ssl/qsslcertificate_qt.cpp \
ssl/qsslcertificate_winrt.cpp \
+ ssl/qssldiffiehellmanparameters_dummy.cpp \
ssl/qsslkey_qt.cpp \
ssl/qsslkey_winrt.cpp \
ssl/qsslsocket_winrt.cpp \
ssl/qsslellipticcurve_dummy.cpp
}
- contains(QT_CONFIG, securetransport) {
+ qtConfig(securetransport) {
HEADERS += ssl/qsslsocket_mac_p.h
SOURCES += ssl/qsslcertificate_qt.cpp \
+ ssl/qssldiffiehellmanparameters_dummy.cpp \
ssl/qsslkey_qt.cpp \
ssl/qsslkey_mac.cpp \
ssl/qsslsocket_mac_shared.cpp \
ssl/qsslsocket_mac.cpp \
ssl/qsslellipticcurve_dummy.cpp
}
-}
-contains(QT_CONFIG, openssl) | contains(QT_CONFIG, openssl-linked) {
- HEADERS += ssl/qsslcontext_openssl_p.h \
- ssl/qsslsocket_openssl_p.h \
- ssl/qsslsocket_openssl_symbols_p.h
- SOURCES += ssl/qsslcertificate_openssl.cpp \
- ssl/qsslcontext_openssl.cpp \
- ssl/qsslellipticcurve_openssl.cpp \
- ssl/qsslkey_openssl.cpp \
- ssl/qsslsocket_openssl.cpp \
- ssl/qsslsocket_openssl_symbols.cpp
+ qtConfig(openssl)|qtConfig(openssl-linked) {
+ HEADERS += ssl/qsslcontext_openssl_p.h \
+ ssl/qsslsocket_openssl_p.h \
+ ssl/qsslsocket_openssl_symbols_p.h
+ SOURCES += ssl/qsslcertificate_openssl.cpp \
+ ssl/qsslcontext_openssl.cpp \
+ ssl/qssldiffiehellmanparameters_openssl.cpp \
+ ssl/qsslellipticcurve_openssl.cpp \
+ ssl/qsslkey_openssl.cpp \
+ ssl/qsslsocket_openssl.cpp \
+ ssl/qsslsocket_openssl_symbols.cpp
- darwin:SOURCES += ssl/qsslsocket_mac_shared.cpp
+ darwin:SOURCES += ssl/qsslsocket_mac_shared.cpp
- android: SOURCES += ssl/qsslsocket_openssl_android.cpp
+ android: SOURCES += ssl/qsslsocket_openssl_android.cpp
- # Add optional SSL libs
- # Static linking of OpenSSL with msvc:
- # - Binaries http://slproweb.com/products/Win32OpenSSL.html
- # - also needs -lUser32 -lAdvapi32 -lGdi32 -lCrypt32
- # - libs in <OPENSSL_DIR>\lib\VC\static
- # - configure: -openssl -openssl-linked -I <OPENSSL_DIR>\include -L <OPENSSL_DIR>\lib\VC\static OPENSSL_LIBS="-lUser32 -lAdvapi32 -lGdi32" OPENSSL_LIBS_DEBUG="-lssleay32MDd -llibeay32MDd" OPENSSL_LIBS_RELEASE="-lssleay32MD -llibeay32MD"
+ # Add optional SSL libs
+ # Static linking of OpenSSL with msvc:
+ # - Binaries http://slproweb.com/products/Win32OpenSSL.html
+ # - also needs -lUser32 -lAdvapi32 -lGdi32 -lCrypt32
+ # - libs in <OPENSSL_DIR>\lib\VC\static
+ # - configure: -openssl -openssl-linked -I <OPENSSL_DIR>\include -L <OPENSSL_DIR>\lib\VC\static OPENSSL_LIBS="-lUser32 -lAdvapi32 -lGdi32" OPENSSL_LIBS_DEBUG="-lssleay32MDd -llibeay32MDd" OPENSSL_LIBS_RELEASE="-lssleay32MD -llibeay32MD"
- CONFIG(debug, debug|release) {
- LIBS_PRIVATE += $$OPENSSL_LIBS_DEBUG
- } else {
- LIBS_PRIVATE += $$OPENSSL_LIBS_RELEASE
- }
+ CONFIG(debug, debug|release) {
+ LIBS_PRIVATE += $$OPENSSL_LIBS_DEBUG
+ } else {
+ LIBS_PRIVATE += $$OPENSSL_LIBS_RELEASE
+ }
- QMAKE_CXXFLAGS += $$OPENSSL_CFLAGS
- LIBS_PRIVATE += $$OPENSSL_LIBS
- win32: LIBS_PRIVATE += -lcrypt32
+ QMAKE_CXXFLAGS += $$OPENSSL_CFLAGS
+ LIBS_PRIVATE += $$OPENSSL_LIBS
+ win32: LIBS_PRIVATE += -lcrypt32
+ }
}