summaryrefslogtreecommitdiffstats
path: root/src/network/access/http2
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/access/http2')
-rw-r--r--src/network/access/http2/http2.pri10
-rw-r--r--src/network/access/http2/http2frames.cpp525
-rw-r--r--src/network/access/http2/http2frames_p.h173
-rw-r--r--src/network/access/http2/http2protocol.cpp156
-rw-r--r--src/network/access/http2/http2protocol_p.h164
-rw-r--r--src/network/access/http2/http2streams.cpp100
-rw-r--r--src/network/access/http2/http2streams_p.h92
7 files changed, 1218 insertions, 2 deletions
diff --git a/src/network/access/http2/http2.pri b/src/network/access/http2/http2.pri
index 2157e35e2f..e9f30aeb4a 100644
--- a/src/network/access/http2/http2.pri
+++ b/src/network/access/http2/http2.pri
@@ -2,10 +2,16 @@ HEADERS += \
access/http2/bitstreams_p.h \
access/http2/huffman_p.h \
access/http2/hpack_p.h \
- access/http2/hpacktable_p.h
+ access/http2/hpacktable_p.h \
+ access/http2/http2frames_p.h \
+ access/http2/http2streams_p.h \
+ access/http2/http2protocol_p.h
SOURCES += \
access/http2/bitstreams.cpp \
access/http2/huffman.cpp \
access/http2/hpack.cpp \
- access/http2/hpacktable.cpp
+ access/http2/hpacktable.cpp \
+ access/http2/http2frames.cpp \
+ access/http2/http2streams.cpp \
+ access/http2/http2protocol.cpp
diff --git a/src/network/access/http2/http2frames.cpp b/src/network/access/http2/http2frames.cpp
new file mode 100644
index 0000000000..471fb2c7fb
--- /dev/null
+++ b/src/network/access/http2/http2frames.cpp
@@ -0,0 +1,525 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "http2frames_p.h"
+
+#include <QtNetwork/qabstractsocket.h>
+
+#include <algorithm>
+#include <utility>
+
+QT_BEGIN_NAMESPACE
+
+namespace Http2
+{
+
+// HTTP/2 frames are defined by RFC7540, clauses 4 and 6.
+
+FrameStatus validate_frame_header(FrameType type, FrameFlags flags, quint32 payloadSize)
+{
+ // 4.2 Frame Size
+ if (payloadSize > maxPayloadSize)
+ return FrameStatus::sizeError;
+
+ switch (type) {
+ case FrameType::SETTINGS:
+ // SETTINGS ACK can not have any payload.
+ // The payload of a SETTINGS frame consists of zero
+ // or more parameters, each consisting of an unsigned
+ // 16-bit setting identifier and an unsigned 32-bit value.
+ // Thus the payload size must be a multiple of 6.
+ if (flags.testFlag(FrameFlag::ACK) ? payloadSize : payloadSize % 6)
+ return FrameStatus::sizeError;
+ break;
+ case FrameType::PRIORITY:
+ // 6.3 PRIORITY
+ if (payloadSize != 5)
+ return FrameStatus::sizeError;
+ break;
+ case FrameType::PING:
+ case FrameType::GOAWAY:
+ // 6.7 PING, 6.8 GOAWAY
+ if (payloadSize != 8)
+ return FrameStatus::sizeError;
+ break;
+ case FrameType::RST_STREAM:
+ case FrameType::WINDOW_UPDATE:
+ // 6.4 RST_STREAM, 6.9 WINDOW_UPDATE
+ if (payloadSize != 4)
+ return FrameStatus::sizeError;
+ break;
+ case FrameType::PUSH_PROMISE:
+ // 6.6 PUSH_PROMISE
+ if (payloadSize < 4)
+ return FrameStatus::sizeError;
+ default:
+ // DATA/HEADERS/CONTINUATION will be verified
+ // when we have payload.
+ // Frames of unknown types are ignored (5.1)
+ break;
+ }
+
+ return FrameStatus::goodFrame;
+}
+
+FrameStatus validate_frame_payload(FrameType type, FrameFlags flags,
+ quint32 size, const uchar *src)
+{
+ Q_ASSERT(!size || src);
+
+ // Ignored, 5.1
+ if (type == FrameType::LAST_FRAME_TYPE)
+ return FrameStatus::goodFrame;
+
+ // 6.1 DATA, 6.2 HEADERS
+ if (type == FrameType::DATA || type == FrameType::HEADERS) {
+ if (flags.testFlag(FrameFlag::PADDED)) {
+ if (!size || size < src[0])
+ return FrameStatus::sizeError;
+ size -= src[0];
+ }
+ if (type == FrameType::HEADERS && flags.testFlag(FrameFlag::PRIORITY)) {
+ if (size < 5)
+ return FrameStatus::sizeError;
+ }
+ }
+
+ // 6.6 PUSH_PROMISE
+ if (type == FrameType::PUSH_PROMISE) {
+ if (flags.testFlag(FrameFlag::PADDED)) {
+ if (!size || size < src[0])
+ return FrameStatus::sizeError;
+ size -= src[0];
+ }
+
+ if (size < 4)
+ return FrameStatus::sizeError;
+ }
+
+ return FrameStatus::goodFrame;
+}
+
+FrameStatus validate_frame_payload(FrameType type, FrameFlags flags,
+ const std::vector<uchar> &payload)
+{
+ const uchar *src = payload.size() ? &payload[0] : nullptr;
+ return validate_frame_payload(type, flags, quint32(payload.size()), src);
+}
+
+
+FrameReader::FrameReader(FrameReader &&rhs)
+ : framePayload(std::move(rhs.framePayload))
+{
+ type = rhs.type;
+ rhs.type = FrameType::LAST_FRAME_TYPE;
+
+ flags = rhs.flags;
+ rhs.flags = FrameFlag::EMPTY;
+
+ streamID = rhs.streamID;
+ rhs.streamID = 0;
+
+ payloadSize = rhs.payloadSize;
+ rhs.payloadSize = 0;
+
+ incompleteRead = rhs.incompleteRead;
+ rhs.incompleteRead = false;
+
+ offset = rhs.offset;
+ rhs.offset = 0;
+}
+
+FrameReader &FrameReader::operator = (FrameReader &&rhs)
+{
+ framePayload = std::move(rhs.framePayload);
+
+ type = rhs.type;
+ rhs.type = FrameType::LAST_FRAME_TYPE;
+
+ flags = rhs.flags;
+ rhs.flags = FrameFlag::EMPTY;
+
+ streamID = rhs.streamID;
+ rhs.streamID = 0;
+
+ payloadSize = rhs.payloadSize;
+ rhs.payloadSize = 0;
+
+ incompleteRead = rhs.incompleteRead;
+ rhs.incompleteRead = false;
+
+ offset = rhs.offset;
+ rhs.offset = 0;
+
+ return *this;
+}
+
+FrameStatus FrameReader::read(QAbstractSocket &socket)
+{
+ if (!incompleteRead) {
+ if (!readHeader(socket))
+ return FrameStatus::incompleteFrame;
+
+ const auto status = validate_frame_header(type, flags, payloadSize);
+ if (status != FrameStatus::goodFrame) {
+ // No need to read any payload.
+ return status;
+ }
+
+ if (Http2PredefinedParameters::maxFrameSize < payloadSize)
+ return FrameStatus::sizeError;
+
+ framePayload.resize(payloadSize);
+ offset = 0;
+ }
+
+ if (framePayload.size()) {
+ if (!readPayload(socket))
+ return FrameStatus::incompleteFrame;
+ }
+
+ return validate_frame_payload(type, flags, framePayload);
+}
+
+bool FrameReader::padded(uchar *pad) const
+{
+ Q_ASSERT(pad);
+
+ if (!flags.testFlag(FrameFlag::PADDED))
+ return false;
+
+ if (type == FrameType::DATA
+ || type == FrameType::PUSH_PROMISE
+ || type == FrameType::HEADERS) {
+ Q_ASSERT(framePayload.size() >= 1);
+ *pad = framePayload[0];
+ return true;
+ }
+
+ return false;
+}
+
+bool FrameReader::priority(quint32 *streamID, uchar *weight) const
+{
+ Q_ASSERT(streamID);
+ Q_ASSERT(weight);
+
+ if (!framePayload.size())
+ return false;
+
+ const uchar *src = &framePayload[0];
+ if (type == FrameType::HEADERS && flags.testFlag(FrameFlag::PADDED))
+ ++src;
+
+ if ((type == FrameType::HEADERS && flags.testFlag(FrameFlag::PRIORITY))
+ || type == FrameType::PRIORITY) {
+ *streamID = qFromBigEndian<quint32>(src);
+ *weight = src[4];
+ return true;
+ }
+
+ return false;
+}
+
+quint32 FrameReader::dataSize() const
+{
+ quint32 size = quint32(framePayload.size());
+ uchar pad = 0;
+ if (padded(&pad)) {
+ // + 1 one for a byte with padding number itself:
+ size -= pad + 1;
+ }
+
+ quint32 dummyID = 0;
+ uchar dummyW = 0;
+ if (priority(&dummyID, &dummyW))
+ size -= 5;
+
+ return size;
+}
+
+const uchar *FrameReader::dataBegin() const
+{
+ if (!framePayload.size())
+ return nullptr;
+
+ const uchar *src = &framePayload[0];
+ uchar dummyPad = 0;
+ if (padded(&dummyPad))
+ ++src;
+
+ quint32 dummyID = 0;
+ uchar dummyW = 0;
+ if (priority(&dummyID, &dummyW))
+ src += 5;
+
+ return src;
+}
+
+bool FrameReader::readHeader(QAbstractSocket &socket)
+{
+ if (socket.bytesAvailable() < frameHeaderSize)
+ return false;
+
+ uchar src[frameHeaderSize] = {};
+ socket.read(reinterpret_cast<char*>(src), frameHeaderSize);
+
+ payloadSize = src[0] << 16 | src[1] << 8 | src[2];
+
+ type = FrameType(src[3]);
+ if (int(type) >= int(FrameType::LAST_FRAME_TYPE))
+ type = FrameType::LAST_FRAME_TYPE; // To be ignored, 5.1
+
+ flags = FrameFlags(src[4]);
+ streamID = qFromBigEndian<quint32>(src + 5);
+
+ return true;
+}
+
+bool FrameReader::readPayload(QAbstractSocket &socket)
+{
+ Q_ASSERT(offset <= framePayload.size());
+
+ // Casts and ugliness - to deal with MSVC. Values are guaranteed to fit into quint32.
+ if (const auto residue = std::min(qint64(framePayload.size() - offset), socket.bytesAvailable())) {
+ socket.read(reinterpret_cast<char *>(&framePayload[offset]), residue);
+ offset += quint32(residue);
+ }
+
+ incompleteRead = offset < framePayload.size();
+ return !incompleteRead;
+}
+
+
+FrameWriter::FrameWriter()
+{
+ frameBuffer.reserve(Http2PredefinedParameters::maxFrameSize +
+ Http2PredefinedParameters::frameHeaderSize);
+}
+
+FrameWriter::FrameWriter(FrameType type, FrameFlags flags, quint32 streamID)
+{
+ frameBuffer.reserve(Http2PredefinedParameters::maxFrameSize +
+ Http2PredefinedParameters::frameHeaderSize);
+ start(type, flags, streamID);
+}
+
+void FrameWriter::start(FrameType type, FrameFlags flags, quint32 streamID)
+{
+ frameBuffer.resize(frameHeaderSize);
+ // The first three bytes - payload size, which is 0 for now.
+ frameBuffer[0] = 0;
+ frameBuffer[1] = 0;
+ frameBuffer[2] = 0;
+
+ frameBuffer[3] = uchar(type);
+ frameBuffer[4] = uchar(flags);
+
+ qToBigEndian(streamID, &frameBuffer[5]);
+}
+
+void FrameWriter::setPayloadSize(quint32 size)
+{
+ Q_ASSERT(frameBuffer.size() >= frameHeaderSize);
+ Q_ASSERT(size < maxPayloadSize);
+
+ frameBuffer[0] = size >> 16;
+ frameBuffer[1] = size >> 8;
+ frameBuffer[2] = size;
+}
+
+quint32 FrameWriter::payloadSize() const
+{
+ Q_ASSERT(frameBuffer.size() >= frameHeaderSize);
+ return frameBuffer[0] << 16 | frameBuffer[1] << 8 | frameBuffer[2];
+}
+
+void FrameWriter::setType(FrameType type)
+{
+ Q_ASSERT(frameBuffer.size() >= frameHeaderSize);
+ frameBuffer[3] = uchar(type);
+}
+
+FrameType FrameWriter::type() const
+{
+ Q_ASSERT(frameBuffer.size() >= frameHeaderSize);
+ return FrameType(frameBuffer[3]);
+}
+
+void FrameWriter::setFlags(FrameFlags flags)
+{
+ Q_ASSERT(frameBuffer.size() >= frameHeaderSize);
+ frameBuffer[4] = uchar(flags);
+}
+
+void FrameWriter::addFlag(FrameFlag flag)
+{
+ setFlags(flags() | flag);
+}
+
+FrameFlags FrameWriter::flags() const
+{
+ Q_ASSERT(frameBuffer.size() >= frameHeaderSize);
+ return FrameFlags(frameBuffer[4]);
+}
+
+quint32 FrameWriter::streamID() const
+{
+ return qFromBigEndian<quint32>(&frameBuffer[5]);
+}
+
+void FrameWriter::append(uchar val)
+{
+ frameBuffer.push_back(val);
+ updatePayloadSize();
+}
+
+void FrameWriter::append(const uchar *begin, const uchar *end)
+{
+ Q_ASSERT(begin && end);
+ Q_ASSERT(begin < end);
+
+ frameBuffer.insert(frameBuffer.end(), begin, end);
+ updatePayloadSize();
+}
+
+void FrameWriter::updatePayloadSize()
+{
+ // First, compute size:
+ const quint32 payloadSize = quint32(frameBuffer.size() - frameHeaderSize);
+ Q_ASSERT(payloadSize <= maxPayloadSize);
+ setPayloadSize(payloadSize);
+}
+
+bool FrameWriter::write(QAbstractSocket &socket) const
+{
+ Q_ASSERT(frameBuffer.size() >= frameHeaderSize);
+ // Do some sanity check first:
+ Q_ASSERT(int(type()) < int(FrameType::LAST_FRAME_TYPE));
+ Q_ASSERT(validate_frame_header(type(), flags(), payloadSize()) == FrameStatus::goodFrame);
+
+ const auto nWritten = socket.write(reinterpret_cast<const char *>(&frameBuffer[0]),
+ frameBuffer.size());
+ return nWritten != -1 && size_type(nWritten) == frameBuffer.size();
+}
+
+bool FrameWriter::writeHEADERS(QAbstractSocket &socket, quint32 sizeLimit)
+{
+ Q_ASSERT(frameBuffer.size() >= frameHeaderSize);
+
+ if (sizeLimit > quint32(maxPayloadSize))
+ sizeLimit = quint32(maxPayloadSize);
+
+ if (quint32(frameBuffer.size() - frameHeaderSize) <= sizeLimit) {
+ updatePayloadSize();
+ return write(socket);
+ }
+
+ // Write a frame's header (not controlled by sizeLimit) and
+ // as many bytes of payload as we can within sizeLimit,
+ // then send CONTINUATION frames, as needed.
+ setPayloadSize(sizeLimit);
+ const quint32 firstChunkSize = frameHeaderSize + sizeLimit;
+ qint64 written = socket.write(reinterpret_cast<const char *>(&frameBuffer[0]),
+ firstChunkSize);
+
+ if (written != qint64(firstChunkSize))
+ return false;
+
+ FrameWriter continuationFrame(FrameType::CONTINUATION, FrameFlag::EMPTY, streamID());
+ quint32 offset = firstChunkSize;
+
+ while (offset != frameBuffer.size()) {
+ const auto chunkSize = std::min(sizeLimit, quint32(frameBuffer.size() - offset));
+ if (chunkSize + offset == frameBuffer.size())
+ continuationFrame.addFlag(FrameFlag::END_HEADERS);
+ continuationFrame.setPayloadSize(chunkSize);
+ if (!continuationFrame.write(socket))
+ return false;
+ written = socket.write(reinterpret_cast<const char *>(&frameBuffer[offset]),
+ chunkSize);
+ if (written != qint64(chunkSize))
+ return false;
+
+ offset += chunkSize;
+ }
+
+ return true;
+}
+
+bool FrameWriter::writeDATA(QAbstractSocket &socket, quint32 sizeLimit,
+ const uchar *src, quint32 size)
+{
+ // With DATA frame(s) we always have:
+ // 1) frame's header (9 bytes)
+ // 2) a separate payload (from QNonContiguousByteDevice).
+ // We either fit within a sizeLimit, or split into several
+ // DATA frames.
+
+ Q_ASSERT(src);
+
+ if (sizeLimit > quint32(maxPayloadSize))
+ sizeLimit = quint32(maxPayloadSize);
+ // We NEVER set END_STREAM, since QHttp2ProtocolHandler works with
+ // QNonContiguousByteDevice and this 'writeDATA' is probably
+ // not the last one for a given request.
+ // This has to be done externally (sending an empty DATA frame with END_STREAM).
+ for (quint32 offset = 0; offset != size;) {
+ const auto chunkSize = std::min(size - offset, sizeLimit);
+ setPayloadSize(chunkSize);
+ // Frame's header first:
+ if (!write(socket))
+ return false;
+ // Payload (if any):
+ if (chunkSize) {
+ const auto written = socket.write(reinterpret_cast<const char*>(src + offset),
+ chunkSize);
+ if (written != qint64(chunkSize))
+ return false;
+ }
+
+ offset += chunkSize;
+ }
+
+ return true;
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/http2/http2frames_p.h b/src/network/access/http2/http2frames_p.h
new file mode 100644
index 0000000000..860555c180
--- /dev/null
+++ b/src/network/access/http2/http2frames_p.h
@@ -0,0 +1,173 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef HTTP2FRAMES_P_H
+#define HTTP2FRAMES_P_H
+
+#include "http2protocol_p.h"
+#include "hpack_p.h"
+
+#include <QtCore/qendian.h>
+#include <QtCore/qglobal.h>
+
+#include <algorithm>
+#include <vector>
+
+QT_BEGIN_NAMESPACE
+
+class QHttp2ProtocolHandler;
+class QAbstractSocket;
+
+namespace Http2
+{
+
+class Q_AUTOTEST_EXPORT FrameReader
+{
+ friend class QT_PREPEND_NAMESPACE(QHttp2ProtocolHandler);
+
+public:
+ FrameReader() = default;
+
+ FrameReader(const FrameReader &) = default;
+ FrameReader(FrameReader &&rhs);
+
+ FrameReader &operator = (const FrameReader &) = default;
+ FrameReader &operator = (FrameReader &&rhs);
+
+ FrameStatus read(QAbstractSocket &socket);
+
+ bool padded(uchar *pad) const;
+ bool priority(quint32 *streamID, uchar *weight) const;
+
+ // N of bytes without padding and/or priority
+ quint32 dataSize() const;
+ // Beginning of payload without priority/padding
+ // bytes.
+ const uchar *dataBegin() const;
+
+ FrameType type = FrameType::LAST_FRAME_TYPE;
+ FrameFlags flags = FrameFlag::EMPTY;
+ quint32 streamID = 0;
+ quint32 payloadSize = 0;
+
+private:
+ bool readHeader(QAbstractSocket &socket);
+ bool readPayload(QAbstractSocket &socket);
+
+ // As soon as we got a header, we
+ // know payload size, offset is
+ // needed if we do not have enough
+ // data and will read the next chunk.
+ bool incompleteRead = false;
+ quint32 offset = 0;
+ std::vector<uchar> framePayload;
+};
+
+class Q_AUTOTEST_EXPORT FrameWriter
+{
+ friend class QT_PREPEND_NAMESPACE(QHttp2ProtocolHandler);
+
+public:
+ using payload_type = std::vector<uchar>;
+ using size_type = payload_type::size_type;
+
+ FrameWriter();
+ FrameWriter(FrameType type, FrameFlags flags, quint32 streamID);
+
+ void start(FrameType type, FrameFlags flags, quint32 streamID);
+
+ void setPayloadSize(quint32 size);
+ quint32 payloadSize() const;
+
+ void setType(FrameType type);
+ FrameType type() const;
+
+ void setFlags(FrameFlags flags);
+ void addFlag(FrameFlag flag);
+ FrameFlags flags() const;
+
+ quint32 streamID() const;
+
+ // All append functions also update frame's payload
+ // length.
+ template<typename ValueType>
+ void append(ValueType val)
+ {
+ uchar wired[sizeof val] = {};
+ qToBigEndian(val, wired);
+ append(wired, wired + sizeof val);
+ }
+ void append(uchar val);
+ void append(Settings identifier)
+ {
+ append(quint16(identifier));
+ }
+ void append(const payload_type &payload)
+ {
+ append(&payload[0], &payload[0] + payload.size());
+ }
+
+ void append(const uchar *begin, const uchar *end);
+
+ // Write 'frameBuffer' as a single frame:
+ bool write(QAbstractSocket &socket) const;
+ // Write as a single frame if we can, or write headers and
+ // CONTINUATION(s) frame(s).
+ bool writeHEADERS(QAbstractSocket &socket, quint32 sizeLimit);
+ // Write either a single DATA frame or several DATA frames
+ // depending on 'sizeLimit'. Data itself is 'external' to
+ // FrameWriter, since it's a 'readPointer' from QNonContiguousData.
+ bool writeDATA(QAbstractSocket &socket, quint32 sizeLimit,
+ const uchar *src, quint32 size);
+
+ std::vector<uchar> &rawFrameBuffer()
+ {
+ return frameBuffer;
+ }
+
+private:
+ void updatePayloadSize();
+ std::vector<uchar> frameBuffer;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/http2/http2protocol.cpp b/src/network/access/http2/http2protocol.cpp
new file mode 100644
index 0000000000..7f788a6f42
--- /dev/null
+++ b/src/network/access/http2/http2protocol.cpp
@@ -0,0 +1,156 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qstring.h>
+
+#include "http2protocol_p.h"
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(QT_HTTP2, "qt.network.http2")
+
+namespace Http2
+{
+
+// 3.5 HTTP/2 Connection Preface:
+// "That is, the connection preface starts with the string
+// PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n)."
+const char Http2clientPreface[clientPrefaceLength] =
+ {0x50, 0x52, 0x49, 0x20, 0x2a, 0x20,
+ 0x48, 0x54, 0x54, 0x50, 0x2f, 0x32,
+ 0x2e, 0x30, 0x0d, 0x0a, 0x0d, 0x0a,
+ 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a};
+
+
+void qt_error(quint32 errorCode, QNetworkReply::NetworkError &error,
+ QString &errorMessage)
+{
+ if (errorCode > quint32(HTTP_1_1_REQUIRED)) {
+ error = QNetworkReply::ProtocolFailure;
+ errorMessage = QLatin1String("RST_STREAM with unknown error code (%1)");
+ errorMessage = errorMessage.arg(errorCode);
+ return;
+ }
+
+ const Http2Error http2Error = Http2Error(errorCode);
+
+ switch (http2Error) {
+ case HTTP2_NO_ERROR:
+ error = QNetworkReply::NoError;
+ errorMessage.clear();
+ break;
+ case PROTOCOL_ERROR:
+ error = QNetworkReply::ProtocolFailure;
+ errorMessage = QLatin1String("HTTP/2 protocol error");
+ break;
+ case INTERNAL_ERROR:
+ error = QNetworkReply::InternalServerError;
+ errorMessage = QLatin1String("Internal server error");
+ break;
+ case FLOW_CONTROL_ERROR:
+ error = QNetworkReply::ProtocolFailure;
+ errorMessage = QLatin1String("Flow control error");
+ break;
+ case SETTINGS_TIMEOUT:
+ error = QNetworkReply::TimeoutError;
+ errorMessage = QLatin1String("SETTINGS ACK timeout error");
+ break;
+ case STREAM_CLOSED:
+ error = QNetworkReply::ProtocolFailure;
+ errorMessage = QLatin1String("Server received frame(s) on a half-closed stream");
+ break;
+ case FRAME_SIZE_ERROR:
+ error = QNetworkReply::ProtocolFailure;
+ errorMessage = QLatin1String("Server received a frame with an invalid size");
+ break;
+ case REFUSE_STREAM:
+ error = QNetworkReply::ProtocolFailure;
+ errorMessage = QLatin1String("Server refused a stream");
+ break;
+ case CANCEL:
+ error = QNetworkReply::ProtocolFailure;
+ errorMessage = QLatin1String("Stream is no longer needed");
+ break;
+ case COMPRESSION_ERROR:
+ error = QNetworkReply::ProtocolFailure;
+ errorMessage = QLatin1String("Server is unable to maintain the "
+ "header compression context for the connection");
+ break;
+ case CONNECT_ERROR:
+ // TODO: in Qt6 we'll have to add more error codes in QNetworkReply.
+ error = QNetworkReply::UnknownNetworkError;
+ errorMessage = QLatin1String("The connection established in response "
+ "to a CONNECT request was reset or abnormally closed");
+ break;
+ case ENHANCE_YOUR_CALM:
+ error = QNetworkReply::UnknownServerError;
+ errorMessage = QLatin1String("Server dislikes our behavior, excessive load detected.");
+ break;
+ case INADEQUATE_SECURITY:
+ error = QNetworkReply::ContentAccessDenied;
+ errorMessage = QLatin1String("The underlying transport has properties "
+ "that do not meet minimum security "
+ "requirements");
+ break;
+ case HTTP_1_1_REQUIRED:
+ error = QNetworkReply::ProtocolFailure;
+ errorMessage = QLatin1String("Server requires that HTTP/1.1 "
+ "be used instead of HTTP/2.");
+ }
+}
+
+QString qt_error_string(quint32 errorCode)
+{
+ QNetworkReply::NetworkError error = QNetworkReply::NoError;
+ QString message;
+ qt_error(errorCode, error, message);
+ return message;
+}
+
+QNetworkReply::NetworkError qt_error(quint32 errorCode)
+{
+ QNetworkReply::NetworkError error = QNetworkReply::NoError;
+ QString message;
+ qt_error(errorCode, error, message);
+ return error;
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/http2/http2protocol_p.h b/src/network/access/http2/http2protocol_p.h
new file mode 100644
index 0000000000..c7088e2179
--- /dev/null
+++ b/src/network/access/http2/http2protocol_p.h
@@ -0,0 +1,164 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef HTTP2PROTOCOL_P_H
+#define HTTP2PROTOCOL_P_H
+
+#include <QtNetwork/qnetworkreply.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qglobal.h>
+
+// Different HTTP/2 constants/values as defined by RFC 7540.
+
+QT_BEGIN_NAMESPACE
+
+class QString;
+
+namespace Http2
+{
+
+enum class Settings : quint16
+{
+ HEADER_TABLE_SIZE_ID = 0x1,
+ ENABLE_PUSH_ID = 0x2,
+ MAX_CONCURRENT_STREAMS_ID = 0x3,
+ INITIAL_WINDOW_SIZE_ID = 0x4,
+ MAX_FRAME_SIZE_ID = 0x5,
+ MAX_HEADER_LIST_SIZE_ID = 0x6
+};
+
+enum class FrameType : uchar
+{
+ DATA = 0x0,
+ HEADERS = 0x1,
+ PRIORITY = 0x2,
+ RST_STREAM = 0x3,
+ SETTINGS = 0x4,
+ PUSH_PROMISE = 0x5,
+ PING = 0x6,
+ GOAWAY = 0x7,
+ WINDOW_UPDATE = 0x8,
+ CONTINUATION = 0x9,
+ // ATTENTION: enumerators must be sorted.
+ // We use LAST_FRAME_TYPE to check if
+ // frame type is known, if not - this frame
+ // must be ignored, HTTP/2 5.1).
+ LAST_FRAME_TYPE
+};
+
+enum class FrameFlag : uchar
+{
+ EMPTY = 0x0, // Valid for any frame type.
+ ACK = 0x1, // Valid for PING, SETTINGS
+ END_STREAM = 0x1, // Valid for HEADERS, DATA
+ END_HEADERS = 0x4, // Valid for PUSH_PROMISE, HEADERS,
+ PADDED = 0x8, // Valid for PUSH_PROMISE, HEADERS, DATA
+ PRIORITY = 0x20 // Valid for HEADERS,
+};
+
+Q_DECLARE_FLAGS(FrameFlags, FrameFlag)
+Q_DECLARE_OPERATORS_FOR_FLAGS(FrameFlags)
+
+enum Http2PredefinedParameters
+{
+ // Old-style enum, so we
+ // can use as Http2::frameHeaderSize for example.
+ clientPrefaceLength = 24, // HTTP/2, 3.5
+ connectionStreamID = 0, // HTTP/2, 5.1.1
+ frameHeaderSize = 9, // HTTP/2, 4.1
+
+ // It's our max frame size we send in SETTINGS frame,
+ // it's also the default one and we also use it to later
+ // validate incoming frames:
+ maxFrameSize = 16384, // HTTP/2 6.5.2
+
+ defaultSessionWindowSize = 65535, // HTTP/2 6.5.2
+ maxPayloadSize = (1 << 24) - 1, // HTTP/2 6.5.2
+ // Using 1000 (rather arbitrarily), just to
+ // impose *some* upper limit:
+ maxPeerConcurrentStreams = 1000,
+ maxConcurrentStreams = 100 // HTTP/2, 6.5.2
+};
+
+extern const Q_AUTOTEST_EXPORT char Http2clientPreface[clientPrefaceLength];
+
+enum class FrameStatus
+{
+ protocolError,
+ sizeError,
+ incompleteFrame,
+ goodFrame
+};
+
+enum Http2Error
+{
+ // Old-style enum to avoid excessive name
+ // qualification ...
+ // NB:
+ // I use the last enumerator to check
+ // that errorCode (quint32) is valid,
+ // so it needs to be the highest-numbered!
+ // HTTP/2 7:
+ HTTP2_NO_ERROR = 0x0,
+ PROTOCOL_ERROR = 0x1,
+ INTERNAL_ERROR = 0x2,
+ FLOW_CONTROL_ERROR = 0x3,
+ SETTINGS_TIMEOUT = 0x4,
+ STREAM_CLOSED = 0x5,
+ FRAME_SIZE_ERROR = 0x6,
+ REFUSE_STREAM = 0x7,
+ CANCEL = 0x8,
+ COMPRESSION_ERROR = 0x9,
+ CONNECT_ERROR = 0xa,
+ ENHANCE_YOUR_CALM = 0xb,
+ INADEQUATE_SECURITY = 0xc,
+ HTTP_1_1_REQUIRED = 0xd
+};
+
+void qt_error(quint32 errorCode, QNetworkReply::NetworkError &error, QString &errorString);
+QString qt_error_string(quint32 errorCode);
+QNetworkReply::NetworkError qt_error(quint32 errorCode);
+
+}
+
+Q_DECLARE_LOGGING_CATEGORY(QT_HTTP2)
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/http2/http2streams.cpp b/src/network/access/http2/http2streams.cpp
new file mode 100644
index 0000000000..f57f8d8367
--- /dev/null
+++ b/src/network/access/http2/http2streams.cpp
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "http2streams_p.h"
+
+#include "private/qhttp2protocolhandler_p.h"
+#include "private/qhttpnetworkreply_p.h"
+
+#include <QtCore/qdebug.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace Http2
+{
+
+Stream::Stream(const HttpMessagePair &message, quint32 id, qint32 sendSize, qint32 recvSize)
+ : httpPair(message),
+ streamID(id),
+ sendWindow(sendSize),
+ recvWindow(recvSize)
+{
+}
+
+QHttpNetworkReply *Stream::reply() const
+{
+ return httpPair.second;
+}
+
+const QHttpNetworkRequest &Stream::request() const
+{
+ return httpPair.first;
+}
+
+QHttpNetworkRequest &Stream::request()
+{
+ return httpPair.first;
+}
+
+QHttpNetworkRequest::Priority Stream::priority() const
+{
+ return httpPair.first.priority();
+}
+
+uchar Stream::weight() const
+{
+ switch (priority()) {
+ case QHttpNetworkRequest::LowPriority:
+ return 0;
+ case QHttpNetworkRequest::NormalPriority:
+ return 127;
+ case QHttpNetworkRequest::HighPriority:
+ default:
+ return 255;
+ }
+}
+
+QNonContiguousByteDevice *Stream::data() const
+{
+ return httpPair.first.uploadByteDevice();
+}
+
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/http2/http2streams_p.h b/src/network/access/http2/http2streams_p.h
new file mode 100644
index 0000000000..7db5d21694
--- /dev/null
+++ b/src/network/access/http2/http2streams_p.h
@@ -0,0 +1,92 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef HTTP2STREAMS_P_H
+#define HTTP2STREAMS_P_H
+
+#include <private/qhttpnetworkconnectionchannel_p.h>
+#include <private/qhttpnetworkrequest_p.h>
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class QNonContiguousByteDevice;
+
+namespace Http2
+{
+
+struct Q_AUTOTEST_EXPORT Stream
+{
+ enum StreamState {
+ idle,
+ open,
+ halfClosedLocal,
+ halfClosedRemote,
+ closed
+ };
+
+ Stream() = default;
+ Stream(const HttpMessagePair &message, quint32 streamID, qint32 sendSize,
+ qint32 recvSize);
+
+ // TODO: check includes!!!
+ QHttpNetworkReply *reply() const;
+ const QHttpNetworkRequest &request() const;
+ QHttpNetworkRequest &request();
+ QHttpNetworkRequest::Priority priority() const;
+ uchar weight() const;
+
+ QNonContiguousByteDevice *data() const;
+
+ HttpMessagePair httpPair;
+ quint32 streamID = 0;
+ // Signed as window sizes can become negative:
+ qint32 sendWindow = 65535;
+ qint32 recvWindow = 65535;
+
+ StreamState state = idle;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
+