HTTP2 protocol handler
Add HTTP2 support in QNAM - QHttp2ProtocolHandler + aux. classes. [ChangeLog][QtNetwork] Added support for HTTP/2 protocol Task-number: QTBUG-50956 Change-Id: I3a119cfbb1fb3118d9003225f5e54b46ae2829b6 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>bb10
parent
6f61cfc338
commit
b2c0f9713c
|
|
@ -38,7 +38,8 @@ HEADERS += \
|
|||
access/qhttpthreaddelegate_p.h \
|
||||
access/qhttpmultipart.h \
|
||||
access/qhttpmultipart_p.h \
|
||||
access/qnetworkfile_p.h
|
||||
access/qnetworkfile_p.h \
|
||||
access/qhttp2protocolhandler_p.h
|
||||
|
||||
SOURCES += \
|
||||
access/qftp.cpp \
|
||||
|
|
@ -70,7 +71,8 @@ SOURCES += \
|
|||
access/qnetworkdiskcache.cpp \
|
||||
access/qhttpthreaddelegate.cpp \
|
||||
access/qhttpmultipart.cpp \
|
||||
access/qnetworkfile.cpp
|
||||
access/qnetworkfile.cpp \
|
||||
access/qhttp2protocolhandler.cpp
|
||||
|
||||
mac: LIBS_PRIVATE += -framework Security
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,196 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the QtNetwork module of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL3 included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 3 requirements
|
||||
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 2.0 or (at your option) the GNU General
|
||||
** Public license version 3 or any later version approved by the KDE Free
|
||||
** Qt Foundation. The licenses are as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
||||
** https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QHTTP2PROTOCOLHANDLER_P_H
|
||||
#define QHTTP2PROTOCOLHANDLER_P_H
|
||||
|
||||
#include <private/qhttpnetworkconnectionchannel_p.h>
|
||||
#include <private/qabstractprotocolhandler_p.h>
|
||||
#include <private/qhttpnetworkrequest_p.h>
|
||||
|
||||
#if !defined(QT_NO_HTTP) && !defined(QT_NO_SSL)
|
||||
|
||||
#include "http2/http2protocol_p.h"
|
||||
#include "http2/http2streams_p.h"
|
||||
#include "http2/http2frames_p.h"
|
||||
#include "http2/hpacktable_p.h"
|
||||
#include "http2/hpack_p.h"
|
||||
|
||||
#include <QtCore/qbytearray.h>
|
||||
#include <QtCore/qglobal.h>
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtCore/qflags.h>
|
||||
#include <QtCore/qhash.h>
|
||||
|
||||
#include <vector>
|
||||
#include <limits>
|
||||
#include <deque>
|
||||
#include <set>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QHttp2ProtocolHandler : public QObject, public QAbstractProtocolHandler
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *channel);
|
||||
|
||||
QHttp2ProtocolHandler(const QHttp2ProtocolHandler &rhs) = delete;
|
||||
QHttp2ProtocolHandler(QHttp2ProtocolHandler &&rhs) = delete;
|
||||
|
||||
QHttp2ProtocolHandler &operator = (const QHttp2ProtocolHandler &rhs) = delete;
|
||||
QHttp2ProtocolHandler &operator = (QHttp2ProtocolHandler &&rhs) = delete;
|
||||
|
||||
private slots:
|
||||
void _q_uploadDataReadyRead();
|
||||
void _q_replyDestroyed(QObject* reply);
|
||||
|
||||
private:
|
||||
using Stream = Http2::Stream;
|
||||
|
||||
void _q_readyRead() override;
|
||||
void _q_receiveReply() override;
|
||||
Q_INVOKABLE bool sendRequest() override;
|
||||
|
||||
bool sendClientPreface();
|
||||
bool sendSETTINGS_ACK();
|
||||
bool sendHEADERS(Stream &stream);
|
||||
bool sendDATA(Stream &stream);
|
||||
Q_INVOKABLE bool sendWINDOW_UPDATE(quint32 streamID, quint32 delta);
|
||||
bool sendRST_STREAM(quint32 streamID, quint32 errorCoder);
|
||||
bool sendGOAWAY(quint32 errorCode);
|
||||
|
||||
void handleDATA();
|
||||
void handleHEADERS();
|
||||
void handlePRIORITY();
|
||||
void handleRST_STREAM();
|
||||
void handleSETTINGS();
|
||||
void handlePUSH_PROMISE();
|
||||
void handlePING();
|
||||
void handleGOAWAY();
|
||||
void handleWINDOW_UPDATE();
|
||||
void handleCONTINUATION();
|
||||
|
||||
void handleContinuedHEADERS();
|
||||
|
||||
bool acceptSetting(Http2::Settings identifier, quint32 newValue);
|
||||
|
||||
void updateStream(Stream &stream, const HPack::HttpHeader &headers);
|
||||
void updateStream(Stream &stream, const Http2::FrameReader &dataFrame);
|
||||
void finishStream(Stream &stream);
|
||||
// Error code send by a peer (GOAWAY/RST_STREAM):
|
||||
void finishStreamWithError(Stream &stream, quint32 errorCode);
|
||||
// Locally encountered error:
|
||||
void finishStreamWithError(Stream &stream, QNetworkReply::NetworkError error,
|
||||
const QString &message);
|
||||
|
||||
// Stream's lifecycle management:
|
||||
quint32 createNewStream(const HttpMessagePair &message);
|
||||
void addToSuspended(Stream &stream);
|
||||
void markAsReset(quint32 streamID);
|
||||
quint32 popStreamToResume();
|
||||
void removeFromSuspended(quint32 streamID);
|
||||
void deleteActiveStream(quint32 streamID);
|
||||
bool streamWasReset(quint32 streamID) const;
|
||||
|
||||
bool prefaceSent = false;
|
||||
// In the current implementation we send
|
||||
// SETTINGS only once, immediately after
|
||||
// the client's preface 24-byte message.
|
||||
bool waitingForSettingsACK = false;
|
||||
|
||||
static const quint32 maxAcceptableTableSize = 16 * HPack::FieldLookupTable::DefaultSize;
|
||||
// HTTP/2 4.3: Header compression is stateful. One compression context and
|
||||
// one decompression context are used for the entire connection.
|
||||
HPack::Decoder decoder;
|
||||
HPack::Encoder encoder;
|
||||
|
||||
QHash<quint32, Stream> activeStreams;
|
||||
std::deque<quint32> suspendedStreams[3]; // 3 for priorities: High, Normal, Low.
|
||||
static const std::deque<quint32>::size_type maxRecycledStreams;
|
||||
std::deque<quint32> recycledStreams;
|
||||
|
||||
// Peer's max frame size.
|
||||
quint32 maxFrameSize = Http2::maxFrameSize;
|
||||
|
||||
Http2::FrameReader inboundFrame;
|
||||
Http2::FrameWriter outboundFrame;
|
||||
// Temporary storage to assemble HEADERS' block
|
||||
// from several CONTINUATION frames ...
|
||||
bool continuationExpected = false;
|
||||
std::vector<Http2::FrameReader> continuedFrames;
|
||||
|
||||
// Peer's max number of streams ...
|
||||
quint32 maxConcurrentStreams = Http2::maxConcurrentStreams;
|
||||
|
||||
// Control flow:
|
||||
static const qint32 sessionMaxRecvWindowSize = Http2::defaultSessionWindowSize * 10;
|
||||
// Signed integer, it can become negative (it's still a valid window size):
|
||||
qint32 sessionRecvWindowSize = sessionMaxRecvWindowSize;
|
||||
|
||||
// We do not negotiate this window size
|
||||
// We have to send WINDOW_UPDATE frames to our peer also.
|
||||
static const qint32 streamInitialRecvWindowSize = Http2::defaultSessionWindowSize;
|
||||
|
||||
// Updated by SETTINGS and WINDOW_UPDATE.
|
||||
qint32 sessionSendWindowSize = Http2::defaultSessionWindowSize;
|
||||
qint32 streamInitialSendWindowSize = Http2::defaultSessionWindowSize;
|
||||
|
||||
// It's unlimited by default, but can be changed via SETTINGS.
|
||||
quint32 maxHeaderListSize = (std::numeric_limits<quint32>::max)();
|
||||
|
||||
Q_INVOKABLE void resumeSuspendedStreams();
|
||||
// Our stream IDs (all odd), the first valid will be 1.
|
||||
quint32 nextID = 1;
|
||||
quint32 allocateStreamID();
|
||||
bool validPeerStreamID() const;
|
||||
bool goingAway = false;
|
||||
|
||||
// Errors:
|
||||
void connectionError(Http2::Http2Error errorCode,
|
||||
const char *message);
|
||||
void closeSession();
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // !defined(QT_NO_HTTP) && !defined(QT_NO_SSL)
|
||||
|
||||
#endif
|
||||
|
|
@ -82,7 +82,8 @@ QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &host
|
|||
networkLayerState(Unknown),
|
||||
hostName(hostName), port(port), encrypt(encrypt), delayIpv4(true)
|
||||
#ifndef QT_NO_SSL
|
||||
, channelCount((type == QHttpNetworkConnection::ConnectionTypeSPDY) ? 1 : defaultHttpChannelCount)
|
||||
, channelCount((type == QHttpNetworkConnection::ConnectionTypeSPDY || type == QHttpNetworkConnection::ConnectionTypeHTTP2)
|
||||
? 1 : defaultHttpChannelCount)
|
||||
#else
|
||||
, channelCount(defaultHttpChannelCount)
|
||||
#endif // QT_NO_SSL
|
||||
|
|
@ -1011,7 +1012,8 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
|
|||
}
|
||||
break;
|
||||
}
|
||||
case QHttpNetworkConnection::ConnectionTypeSPDY: {
|
||||
case QHttpNetworkConnection::ConnectionTypeSPDY:
|
||||
case QHttpNetworkConnection::ConnectionTypeHTTP2: {
|
||||
#ifndef QT_NO_SSL
|
||||
if (channels[0].spdyRequestsToSend.isEmpty())
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -99,7 +99,8 @@ public:
|
|||
|
||||
enum ConnectionType {
|
||||
ConnectionTypeHTTP,
|
||||
ConnectionTypeSPDY
|
||||
ConnectionTypeSPDY,
|
||||
ConnectionTypeHTTP2
|
||||
};
|
||||
|
||||
#ifndef QT_NO_BEARERMANAGEMENT
|
||||
|
|
@ -161,6 +162,7 @@ private:
|
|||
friend class QHttpNetworkReply;
|
||||
friend class QHttpNetworkReplyPrivate;
|
||||
friend class QHttpNetworkConnectionChannel;
|
||||
friend class QHttp2ProtocolHandler;
|
||||
friend class QHttpProtocolHandler;
|
||||
friend class QSpdyProtocolHandler;
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@
|
|||
|
||||
#ifndef QT_NO_HTTP
|
||||
|
||||
#include <private/qhttp2protocolhandler_p.h>
|
||||
#include <private/qhttpprotocolhandler_p.h>
|
||||
#include <private/qspdyprotocolhandler_p.h>
|
||||
|
||||
|
|
@ -971,7 +972,8 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
|
|||
} while (!connection->d_func()->highPriorityQueue.isEmpty()
|
||||
|| !connection->d_func()->lowPriorityQueue.isEmpty());
|
||||
#ifndef QT_NO_SSL
|
||||
if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY) {
|
||||
if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY ||
|
||||
connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2) {
|
||||
QList<HttpMessagePair> spdyPairs = spdyRequestsToSend.values();
|
||||
for (int a = 0; a < spdyPairs.count(); ++a) {
|
||||
// emit error for all replies
|
||||
|
|
@ -1003,7 +1005,8 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
|
|||
void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth)
|
||||
{
|
||||
#ifndef QT_NO_SSL
|
||||
if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY) {
|
||||
if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY ||
|
||||
connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2) {
|
||||
connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
|
||||
} else { // HTTP
|
||||
#endif // QT_NO_SSL
|
||||
|
|
@ -1043,6 +1046,10 @@ void QHttpNetworkConnectionChannel::_q_encrypted()
|
|||
// no need to re-queue requests, if SPDY was enabled on the request it
|
||||
// has gone to the SPDY queue already
|
||||
break;
|
||||
} else if (nextProtocol == QSslConfiguration::ALPNProtocolHTTP2) {
|
||||
protocolHandler.reset(new QHttp2ProtocolHandler(this));
|
||||
connection->setConnectionType(QHttpNetworkConnection::ConnectionTypeHTTP2);
|
||||
break;
|
||||
} else {
|
||||
emitFinishedWithError(QNetworkReply::SslHandshakeFailedError,
|
||||
"detected unknown Next Protocol Negotiation protocol");
|
||||
|
|
@ -1066,7 +1073,8 @@ void QHttpNetworkConnectionChannel::_q_encrypted()
|
|||
state = QHttpNetworkConnectionChannel::IdleState;
|
||||
pendingEncrypt = false;
|
||||
|
||||
if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY) {
|
||||
if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeSPDY ||
|
||||
connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2) {
|
||||
// we call setSpdyWasUsed(true) on the replies in the SPDY handler when the request is sent
|
||||
if (spdyRequestsToSend.count() > 0)
|
||||
// wait for data from the server first (e.g. initial window, max concurrent requests)
|
||||
|
|
|
|||
|
|
@ -180,6 +180,7 @@ private:
|
|||
friend class QHttpNetworkConnection;
|
||||
friend class QHttpNetworkConnectionPrivate;
|
||||
friend class QHttpNetworkConnectionChannel;
|
||||
friend class QHttp2ProtocolHandler;
|
||||
friend class QHttpProtocolHandler;
|
||||
friend class QSpdyProtocolHandler;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ QT_BEGIN_NAMESPACE
|
|||
QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(QHttpNetworkRequest::Operation op,
|
||||
QHttpNetworkRequest::Priority pri, const QUrl &newUrl)
|
||||
: QHttpNetworkHeaderPrivate(newUrl), operation(op), priority(pri), uploadByteDevice(0),
|
||||
autoDecompress(false), pipeliningAllowed(false), spdyAllowed(false),
|
||||
autoDecompress(false), pipeliningAllowed(false), spdyAllowed(false), http2Allowed(false),
|
||||
withCredentials(true), preConnect(false), followRedirect(false), redirectCount(0)
|
||||
{
|
||||
}
|
||||
|
|
@ -61,6 +61,7 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest
|
|||
autoDecompress(other.autoDecompress),
|
||||
pipeliningAllowed(other.pipeliningAllowed),
|
||||
spdyAllowed(other.spdyAllowed),
|
||||
http2Allowed(other.http2Allowed),
|
||||
withCredentials(other.withCredentials),
|
||||
ssl(other.ssl),
|
||||
preConnect(other.preConnect),
|
||||
|
|
@ -82,6 +83,7 @@ bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &ot
|
|||
&& (autoDecompress == other.autoDecompress)
|
||||
&& (pipeliningAllowed == other.pipeliningAllowed)
|
||||
&& (spdyAllowed == other.spdyAllowed)
|
||||
&& (http2Allowed == other.http2Allowed)
|
||||
// we do not clear the customVerb in setOperation
|
||||
&& (operation != QHttpNetworkRequest::Custom || (customVerb == other.customVerb))
|
||||
&& (withCredentials == other.withCredentials)
|
||||
|
|
@ -331,6 +333,16 @@ void QHttpNetworkRequest::setSPDYAllowed(bool b)
|
|||
d->spdyAllowed = b;
|
||||
}
|
||||
|
||||
bool QHttpNetworkRequest::isHTTP2Allowed() const
|
||||
{
|
||||
return d->http2Allowed;
|
||||
}
|
||||
|
||||
void QHttpNetworkRequest::setHTTP2Allowed(bool b)
|
||||
{
|
||||
d->http2Allowed = b;
|
||||
}
|
||||
|
||||
bool QHttpNetworkRequest::withCredentials() const
|
||||
{
|
||||
return d->withCredentials;
|
||||
|
|
|
|||
|
|
@ -117,6 +117,9 @@ public:
|
|||
bool isSPDYAllowed() const;
|
||||
void setSPDYAllowed(bool b);
|
||||
|
||||
bool isHTTP2Allowed() const;
|
||||
void setHTTP2Allowed(bool b);
|
||||
|
||||
bool withCredentials() const;
|
||||
void setWithCredentials(bool b);
|
||||
|
||||
|
|
@ -144,6 +147,7 @@ private:
|
|||
friend class QHttpNetworkConnectionPrivate;
|
||||
friend class QHttpNetworkConnectionChannel;
|
||||
friend class QHttpProtocolHandler;
|
||||
friend class QHttp2ProtocolHandler;
|
||||
friend class QSpdyProtocolHandler;
|
||||
};
|
||||
|
||||
|
|
@ -165,6 +169,7 @@ public:
|
|||
bool autoDecompress;
|
||||
bool pipeliningAllowed;
|
||||
bool spdyAllowed;
|
||||
bool http2Allowed;
|
||||
bool withCredentials;
|
||||
bool ssl;
|
||||
bool preConnect;
|
||||
|
|
|
|||
|
|
@ -287,7 +287,13 @@ void QHttpThreadDelegate::startRequest()
|
|||
QHttpNetworkConnection::ConnectionType connectionType
|
||||
= QHttpNetworkConnection::ConnectionTypeHTTP;
|
||||
#ifndef QT_NO_SSL
|
||||
if (httpRequest.isSPDYAllowed() && ssl) {
|
||||
if (httpRequest.isHTTP2Allowed() && ssl) {
|
||||
connectionType = QHttpNetworkConnection::ConnectionTypeHTTP2;
|
||||
QList<QByteArray> protocols;
|
||||
protocols << QSslConfiguration::ALPNProtocolHTTP2
|
||||
<< QSslConfiguration::NextProtocolHttp1_1;
|
||||
incomingSslConfiguration.setAllowedNextProtocols(protocols);
|
||||
} else if (httpRequest.isSPDYAllowed() && ssl) {
|
||||
connectionType = QHttpNetworkConnection::ConnectionTypeSPDY;
|
||||
urlCopy.setScheme(QStringLiteral("spdy")); // to differentiate SPDY requests from HTTPS requests
|
||||
QList<QByteArray> nextProtocols;
|
||||
|
|
|
|||
|
|
@ -738,6 +738,9 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
|
|||
if (request.attribute(QNetworkRequest::SpdyAllowedAttribute).toBool())
|
||||
httpRequest.setSPDYAllowed(true);
|
||||
|
||||
if (request.attribute(QNetworkRequest::HTTP2AllowedAttribute).toBool())
|
||||
httpRequest.setHTTP2Allowed(true);
|
||||
|
||||
if (static_cast<QNetworkRequest::LoadControl>
|
||||
(newHttpRequest.attribute(QNetworkRequest::AuthenticationReuseAttribute,
|
||||
QNetworkRequest::Automatic).toInt()) == QNetworkRequest::Manual)
|
||||
|
|
|
|||
|
|
@ -87,6 +87,8 @@ public:
|
|||
BackgroundRequestAttribute,
|
||||
SpdyAllowedAttribute,
|
||||
SpdyWasUsedAttribute,
|
||||
HTTP2AllowedAttribute,
|
||||
HTTP2WasUsedAttribute,
|
||||
EmitAllUploadProgressSignalsAttribute,
|
||||
FollowRedirectsAttribute,
|
||||
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ const QSsl::SslOptions QSslConfigurationPrivate::defaultSslOptions = QSsl::SslOp
|
|||
|QSsl::SslOptionDisableCompression
|
||||
|QSsl::SslOptionDisableSessionPersistence;
|
||||
|
||||
const char QSslConfiguration::ALPNProtocolHTTP2[] = "h2";
|
||||
const char QSslConfiguration::NextProtocolSpdy3_0[] = "spdy/3";
|
||||
const char QSslConfiguration::NextProtocolHttp1_1[] = "http/1.1";
|
||||
|
||||
|
|
|
|||
|
|
@ -168,6 +168,7 @@ public:
|
|||
QByteArray nextNegotiatedProtocol() const;
|
||||
NextProtocolNegotiationStatus nextProtocolNegotiationStatus() const;
|
||||
|
||||
static const char ALPNProtocolHTTP2[];
|
||||
static const char NextProtocolSpdy3_0[];
|
||||
static const char NextProtocolHttp1_1[];
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue