summaryrefslogtreecommitdiffstats
path: root/src/network/access/qhttp2protocolhandler.cpp
diff options
context:
space:
mode:
authorTimur Pocheptsov <timur.pocheptsov@theqtcompany.com>2016-07-14 16:49:16 +0200
committerTimur Pocheptsov <timur.pocheptsov@theqtcompany.com>2016-08-03 15:31:20 +0000
commit12d71f4ea20415ff2274e1e90f9e4d5a8b935d7f (patch)
tree8ba8d02c73305bb533267bbe3784fc6e607716ad /src/network/access/qhttp2protocolhandler.cpp
parentd7132c6c6d3bb9d1362744f36b2995b1d2daf4fe (diff)
Implement protocol upgrade for HTTP/2 enabled requests
Without ALPN/NPN (== without OpenSSL) we can negotiate HTTP/2 using the protocol upgrade procedure as described by RFC7540. Since there is no TLS handshake, after our http channel was connected we first send an 'augmented' HTTP/1.1 request - its header contains additional 'HTTP2-Settings' and 'Upgrade' (to 'h2c') fields. If we receive reponse 101 (switch protocol) we re-create a protocol handler and switch to HTTP/2. Task-number: QTBUG-50955 Change-Id: I36e9985e06ba76edaf7fdb22bb43770f8d593c61 Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Diffstat (limited to 'src/network/access/qhttp2protocolhandler.cpp')
-rw-r--r--src/network/access/qhttp2protocolhandler.cpp51
1 files changed, 36 insertions, 15 deletions
diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp
index 937686920c..2cf44521eb 100644
--- a/src/network/access/qhttp2protocolhandler.cpp
+++ b/src/network/access/qhttp2protocolhandler.cpp
@@ -40,7 +40,7 @@
#include "qhttpnetworkconnection_p.h"
#include "qhttp2protocolhandler_p.h"
-#if !defined(QT_NO_HTTP) && !defined(QT_NO_SSL)
+#if !defined(QT_NO_HTTP)
#include "http2/bitstreams_p.h"
@@ -54,6 +54,7 @@
#include <QtCore/qurl.h>
#include <algorithm>
+#include <utility>
#include <vector>
QT_BEGIN_NAMESPACE
@@ -133,6 +134,28 @@ QHttp2ProtocolHandler::QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *chan
continuedFrames.reserve(20);
}
+QHttp2ProtocolHandler::QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *channel,
+ const HttpMessagePair &message)
+ : QAbstractProtocolHandler(channel),
+ prefaceSent(false),
+ waitingForSettingsACK(false),
+ decoder(HPack::FieldLookupTable::DefaultSize),
+ encoder(HPack::FieldLookupTable::DefaultSize, true)
+{
+ // That's a protocol upgrade scenario - 3.2.
+ //
+ // We still have to send settings and the preface
+ // (though SETTINGS was a part of the first HTTP/1.1
+ // request "HTTP2-Settings" field).
+ //
+ // We pass 'false' for upload data, this was done by HTTP/1.1 protocol
+ // handler for us while sending the first request.
+ const quint32 initialStreamID = createNewStream(message, false);
+ Q_ASSERT(initialStreamID == 1);
+ Stream &stream = activeStreams[initialStreamID];
+ stream.state = Stream::halfClosedLocal;
+}
+
void QHttp2ProtocolHandler::_q_uploadDataReadyRead()
{
auto data = qobject_cast<QNonContiguousByteDevice *>(sender());
@@ -247,7 +270,7 @@ bool QHttp2ProtocolHandler::sendRequest()
auto it = requests.begin();
m_channel->state = QHttpNetworkConnectionChannel::WritingState;
for (quint32 i = 0; i < streamsToUse; ++i) {
- const qint32 newStreamID = createNewStream(*it);
+ const qint32 newStreamID = createNewStream(*it, true /* upload data */);
if (!newStreamID) {
// TODO: actually we have to open a new connection.
qCCritical(QT_HTTP2, "sendRequest: out of stream IDs");
@@ -278,7 +301,6 @@ bool QHttp2ProtocolHandler::sendRequest()
return true;
}
-
bool QHttp2ProtocolHandler::sendClientPreface()
{
// 3.5 HTTP/2 Connection Preface
@@ -293,12 +315,8 @@ bool QHttp2ProtocolHandler::sendClientPreface()
return false;
// 6.5 SETTINGS
- outboundFrame.start(FrameType::SETTINGS, FrameFlag::EMPTY, Http2::connectionStreamID);
- // MAX frame size (16 kb), disable PUSH
- outboundFrame.append(Settings::MAX_FRAME_SIZE_ID);
- outboundFrame.append(quint32(Http2::maxFrameSize));
- outboundFrame.append(Settings::ENABLE_PUSH_ID);
- outboundFrame.append(quint32(0));
+ outboundFrame = Http2::qt_default_SETTINGS_frame();
+ Q_ASSERT(outboundFrame.payloadSize());
if (!outboundFrame.write(*m_socket))
return false;
@@ -1022,7 +1040,8 @@ void QHttp2ProtocolHandler::finishStreamWithError(Stream &stream, QNetworkReply:
emit httpReply->finishedWithError(error, message);
}
-quint32 QHttp2ProtocolHandler::createNewStream(const HttpMessagePair &message)
+quint32 QHttp2ProtocolHandler::createNewStream(const HttpMessagePair &message,
+ bool uploadData)
{
const qint32 newStreamID = allocateStreamID();
if (!newStreamID)
@@ -1043,10 +1062,12 @@ quint32 QHttp2ProtocolHandler::createNewStream(const HttpMessagePair &message)
streamInitialSendWindowSize,
streamInitialRecvWindowSize);
- if (auto src = newStream.data()) {
- connect(src, SIGNAL(readyRead()), this,
- SLOT(_q_uploadDataReadyRead()), Qt::QueuedConnection);
- src->setProperty("HTTP2StreamID", newStreamID);
+ if (uploadData) {
+ if (auto src = newStream.data()) {
+ connect(src, SIGNAL(readyRead()), this,
+ SLOT(_q_uploadDataReadyRead()), Qt::QueuedConnection);
+ src->setProperty("HTTP2StreamID", newStreamID);
+ }
}
activeStreams.insert(newStreamID, newStream);
@@ -1214,4 +1235,4 @@ void QHttp2ProtocolHandler::closeSession()
QT_END_NAMESPACE
-#endif // !defined(QT_NO_HTTP) && !defined(QT_NO_SSL)
+#endif // !defined(QT_NO_HTTP)