summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
Diffstat (limited to 'src/network')
-rw-r--r--src/network/access/access.pri7
-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.pri11
-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/qhttpnetworkreply.cpp5
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp40
-rw-r--r--src/network/access/qnetworkaccessmanager.h1
-rw-r--r--src/network/access/qnetworkaccessmanager_p.h7
-rw-r--r--src/network/access/qnetworkdiskcache.cpp2
-rw-r--r--src/network/access/qnetworkfile.cpp (renamed from src/network/kernel/qnetworkfunctions_wince.h)96
-rw-r--r--src/network/access/qnetworkfile_p.h79
-rw-r--r--src/network/access/qnetworkreplyfileimpl.cpp150
-rw-r--r--src/network/access/qnetworkreplyfileimpl_p.h12
-rw-r--r--src/network/access/qnetworkreplyhttpimpl.cpp15
-rw-r--r--src/network/access/qspdyprotocolhandler.cpp8
-rw-r--r--src/network/kernel/kernel.pri4
-rw-r--r--src/network/kernel/qauthenticator.cpp6
-rw-r--r--src/network/kernel/qhostaddress.cpp120
-rw-r--r--src/network/kernel/qhostaddress.h12
-rw-r--r--src/network/kernel/qhostinfo_win.cpp11
-rw-r--r--src/network/kernel/qnetworkinterface_win.cpp8
-rw-r--r--src/network/kernel/qnetworkproxy_mac.cpp97
-rw-r--r--src/network/kernel/qnetworkproxy_win.cpp19
-rw-r--r--src/network/socket/qabstractsocket.cpp79
-rw-r--r--src/network/socket/qabstractsocket_p.h4
-rw-r--r--src/network/socket/qnativesocketengine_p.h14
-rw-r--r--src/network/socket/qnativesocketengine_win.cpp46
-rw-r--r--src/network/socket/qsocks5socketengine.cpp4
-rw-r--r--src/network/socket/qtcpserver.cpp5
-rw-r--r--src/network/socket/qudpsocket.cpp3
-rw-r--r--src/network/socket/socket.pri11
-rw-r--r--src/network/ssl/qsslconfiguration.cpp77
-rw-r--r--src/network/ssl/qsslconfiguration.h7
-rw-r--r--src/network/ssl/qsslconfiguration_p.h7
-rw-r--r--src/network/ssl/qsslcontext_openssl.cpp61
-rw-r--r--src/network/ssl/qssldiffiehellmanparameters.cpp307
-rw-r--r--src/network/ssl/qssldiffiehellmanparameters.h117
-rw-r--r--src/network/ssl/qssldiffiehellmanparameters_dummy.cpp59
-rw-r--r--src/network/ssl/qssldiffiehellmanparameters_openssl.cpp164
-rw-r--r--src/network/ssl/qssldiffiehellmanparameters_p.h78
-rw-r--r--src/network/ssl/qsslsocket.cpp28
-rw-r--r--src/network/ssl/qsslsocket.h2
-rw-r--r--src/network/ssl/qsslsocket_mac.cpp187
-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.h1
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols.cpp30
-rw-r--r--src/network/ssl/qsslsocket_openssl_symbols_p.h37
-rw-r--r--src/network/ssl/qsslsocket_p.h5
-rw-r--r--src/network/ssl/qsslsocket_winrt.cpp4
-rw-r--r--src/network/ssl/ssl.pri62
57 files changed, 4291 insertions, 644 deletions
diff --git a/src/network/access/access.pri b/src/network/access/access.pri
index 38e9c25269..746ddc6916 100644
--- a/src/network/access/access.pri
+++ b/src/network/access/access.pri
@@ -37,7 +37,8 @@ HEADERS += \
access/qnetworkdiskcache.h \
access/qhttpthreaddelegate_p.h \
access/qhttpmultipart.h \
- access/qhttpmultipart_p.h
+ access/qhttpmultipart_p.h \
+ access/qnetworkfile_p.h
SOURCES += \
access/qftp.cpp \
@@ -68,8 +69,10 @@ SOURCES += \
access/qabstractnetworkcache.cpp \
access/qnetworkdiskcache.cpp \
access/qhttpthreaddelegate.cpp \
- access/qhttpmultipart.cpp
+ access/qhttpmultipart.cpp \
+ access/qnetworkfile.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..2157e35e2f
--- /dev/null
+++ b/src/network/access/http2/http2.pri
@@ -0,0 +1,11 @@
+HEADERS += \
+ access/http2/bitstreams_p.h \
+ access/http2/huffman_p.h \
+ access/http2/hpack_p.h \
+ access/http2/hpacktable_p.h
+
+SOURCES += \
+ access/http2/bitstreams.cpp \
+ access/http2/huffman.cpp \
+ access/http2/hpack.cpp \
+ access/http2/hpacktable.cpp
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/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/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index 587ab27a0f..9fee172283 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -1516,27 +1516,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..6df0b63ba7 100644
--- a/src/network/access/qnetworkaccessmanager.h
+++ b/src/network/access/qnetworkaccessmanager.h
@@ -173,6 +173,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..5f8148d6e4 100644
--- a/src/network/access/qnetworkaccessmanager_p.h
+++ b/src/network/access/qnetworkaccessmanager_p.h
@@ -74,7 +74,7 @@ class QNetworkAccessManagerPrivate: public QObjectPrivate
public:
QNetworkAccessManagerPrivate()
: networkCache(0), cookieJar(0),
- httpThread(0),
+ thread(0),
#ifndef QT_NO_NETWORKPROXY
proxyFactory(0),
#endif
@@ -107,6 +107,9 @@ public:
}
~QNetworkAccessManagerPrivate();
+ QThread * createThread();
+ void destroyThread();
+
void _q_replyFinished();
void _q_replyEncrypted();
void _q_replySslErrors(const QList<QSslError> &errors);
@@ -163,7 +166,7 @@ public:
QNetworkCookieJar *cookieJar;
- QThread *httpThread;
+ QThread *thread;
#ifndef QT_NO_NETWORKPROXY
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/kernel/qnetworkfunctions_wince.h b/src/network/access/qnetworkfile.cpp
index 0e464a47f3..374dd26e2e 100644
--- a/src/network/kernel/qnetworkfunctions_wince.h
+++ b/src/network/access/qnetworkfile.cpp
@@ -37,60 +37,56 @@
**
****************************************************************************/
-#ifndef QNETWORKFUNCTIONS_WINCE_H
-#define QNETWORKFUNCTIONS_WINCE_H
+#include "qnetworkfile_p.h"
-#if 0
-#pragma qt_sync_stop_processing
-#endif
+#include <QtCore/QDebug>
+#include <QNetworkReply>
+#include <QtCore/QDateTime>
+#include <QtCore/QFileInfo>
+#include <QtCore/QMetaObject>
+#include <QtCore/QCoreApplication>
-#ifdef Q_OS_WINCE
+QT_BEGIN_NAMESPACE
-#include <qt_windows.h>
+QNetworkFile::QNetworkFile()
+ : QFile()
+{
+}
-QT_BEGIN_NAMESPACE
+QNetworkFile::QNetworkFile(const QString &name)
+ : QFile(name)
+{
+}
-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;
-#else
-typedef ENUM_SERVICE_STATUS_PROCESSA ENUM_SERVICE_STATUS_PROCESS;
-typedef LPENUM_SERVICE_STATUS_PROCESSA LPENUM_SERVICE_STATUS_PROCESS;
-#endif // UNICODE
+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);
+}
-QT_END_NAMESPACE
+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();
+}
-#endif // Q_OS_WINCE
-#endif // QNETWORKFUNCTIONS_WINCE_H
+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..7794c0f18a
--- /dev/null
+++ b/src/network/access/qnetworkfile_p.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+**
+** 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 <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/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..1f1be40bc8 100644
--- a/src/network/access/qnetworkreplyfileimpl_p.h
+++ b/src/network/access/qnetworkreplyfileimpl_p.h
@@ -59,13 +59,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 +76,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 +87,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..30f4bb26ce 100644
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
@@ -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();
@@ -1123,8 +1116,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/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/kernel/kernel.pri b/src/network/kernel/kernel.pri
index 811d8b6f0d..b50ca0d1c4 100644
--- a/src/network/kernel/kernel.pri
+++ b/src/network/kernel/kernel.pri
@@ -53,10 +53,10 @@ win32: {
mac {
LIBS_PRIVATE += -framework SystemConfiguration -framework CoreFoundation
- !ios: LIBS_PRIVATE += -framework CoreServices
+ !uikit: LIBS_PRIVATE += -framework CoreServices
}
-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) {
SOURCES += kernel/qnetworkproxy_libproxy.cpp
diff --git a/src/network/kernel/qauthenticator.cpp b/src/network/kernel/qauthenticator.cpp
index a73f01a2fc..959db6e9d9 100644
--- a/src/network/kernel/qauthenticator.cpp
+++ b/src/network/kernel/qauthenticator.cpp
@@ -1455,15 +1455,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/qhostaddress.cpp b/src/network/kernel/qhostaddress.cpp
index 13ecfac3f5..95f90af49e 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()
@@ -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..8cf6876511 100644
--- a/src/network/kernel/qhostaddress.h
+++ b/src/network/kernel/qhostaddress.h
@@ -79,6 +79,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 +128,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 +150,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/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/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_mac.cpp b/src/network/kernel/qnetworkproxy_mac.cpp
index 06a6fbac45..37126298c6 100644
--- a/src/network/kernel/qnetworkproxy_mac.cpp
+++ b/src/network/kernel/qnetworkproxy_mac.cpp
@@ -234,60 +234,55 @@ 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 {
+ }
+ SInt32 errorCode;
+ if (!CFURLCreateDataAndPropertiesFromResource(kCFAllocatorDefault, pacUrl, &pacData, NULL, NULL, &errorCode)) {
QString pacLocation = QCFString::toQString(cfPacLocation);
- qWarning("Mac system proxy: PAC script at \"%s\" not handled", qPrintable(pacLocation));
+ 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);
}
+ return result;
}
}
diff --git a/src/network/kernel/qnetworkproxy_win.cpp b/src/network/kernel/qnetworkproxy_win.cpp
index 513eeaac12..2727bd9257 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)
{
@@ -361,7 +358,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 +409,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 +428,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 +464,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 +474,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 +562,6 @@ void QWindowsSystemProxy::init()
}
functional = isAutoConfig || !proxyServerList.isEmpty();
-#endif
}
QList<QNetworkProxy> QNetworkProxyFactory::systemProxyForQuery(const QNetworkProxyQuery &query)
diff --git a/src/network/socket/qabstractsocket.cpp b/src/network/socket/qabstractsocket.cpp
index 098739adc3..cbae297278 100644
--- a/src/network/socket/qabstractsocket.cpp
+++ b/src/network/socket/qabstractsocket.cpp
@@ -563,6 +563,7 @@ QAbstractSocketPrivate::QAbstractSocketPrivate()
cachedSocketDescriptor(-1),
readBufferMaxSize(0),
isBuffered(false),
+ hasPendingData(false),
connectTimer(0),
disconnectTimer(0),
hostLookupId(-1),
@@ -593,6 +594,7 @@ void QAbstractSocketPrivate::resetSocketLayer()
qDebug("QAbstractSocketPrivate::resetSocketLayer()");
#endif
+ hasPendingData = false;
if (socketEngine) {
socketEngine->close();
socketEngine->disconnect();
@@ -683,14 +685,20 @@ bool QAbstractSocketPrivate::canReadNotification()
qDebug("QAbstractSocketPrivate::canReadNotification()");
#endif
- if (!isBuffered)
- socketEngine->setReadNotificationEnabled(false);
+ if (!isBuffered) {
+ if (hasPendingData) {
+ socketEngine->setReadNotificationEnabled(false);
+ return true;
+ }
+ hasPendingData = true;
+ }
// If buffered, read data from the socket into the read buffer
qint64 newBytes = 0;
if (isBuffered) {
// Return if there is no space in the buffer
if (readBufferMaxSize && buffer.size() >= readBufferMaxSize) {
+ socketEngine->setReadNotificationEnabled(false);
#if defined (QABSTRACTSOCKET_DEBUG)
qDebug("QAbstractSocketPrivate::canReadNotification() buffer is full");
#endif
@@ -708,11 +716,6 @@ bool QAbstractSocketPrivate::canReadNotification()
return false;
}
newBytes = buffer.size() - newBytes;
-
- // If read buffer is full, disable the read socket notifier.
- if (readBufferMaxSize && buffer.size() == readBufferMaxSize) {
- socketEngine->setReadNotificationEnabled(false);
- }
}
// Only emit readyRead() if there is data available.
@@ -728,10 +731,6 @@ bool QAbstractSocketPrivate::canReadNotification()
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;
}
@@ -788,12 +787,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
@@ -833,8 +828,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;
}
@@ -872,8 +871,7 @@ bool QAbstractSocketPrivate::writeToSocket()
emit q->channelBytesWritten(0, written);
}
- if (writeBuffer.isEmpty() && socketEngine && socketEngine->isWriteNotificationEnabled()
- && !socketEngine->bytesToWrite())
+ if (writeBuffer.isEmpty() && socketEngine && !socketEngine->bytesToWrite())
socketEngine->setWriteNotificationEnabled(false);
if (state == QAbstractSocket::ClosingState)
q->disconnectFromHost();
@@ -1146,12 +1144,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.
@@ -1168,11 +1164,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");
@@ -2381,11 +2372,6 @@ void QAbstractSocket::abort()
return;
}
#endif
- if (d->connectTimer) {
- d->connectTimer->stop();
- delete d->connectTimer;
- d->connectTimer = 0;
- }
d->abortCalled = true;
close();
@@ -2432,15 +2418,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
@@ -2463,8 +2441,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);
}
@@ -2830,12 +2809,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_p.h b/src/network/socket/qabstractsocket_p.h
index b718c21ff5..3164c96c1e 100644
--- a/src/network/socket/qabstractsocket_p.h
+++ b/src/network/socket/qabstractsocket_p.h
@@ -128,13 +128,12 @@ 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();
@@ -144,6 +143,7 @@ public:
qint64 readBufferMaxSize;
bool isBuffered;
+ bool hasPendingData;
QTimer *connectTimer;
QTimer *disconnectTimer;
diff --git a/src/network/socket/qnativesocketengine_p.h b/src/network/socket/qnativesocketengine_p.h
index 19e9e1d9b7..5a05d7c98c 100644
--- a/src/network/socket/qnativesocketengine_p.h
+++ b/src/network/socket/qnativesocketengine_p.h
@@ -66,21 +66,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 +95,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;
@@ -210,7 +208,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_win.cpp b/src/network/socket/qnativesocketengine_win.cpp
index 5ffe7b11b8..0c5b8d9264 100644
--- a/src/network/socket/qnativesocketengine_win.cpp
+++ b/src/network/socket/qnativesocketengine_win.cpp
@@ -387,7 +387,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 +413,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) {
@@ -1091,7 +1089,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 +1115,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 +1126,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 +1170,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 +1177,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 +1296,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 +1458,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 +1571,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/qsocks5socketengine.cpp b/src/network/socket/qsocks5socketengine.cpp
index a57a1dca2c..ee3e0d9f0e 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) { \
diff --git a/src/network/socket/qtcpserver.cpp b/src/network/socket/qtcpserver.cpp
index de1dc29cfb..d9ffdbd214 100644
--- a/src/network/socket/qtcpserver.cpp
+++ b/src/network/socket/qtcpserver.cpp
@@ -543,8 +543,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/qudpsocket.cpp b/src/network/socket/qudpsocket.cpp
index c406009069..083648bc23 100644
--- a/src/network/socket/qudpsocket.cpp
+++ b/src/network/socket/qudpsocket.cpp
@@ -398,7 +398,8 @@ qint64 QUdpSocket::readDatagram(char *data, qint64 maxSize, QHostAddress *addres
readBytes = d->socketEngine->readDatagram(data, maxSize);
}
- d_func()->socketEngine->setReadNotificationEnabled(true);
+ d->hasPendingData = false;
+ d->socketEngine->setReadNotificationEnabled(true);
if (readBytes < 0)
d->setErrorAndEmit(d->socketEngine->error(), d->socketEngine->errorString());
return readBytes;
diff --git a/src/network/socket/socket.pri b/src/network/socket/socket.pri
index f50a7b1229..2d80f38bec 100644
--- a/src/network/socket/socket.pri
+++ b/src/network/socket/socket.pri
@@ -43,7 +43,7 @@ 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 +54,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
diff --git a/src/network/ssl/qsslconfiguration.cpp b/src/network/ssl/qsslconfiguration.cpp
index c8040dec7f..e009824a69 100644
--- a/src/network/ssl/qsslconfiguration.cpp
+++ b/src/network/ssl/qsslconfiguration.cpp
@@ -119,7 +119,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 +210,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 +257,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 +265,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 +816,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. 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.
+
+ 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 +891,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 +914,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 +927,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..61246e1009 100644
--- a/src/network/ssl/qsslconfiguration.h
+++ b/src/network/ssl/qsslconfiguration.h
@@ -69,6 +69,7 @@ class QSslCertificate;
class QSslCipher;
class QSslKey;
class QSslEllipticCurve;
+class QSslDiffieHellmanParameters;
class QSslConfigurationPrivate;
class Q_NETWORK_EXPORT QSslConfiguration
@@ -141,6 +142,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);
diff --git a/src/network/ssl/qsslconfiguration_p.h b/src/network/ssl/qsslconfiguration_p.h
index 364bba92ec..139a9fc32b 100644
--- a/src/network/ssl/qsslconfiguration_p.h
+++ b/src/network/ssl/qsslconfiguration_p.h
@@ -73,6 +73,7 @@
#include "qsslcipher.h"
#include "qsslkey.h"
#include "qsslellipticcurve.h"
+#include "qssldiffiehellmanparameters.h"
QT_BEGIN_NAMESPACE
@@ -87,8 +88,10 @@ public:
allowRootCertOnDemandLoading(true),
peerSessionShared(false),
sslOptions(QSslConfigurationPrivate::defaultSslOptions),
+ dhParams(QSslDiffieHellmanParameters::defaultParameters()),
sslSessionTicketLifeTimeHint(-1),
ephemeralServerKey(),
+ preSharedKeyIdentityHint(),
nextProtocolNegotiationStatus(QSslConfiguration::NextProtocolNegotiationNone)
{ }
@@ -117,11 +120,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..5a80d08e24 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.data()->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
+#ifndef OPENSSL_NO_PSK
+ if (!client)
+ q_SSL_CTX_use_psk_identity_hint(sslContext->ctx, sslContext->sslConfiguration.preSharedKeyIdentityHint().constData());
+#endif // 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/qssldiffiehellmanparameters.cpp b/src/network/ssl/qssldiffiehellmanparameters.cpp
new file mode 100644
index 0000000000..d411ea0a5d
--- /dev/null
+++ b/src/network/ssl/qssldiffiehellmanparameters.cpp
@@ -0,0 +1,307 @@
+/****************************************************************************
+**
+** 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 QSslDiffieHellmanParameters(
+ 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)
+{
+}
+
+/*!
+ Constructs a QSslDiffieHellmanParameters object using
+ the byte array \a encoded in either PEM or DER form.
+
+ After construction, the isValid() method should be used to
+ check whether the Diffie-Hellman parameters were valid and
+ loaded correctly.
+
+ \sa isValid()
+ \sa QSslConfiguration
+*/
+QSslDiffieHellmanParameters::QSslDiffieHellmanParameters(const QByteArray &encoded, QSsl::EncodingFormat encoding)
+ : d(new QSslDiffieHellmanParametersPrivate)
+{
+ switch (encoding) {
+ case QSsl::Der:
+ d->decodeDer(encoded);
+ break;
+ case QSsl::Pem:
+ d->decodePem(encoded);
+ break;
+ }
+}
+
+/*!
+ Constructs a QSslDiffieHellmanParameters object by
+ reading from \a device in either PEM or DER form.
+
+ After construction, the isValid() method should be used
+ to check whether the Diffie-Hellman parameters were valid
+ and loaded correctly.
+
+ \sa isValid()
+ \sa QSslConfiguration
+*/
+QSslDiffieHellmanParameters::QSslDiffieHellmanParameters(QIODevice *device, QSsl::EncodingFormat encoding)
+ : d(new QSslDiffieHellmanParametersPrivate)
+{
+ if (!device)
+ return;
+
+ const QByteArray encoded = device->readAll();
+
+ switch (encoding) {
+ case QSsl::Der:
+ d->decodeDer(encoded);
+ break;
+ case QSsl::Pem:
+ d->decodePem(encoded);
+ break;
+ }
+}
+
+/*!
+ Constructs an identical copy of \a other.
+*/
+QSslDiffieHellmanParameters::QSslDiffieHellmanParameters(const QSslDiffieHellmanParameters &other) : d(other.d)
+{
+}
+
+/*!
+ Destroys the QSslDiffieHellmanParameters object.
+*/
+QSslDiffieHellmanParameters::~QSslDiffieHellmanParameters()
+{
+}
+
+/*!
+ 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)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ \fn QSslDiffieHellmanParameters &QSslDiffieHellmanParameters::operator=(QSslDiffieHellmanParameters &&other)
+
+ Move-assigns \a other to this QSslDiffieHellmanParameters instance.
+*/
+
+/*!
+ \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 clear()
+ \sa error()
+*/
+bool QSslDiffieHellmanParameters::isValid() const Q_DECL_NOTHROW
+{
+ return d->error == QSslDiffieHellmanParameters::NoError;
+}
+
+/*!
+ \enum QSslDiffieHellmanParameters::Error
+
+ Describes a QSslDiffieHellmanParameters error.
+
+ \value ErrorInvalidInputData The given input data could not be used to
+ construct a QSslDiffieHellmanParameters
+ object.
+
+ \value ErrorUnsafeParameters 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();
+}
+
+/*!
+ \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
+/*!
+ \relates QSslDiffieHellmanParameters
+
+ Writes the set of Diffie-Hellman parameters in \a dhparm 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
+
+/*!
+ \relates QHash
+
+ 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..aa40be83a6
--- /dev/null
+++ b/src/network/ssl/qssldiffiehellmanparameters.h
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** 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 explicit QSslDiffieHellmanParameters(const QByteArray &encoded, QSsl::EncodingFormat format = QSsl::Pem);
+ Q_NETWORK_EXPORT explicit QSslDiffieHellmanParameters(QIODevice *device, QSsl::EncodingFormat format = QSsl::Pem);
+ Q_NETWORK_EXPORT QSslDiffieHellmanParameters(const QSslDiffieHellmanParameters &other);
+ Q_NETWORK_EXPORT ~QSslDiffieHellmanParameters();
+ Q_NETWORK_EXPORT QSslDiffieHellmanParameters &operator=(const QSslDiffieHellmanParameters &other);
+#ifdef Q_COMPILER_RVALUE_REFS
+ QSslDiffieHellmanParameters &operator=(QSslDiffieHellmanParameters &&other) Q_DECL_NOTHROW { swap(other); return *this; }
+#endif
+
+ void swap(QSslDiffieHellmanParameters &other) Q_DECL_NOTHROW { qSwap(d, other.d); }
+
+ Q_NETWORK_EXPORT bool isEmpty() const Q_DECL_NOTHROW;
+ Q_NETWORK_EXPORT bool isValid() const Q_DECL_NOTHROW;
+ Q_NETWORK_EXPORT QSslDiffieHellmanParameters::Error error() const Q_DECL_NOTHROW;
+ Q_NETWORK_EXPORT QString errorString() const Q_DECL_NOTHROW;
+
+private:
+ QExplicitlySharedDataPointer<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..42222f3f38
--- /dev/null
+++ b/src/network/ssl/qssldiffiehellmanparameters_dummy.cpp
@@ -0,0 +1,59 @@
+/****************************************************************************
+**
+** 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 &)
+{
+ Q_UNIMPLEMENTED();
+}
+
+void QSslDiffieHellmanParametersPrivate::decodePem(const QByteArray &)
+{
+ Q_UNIMPLEMENTED();
+}
+
+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..a5da4e51fc
--- /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
+
+#include "qsslkey.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 <QSharedData>
+
+#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/qsslsocket.cpp b/src/network/ssl/qsslsocket.cpp
index 472db3aa81..580b0fbdde 100644
--- a/src/network/ssl/qsslsocket.cpp
+++ b/src/network/ssl/qsslsocket.cpp
@@ -836,15 +836,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();
}
/*!
@@ -923,6 +915,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();
@@ -2615,6 +2609,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..1f2ed7687b 100644
--- a/src/network/ssl/qsslsocket.h
+++ b/src/network/ssl/qsslsocket.h
@@ -116,7 +116,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 c164342166..194acbeacc 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 not used on iOS or tvOS. (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. (defined in MacErrors.h on OS X)
+#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_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..c6572315f0 100644
--- a/src/network/ssl/qsslsocket_openssl_p.h
+++ b/src/network/ssl/qsslsocket_openssl_p.h
@@ -143,6 +143,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..0f9241e470 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)
@@ -207,6 +211,7 @@ DEFINEFUNC4(RSA *, PEM_read_bio_RSAPrivateKey, BIO *a, a, RSA **b, b, pem_passwo
#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
@@ -300,6 +305,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 +425,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 +789,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)
@@ -816,6 +841,7 @@ bool q_resolveOpenSslSymbols()
#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
@@ -889,6 +915,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
@@ -975,6 +1003,8 @@ bool q_resolveOpenSslSymbols()
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 36e041b6cb..c0ddb8e888 100644
--- a/src/network/ssl/qsslsocket_openssl_symbols_p.h
+++ b/src/network/ssl/qsslsocket_openssl_symbols_p.h
@@ -227,6 +227,21 @@ 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);
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);
@@ -284,6 +299,7 @@ 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,
@@ -376,6 +392,9 @@ void *q_SSL_get_ex_data(const SSL *ssl, int idx);
#ifndef 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_NO_PSK
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
#ifndef OPENSSL_NO_SSL2
@@ -472,6 +491,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 +539,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 +582,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..e791b9d166 100644
--- a/src/network/ssl/qsslsocket_p.h
+++ b/src/network/ssl/qsslsocket_p.h
@@ -89,11 +89,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 +189,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/ssl.pri b/src/network/ssl/ssl.pri
index c70664ef9b..edbbeadf51 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) {
+contains(QT_CONFIG, 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,6 +38,7 @@ 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 \
@@ -44,43 +48,45 @@ contains(QT_CONFIG, ssl) | contains(QT_CONFIG, openssl) | contains(QT_CONFIG, op
contains(QT_CONFIG, 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
+ 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/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
+ }
}