From 12d71f4ea20415ff2274e1e90f9e4d5a8b935d7f Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Thu, 14 Jul 2016 16:49:16 +0200 Subject: 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 --- src/network/access/http2/http2protocol.cpp | 47 ++++++++++++++++++++++++++++++ src/network/access/http2/http2protocol_p.h | 5 ++++ 2 files changed, 52 insertions(+) (limited to 'src/network/access/http2') diff --git a/src/network/access/http2/http2protocol.cpp b/src/network/access/http2/http2protocol.cpp index 7f788a6f42..9f05e926c9 100644 --- a/src/network/access/http2/http2protocol.cpp +++ b/src/network/access/http2/http2protocol.cpp @@ -37,9 +37,12 @@ ** ****************************************************************************/ +#include #include +#include "private/qhttpnetworkrequest_p.h" #include "http2protocol_p.h" +#include "http2frames_p.h" QT_BEGIN_NAMESPACE @@ -57,6 +60,37 @@ const char Http2clientPreface[clientPrefaceLength] = 0x2e, 0x30, 0x0d, 0x0a, 0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a}; +QByteArray qt_default_SETTINGS_to_Base64() +{ + FrameWriter frame(qt_default_SETTINGS_frame()); + // SETTINGS frame's payload consists of pairs: + // 2-byte-identifier | 4-byte-value - multiple of 6. + // Also it's allowed to be empty. + Q_ASSERT(!(frame.payloadSize() % 6)); + const char *src = reinterpret_cast(&frame.rawFrameBuffer()[frameHeaderSize]); + const QByteArray wrapper(QByteArray::fromRawData(src, int(frame.payloadSize()))); + // 3.2.1 + // The content of the HTTP2-Settings header field is the payload + // of a SETTINGS frame (Section 6.5), encoded as a base64url string + // (that is, the URL- and filename-safe Base64 encoding described in + // Section 5 of [RFC4648], with any trailing '=' characters omitted). + return wrapper.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); +} + +void qt_add_ProtocolUpgradeRequest(QHttpNetworkRequest &request) +{ + // RFC 2616, 14.10 + QByteArray value(request.headerField("Connection")); + if (value.size()) + value += ", "; + + value += "Upgrade, HTTP2-Settings"; + request.setHeaderField("Connection", value); + // This we just (re)write. + request.setHeaderField("Upgrade", "h2c"); + // This we just (re)write. + request.setHeaderField("HTTP2-Settings", qt_default_SETTINGS_to_Base64()); +} void qt_error(quint32 errorCode, QNetworkReply::NetworkError &error, QString &errorMessage) @@ -151,6 +185,19 @@ QNetworkReply::NetworkError qt_error(quint32 errorCode) return error; } +FrameWriter qt_default_SETTINGS_frame() +{ + // 6.5 SETTINGS + FrameWriter frame(FrameType::SETTINGS, FrameFlag::EMPTY, connectionStreamID); + // MAX frame size (16 kb), disable PUSH + frame.append(Settings::MAX_FRAME_SIZE_ID); + frame.append(quint32(maxFrameSize)); + frame.append(Settings::ENABLE_PUSH_ID); + frame.append(quint32(0)); + + return frame; +} + } QT_END_NAMESPACE diff --git a/src/network/access/http2/http2protocol_p.h b/src/network/access/http2/http2protocol_p.h index 5c46949e23..e49e9f1218 100644 --- a/src/network/access/http2/http2protocol_p.h +++ b/src/network/access/http2/http2protocol_p.h @@ -59,6 +59,8 @@ QT_BEGIN_NAMESPACE +class QHttpNetworkRequest; +class QByteArray; class QString; namespace Http2 @@ -128,6 +130,7 @@ enum Http2PredefinedParameters }; extern const Q_AUTOTEST_EXPORT char Http2clientPreface[clientPrefaceLength]; +void qt_add_ProtocolUpgradeRequest(QHttpNetworkRequest &request); enum class FrameStatus { @@ -166,6 +169,8 @@ void qt_error(quint32 errorCode, QNetworkReply::NetworkError &error, QString &er QString qt_error_string(quint32 errorCode); QNetworkReply::NetworkError qt_error(quint32 errorCode); +class FrameWriter qt_default_SETTINGS_frame(); + } Q_DECLARE_LOGGING_CATEGORY(QT_HTTP2) -- cgit v1.2.3