diff options
Diffstat (limited to 'src/network/access/http2')
-rw-r--r-- | src/network/access/http2/http2frames.cpp | 6 | ||||
-rw-r--r-- | src/network/access/http2/http2frames_p.h | 2 | ||||
-rw-r--r-- | src/network/access/http2/http2protocol.cpp | 75 | ||||
-rw-r--r-- | src/network/access/http2/http2protocol_p.h | 6 |
4 files changed, 87 insertions, 2 deletions
diff --git a/src/network/access/http2/http2frames.cpp b/src/network/access/http2/http2frames.cpp index 5a684c2f41..e695b4dd9e 100644 --- a/src/network/access/http2/http2frames.cpp +++ b/src/network/access/http2/http2frames.cpp @@ -361,6 +361,12 @@ FrameWriter::FrameWriter(FrameType type, FrameFlags flags, quint32 streamID) start(type, flags, streamID); } +void FrameWriter::setOutboundFrame(Frame &&newFrame) +{ + frame = std::move(newFrame); + updatePayloadSize(); +} + void FrameWriter::start(FrameType type, FrameFlags flags, quint32 streamID) { auto &buffer = frame.buffer; diff --git a/src/network/access/http2/http2frames_p.h b/src/network/access/http2/http2frames_p.h index e5f6d46c67..4bdc775806 100644 --- a/src/network/access/http2/http2frames_p.h +++ b/src/network/access/http2/http2frames_p.h @@ -129,6 +129,8 @@ public: return frame; } + void setOutboundFrame(Frame &&newFrame); + // Frame 'builders': void start(FrameType type, FrameFlags flags, quint32 streamID); void setPayloadSize(quint32 size); diff --git a/src/network/access/http2/http2protocol.cpp b/src/network/access/http2/http2protocol.cpp index 7f788a6f42..54811aeab0 100644 --- a/src/network/access/http2/http2protocol.cpp +++ b/src/network/access/http2/http2protocol.cpp @@ -37,9 +37,14 @@ ** ****************************************************************************/ -#include <QtCore/qstring.h> - #include "http2protocol_p.h" +#include "http2frames_p.h" + +#include "private/qhttpnetworkrequest_p.h" +#include "private/qhttpnetworkreply_p.h" + +#include <QtCore/qbytearray.h> +#include <QtCore/qstring.h> QT_BEGIN_NAMESPACE @@ -57,6 +62,38 @@ const char Http2clientPreface[clientPrefaceLength] = 0x2e, 0x30, 0x0d, 0x0a, 0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a}; +QByteArray default_SETTINGS_to_Base64() +{ + Frame frame(default_SETTINGS_frame()); + // SETTINGS frame's payload consists of pairs: + // 2-byte-identifier | 4-byte-value == multiple of 6. + Q_ASSERT(frame.payloadSize() && !(frame.payloadSize() % 6)); + const char *src = reinterpret_cast<const char *>(frame.dataBegin()); + const QByteArray wrapper(QByteArray::fromRawData(src, int(frame.dataSize()))); + // 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 prepare_for_protocol_upgrade(QHttpNetworkRequest &request) +{ + // RFC 2616, 14.10 + // RFC 7540, 3.2 + QByteArray value(request.headerField("Connection")); + // We _append_ 'Upgrade': + 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", default_SETTINGS_to_Base64()); +} void qt_error(quint32 errorCode, QNetworkReply::NetworkError &error, QString &errorMessage) @@ -151,6 +188,40 @@ QNetworkReply::NetworkError qt_error(quint32 errorCode) return error; } +bool is_PUSH_PROMISE_enabled() +{ + bool ok = false; + const int env = qEnvironmentVariableIntValue("QT_HTTP2_ENABLE_PUSH_PROMISE", &ok); + return ok && env; +} + +bool is_protocol_upgraded(const QHttpNetworkReply &reply) +{ + if (reply.statusCode() == 101) { + // Do some minimal checks here - we expect 'Upgrade: h2c' to be found. + const auto &header = reply.header(); + for (const QPair<QByteArray, QByteArray> &field : header) { + if (field.first.toLower() == "upgrade" && field.second.toLower() == "h2c") + return true; + } + } + + return false; } +Frame default_SETTINGS_frame() +{ + // 6.5 SETTINGS + FrameWriter builder(FrameType::SETTINGS, FrameFlag::EMPTY, connectionStreamID); + // MAX frame size (16 kb), disable/enable PUSH_PROMISE + builder.append(Settings::MAX_FRAME_SIZE_ID); + builder.append(quint32(maxFrameSize)); + builder.append(Settings::ENABLE_PUSH_ID); + builder.append(quint32(is_PUSH_PROMISE_enabled())); + + return builder.outboundFrame(); +} + +} // namespace Http2 + QT_END_NAMESPACE diff --git a/src/network/access/http2/http2protocol_p.h b/src/network/access/http2/http2protocol_p.h index 5d730404bb..b26ff0e9f4 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 QHttpNetworkReply; class QString; namespace Http2 @@ -132,6 +134,7 @@ enum Http2PredefinedParameters const quint32 lastValidStreamID((quint32(1) << 31) - 1); // HTTP/2, 5.1.1 extern const Q_AUTOTEST_EXPORT char Http2clientPreface[clientPrefaceLength]; +void prepare_for_protocol_upgrade(QHttpNetworkRequest &request); enum class FrameStatus { @@ -169,6 +172,9 @@ enum Http2Error void qt_error(quint32 errorCode, QNetworkReply::NetworkError &error, QString &errorString); QString qt_error_string(quint32 errorCode); QNetworkReply::NetworkError qt_error(quint32 errorCode); +bool is_PUSH_PROMISE_enabled(); +bool is_protocol_upgraded(const QHttpNetworkReply &reply); +struct Frame default_SETTINGS_frame(); } |