From 76a6b3294223f52568cd8c6190edceedbdca70ce Mon Sep 17 00:00:00 2001 From: Timur Pocheptsov Date: Wed, 18 Oct 2017 11:59:21 +0200 Subject: HTTP/2 - make protocol settings configurable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Recently we have updated our receive window size to a larger value. Unfortunately, this also results in auto-test pumping through more data, which probably takes more time on CI. At the moment we do not have any public API on QNAM's level to customize HTTP/2 parameters (aka 5.10/FF and so on). So we use the fact that QNAM is QObject and we can set a property on it. This property is our Http2::ProtocolParameters object that allows us to configure: - HPACK parameters (in 5.10 - noop) - session receive window size - different SETTINGS as described by RFC 7540, 6.5.2. 2. Undocumented environment variable to set ENABLE_PUSH is not needed anymore. 3. In 5.11 Http2::ProtocolParameter will become a public API and we'll introduce a new setter in QNAM. Change-Id: If08fd5e09e7c0b61cf9700b426b60b5837b6b2e6 Reviewed-by: MÃ¥rten Nordheim Reviewed-by: Edward Welbourne --- src/network/access/http2/http2protocol.cpp | 129 ++++++++++++++++----- src/network/access/http2/http2protocol_p.h | 61 ++++++++-- src/network/access/qhttp2protocolhandler.cpp | 65 ++++++++--- src/network/access/qhttp2protocolhandler_p.h | 34 ++++-- src/network/access/qhttpnetworkconnection.cpp | 17 +++ src/network/access/qhttpnetworkconnection_p.h | 6 + .../access/qhttpnetworkconnectionchannel.cpp | 4 +- src/network/access/qhttpthreaddelegate.cpp | 8 +- src/network/access/qhttpthreaddelegate_p.h | 2 + src/network/access/qnetworkreplyhttpimpl.cpp | 4 + 10 files changed, 262 insertions(+), 68 deletions(-) (limited to 'src/network/access') diff --git a/src/network/access/http2/http2protocol.cpp b/src/network/access/http2/http2protocol.cpp index bb3d6bf575..f51af4be5c 100644 --- a/src/network/access/http2/http2protocol.cpp +++ b/src/network/access/http2/http2protocol.cpp @@ -62,9 +62,88 @@ const char Http2clientPreface[clientPrefaceLength] = 0x2e, 0x30, 0x0d, 0x0a, 0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a}; -QByteArray default_SETTINGS_to_Base64() +// TODO: (in 5.11) - remove it! +const char *http2ParametersPropertyName = "QT_HTTP2_PARAMETERS_PROPERTY"; + +ProtocolParameters::ProtocolParameters() +{ + settingsFrameData[Settings::INITIAL_WINDOW_SIZE_ID] = qtDefaultStreamReceiveWindowSize; + settingsFrameData[Settings::ENABLE_PUSH_ID] = 0; +} + +bool ProtocolParameters::validate() const +{ + // 0. Huffman/indexing: any values are valid and allowed. + + // 1. Session receive window size (client side): HTTP/2 starts from the + // default value of 64Kb, if a client code tries to set lesser value, + // the delta would become negative, but this is not allowed. + if (maxSessionReceiveWindowSize < qint32(defaultSessionWindowSize)) { + qCWarning(QT_HTTP2, "Session receive window must be at least 65535 bytes"); + return false; + } + + // 2. HEADER_TABLE_SIZE: we do not validate HEADER_TABLE_SIZE, considering + // all values as valid. RFC 7540 and 7541 do not provide any lower/upper + // limits. If it's 0 - we do not index anything, if it's too huge - a user + // who provided such a value can potentially have a huge memory footprint, + // up to them to decide. + + // 3. SETTINGS_ENABLE_PUSH: RFC 7540, 6.5.2, a value other than 0 or 1 will + // be treated by our peer as a PROTOCOL_ERROR. + if (settingsFrameData.contains(Settings::ENABLE_PUSH_ID) + && settingsFrameData[Settings::ENABLE_PUSH_ID] > 1) { + qCWarning(QT_HTTP2, "SETTINGS_ENABLE_PUSH can be only 0 or 1"); + return false; + } + + // 4. SETTINGS_MAX_CONCURRENT_STREAMS : RFC 7540 recommends 100 as the lower + // limit, says nothing about the upper limit. The RFC allows 0, but this makes + // no sense to us at all: there is no way a user can change this later and + // we'll not be able to get any responses on such a connection. + if (settingsFrameData.contains(Settings::MAX_CONCURRENT_STREAMS_ID) + && !settingsFrameData[Settings::MAX_CONCURRENT_STREAMS_ID]) { + qCWarning(QT_HTTP2, "MAX_CONCURRENT_STREAMS must be a positive number"); + return false; + } + + // 5. SETTINGS_INITIAL_WINDOW_SIZE. + if (settingsFrameData.contains(Settings::INITIAL_WINDOW_SIZE_ID)) { + const quint32 value = settingsFrameData[Settings::INITIAL_WINDOW_SIZE_ID]; + // RFC 7540, 6.5.2 (the upper limit). The lower limit is our own - we send + // SETTINGS frame only once and will not be able to change this 0, thus + // we'll suspend all streams. + if (!value || value > quint32(maxSessionReceiveWindowSize)) { + qCWarning(QT_HTTP2, "INITIAL_WINDOW_SIZE must be in the range " + "(0, 2^31-1]"); + return false; + } + } + + // 6. SETTINGS_MAX_FRAME_SIZE: RFC 7540, 6.5.2, a value outside of the range + // [2^14-1, 2^24-1] will be treated by our peer as a PROTOCOL_ERROR. + if (settingsFrameData.contains(Settings::MAX_FRAME_SIZE_ID)) { + const quint32 value = settingsFrameData[Settings::INITIAL_WINDOW_SIZE_ID]; + if (value < maxFrameSize || value > maxPayloadSize) { + qCWarning(QT_HTTP2, "MAX_FRAME_SIZE must be in the range [2^14, 2^24-1]"); + return false; + } + } + + // For SETTINGS_MAX_HEADER_LIST_SIZE RFC 7540 does not provide any specific + // numbers. It's clear, if a value is too small, no header can ever be sent + // by our peer at all. The default value is unlimited and we normally do not + // change this. + // + // Note: the size is calculated as the length of uncompressed (no HPACK) + // name + value + 32 bytes. + + return true; +} + +QByteArray ProtocolParameters::settingsFrameToBase64() const { - Frame frame(default_SETTINGS_frame()); + Frame frame(settingsFrame()); // SETTINGS frame's payload consists of pairs: // 2-byte-identifier | 4-byte-value == multiple of 6. Q_ASSERT(frame.payloadSize() && !(frame.payloadSize() % 6)); @@ -78,21 +157,35 @@ QByteArray default_SETTINGS_to_Base64() return wrapper.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals); } -void prepare_for_protocol_upgrade(QHttpNetworkRequest &request) +Frame ProtocolParameters::settingsFrame() const +{ + // 6.5 SETTINGS + FrameWriter builder(FrameType::SETTINGS, FrameFlag::EMPTY, connectionStreamID); + for (auto it = settingsFrameData.cbegin(), end = settingsFrameData.cend(); + it != end; ++it) { + builder.append(it.key()); + builder.append(it.value()); + } + + return builder.outboundFrame(); +} + +void ProtocolParameters::addProtocolUpgradeHeaders(QHttpNetworkRequest *request) const { + Q_ASSERT(request); // RFC 2616, 14.10 // RFC 7540, 3.2 - QByteArray value(request.headerField("Connection")); + QByteArray value(request->headerField("Connection")); // We _append_ 'Upgrade': if (value.size()) value += ", "; value += "Upgrade, HTTP2-Settings"; - request.setHeaderField("Connection", value); + request->setHeaderField("Connection", value); // This we just (re)write. - request.setHeaderField("Upgrade", "h2c"); + request->setHeaderField("Upgrade", "h2c"); // This we just (re)write. - request.setHeaderField("HTTP2-Settings", default_SETTINGS_to_Base64()); + request->setHeaderField("HTTP2-Settings", settingsFrameToBase64()); } void qt_error(quint32 errorCode, QNetworkReply::NetworkError &error, @@ -188,13 +281,6 @@ 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) { @@ -209,21 +295,6 @@ bool is_protocol_upgraded(const QHttpNetworkReply &reply) 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::INITIAL_WINDOW_SIZE_ID); - builder.append(initialStreamReceiveWindowSize); - 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 c64e960002..f7a89859d3 100644 --- a/src/network/access/http2/http2protocol_p.h +++ b/src/network/access/http2/http2protocol_p.h @@ -53,6 +53,7 @@ #include #include +#include #include // Different HTTP/2 constants/values as defined by RFC 7540. @@ -61,6 +62,7 @@ QT_BEGIN_NAMESPACE class QHttpNetworkRequest; class QHttpNetworkReply; +class QByteArray; class QString; namespace Http2 @@ -137,14 +139,54 @@ const quint32 lastValidStreamID((quint32(1) << 31) - 1); // HTTP/2, 5.1.1 // sending WINDOW_UPDATE frames on a stream/session all the time, for each // 2 DATE frames of size 16K (also default) we'll send a WINDOW_UPDATE frame // for a given stream and have a download speed order of magnitude lower than -// our own HTTP/1.1 protocol handler. We choose a bigger window size (normally, -// HTTP/2 servers are not afraid to immediately set it to possible max anyway) -// and split this window size between our concurrent streams. -const qint32 initialSessionReceiveWindowSize = defaultSessionWindowSize * 10000; -const qint32 initialStreamReceiveWindowSize = initialSessionReceiveWindowSize / maxConcurrentStreams; +// our own HTTP/1.1 protocol handler. We choose a bigger window size: normally, +// HTTP/2 servers are not afraid to immediately set it to the possible max, +// we do the same and split this window size between our concurrent streams. +const qint32 maxSessionReceiveWindowSize((quint32(1) << 31) - 1); +const qint32 qtDefaultStreamReceiveWindowSize = maxSessionReceiveWindowSize / maxConcurrentStreams; + +// The class ProtocolParameters allows client code to customize HTTP/2 protocol +// handler, if needed. Normally, we use our own default parameters (see below). +// In 5.10 we can also use setProperty/property on a QNAM object to pass the +// non-default values to the protocol handler. In 5.11 this will probably become +// a public API. + +using RawSettings = QMap; + +struct Q_AUTOTEST_EXPORT ProtocolParameters +{ + ProtocolParameters(); + + bool validate() const; + QByteArray settingsFrameToBase64() const; + struct Frame settingsFrame() const; + void addProtocolUpgradeHeaders(QHttpNetworkRequest *request) const; + + // HPACK: + // TODO: for now we ignore them (fix it for 5.11, would require changes in HPACK) + bool useHuffman = true; + bool indexStrings = true; + + // This parameter is not negotiated via SETTINGS frames, so we have it + // as a member and will convey it to our peer as a WINDOW_UPDATE frame: + qint32 maxSessionReceiveWindowSize = Http2::maxSessionReceiveWindowSize; + + // This is our default SETTINGS frame: + // + // SETTINGS_INITIAL_WINDOW_SIZE: (2^31 - 1) / 100 + // SETTINGS_ENABLE_PUSH: 0. + // + // Note, whenever we skip some value in our SETTINGS frame, our peer + // will assume the defaults recommended by RFC 7540, which in general + // are good enough, although we (and most browsers) prefer to work + // with larger window sizes. + RawSettings settingsFrameData; +}; + +// TODO: remove in 5.11 +extern const Q_AUTOTEST_EXPORT char *http2ParametersPropertyName; extern const Q_AUTOTEST_EXPORT char Http2clientPreface[clientPrefaceLength]; -void prepare_for_protocol_upgrade(QHttpNetworkRequest &request); enum class FrameStatus { @@ -182,14 +224,15 @@ 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(); -} +} // namespace Http2 Q_DECLARE_LOGGING_CATEGORY(QT_HTTP2) QT_END_NAMESPACE +Q_DECLARE_METATYPE(Http2::Settings) +Q_DECLARE_METATYPE(Http2::ProtocolParameters) + #endif diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp index 461f2429b3..4b330c491a 100644 --- a/src/network/access/qhttp2protocolhandler.cpp +++ b/src/network/access/qhttp2protocolhandler.cpp @@ -168,9 +168,34 @@ QHttp2ProtocolHandler::QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *chan decoder(HPack::FieldLookupTable::DefaultSize), encoder(HPack::FieldLookupTable::DefaultSize, true) { - Q_ASSERT(channel); + Q_ASSERT(channel && m_connection); + continuedFrames.reserve(20); - pushPromiseEnabled = is_PUSH_PROMISE_enabled(); + + const ProtocolParameters params(m_connection->http2Parameters()); + Q_ASSERT(params.validate()); + + maxSessionReceiveWindowSize = params.maxSessionReceiveWindowSize; + + const RawSettings &data = params.settingsFrameData; + for (auto param = data.cbegin(), end = data.cend(); param != end; ++param) { + switch (param.key()) { + case Settings::INITIAL_WINDOW_SIZE_ID: + streamInitialReceiveWindowSize = param.value(); + break; + case Settings::ENABLE_PUSH_ID: + pushPromiseEnabled = param.value(); + break; + case Settings::HEADER_TABLE_SIZE_ID: + case Settings::MAX_CONCURRENT_STREAMS_ID: + case Settings::MAX_FRAME_SIZE_ID: + case Settings::MAX_HEADER_LIST_SIZE_ID: + // These other settings are just recommendations to our peer. We + // only check they are not crazy in ProtocolParameters::validate(). + default: + break; + } + } if (!channel->ssl) { // We upgraded from HTTP/1.1 to HTTP/2. channel->request was already sent @@ -360,20 +385,25 @@ bool QHttp2ProtocolHandler::sendClientPreface() if (prefaceSent) return true; - const qint64 written = m_socket->write(Http2clientPreface, + const qint64 written = m_socket->write(Http2::Http2clientPreface, Http2::clientPrefaceLength); if (written != Http2::clientPrefaceLength) return false; // 6.5 SETTINGS - frameWriter.setOutboundFrame(default_SETTINGS_frame()); + const ProtocolParameters params(m_connection->http2Parameters()); + Q_ASSERT(params.validate()); + frameWriter.setOutboundFrame(params.settingsFrame()); Q_ASSERT(frameWriter.outboundFrame().payloadSize()); if (!frameWriter.write(*m_socket)) return false; - sessionRecvWindowSize = Http2::initialSessionReceiveWindowSize; - const auto delta = Http2::initialSessionReceiveWindowSize - Http2::defaultSessionWindowSize; + sessionReceiveWindowSize = maxSessionReceiveWindowSize; + // ProtocolParameters::validate does not allow maxSessionReceiveWindowSize + // to be smaller than defaultSessionWindowSize, so everything is OK here with + // 'delta': + const auto delta = maxSessionReceiveWindowSize - Http2::defaultSessionWindowSize; if (!sendWINDOW_UPDATE(Http2::connectionStreamID, delta)) return false; @@ -523,10 +553,10 @@ void QHttp2ProtocolHandler::handleDATA() if (!activeStreams.contains(streamID) && !streamWasReset(streamID)) return connectionError(ENHANCE_YOUR_CALM, "DATA on invalid stream"); - if (qint32(inboundFrame.payloadSize()) > sessionRecvWindowSize) + if (qint32(inboundFrame.payloadSize()) > sessionReceiveWindowSize) return connectionError(FLOW_CONTROL_ERROR, "Flow control error"); - sessionRecvWindowSize -= inboundFrame.payloadSize(); + sessionReceiveWindowSize -= inboundFrame.payloadSize(); if (activeStreams.contains(streamID)) { auto &stream = activeStreams[streamID]; @@ -545,20 +575,20 @@ void QHttp2ProtocolHandler::handleDATA() if (inboundFrame.flags().testFlag(FrameFlag::END_STREAM)) { finishStream(stream); deleteActiveStream(stream.streamID); - } else if (stream.recvWindow < Http2::initialStreamReceiveWindowSize / 2) { + } else if (stream.recvWindow < streamInitialReceiveWindowSize / 2) { QMetaObject::invokeMethod(this, "sendWINDOW_UPDATE", Qt::QueuedConnection, Q_ARG(quint32, stream.streamID), - Q_ARG(quint32, Http2::initialStreamReceiveWindowSize - stream.recvWindow)); - stream.recvWindow = Http2::initialStreamReceiveWindowSize; + Q_ARG(quint32, streamInitialReceiveWindowSize - stream.recvWindow)); + stream.recvWindow = streamInitialReceiveWindowSize; } } } - if (sessionRecvWindowSize < Http2::initialSessionReceiveWindowSize / 2) { + if (sessionReceiveWindowSize < maxSessionReceiveWindowSize / 2) { QMetaObject::invokeMethod(this, "sendWINDOW_UPDATE", Qt::QueuedConnection, Q_ARG(quint32, connectionStreamID), - Q_ARG(quint32, Http2::initialSessionReceiveWindowSize - sessionRecvWindowSize)); - sessionRecvWindowSize = Http2::initialSessionReceiveWindowSize; + Q_ARG(quint32, maxSessionReceiveWindowSize - sessionReceiveWindowSize)); + sessionReceiveWindowSize = maxSessionReceiveWindowSize; } } @@ -1199,7 +1229,7 @@ quint32 QHttp2ProtocolHandler::createNewStream(const HttpMessagePair &message, b const Stream newStream(message, newStreamID, streamInitialSendWindowSize, - Http2::initialStreamReceiveWindowSize); + streamInitialReceiveWindowSize); if (!uploadDone) { if (auto src = newStream.data()) { @@ -1394,8 +1424,7 @@ bool QHttp2ProtocolHandler::tryReserveStream(const Http2::Frame &pushPromiseFram promise.reservedID = reservedID; promise.pushHeader = requestHeader; - activeStreams.insert(reservedID, Stream(urlKey, reservedID, - Http2::initialStreamReceiveWindowSize)); + activeStreams.insert(reservedID, Stream(urlKey, reservedID, streamInitialReceiveWindowSize)); return true; } @@ -1429,7 +1458,7 @@ void QHttp2ProtocolHandler::initReplyFromPushPromise(const HttpMessagePair &mess // Let's pretent we're sending a request now: Stream closedStream(message, promise.reservedID, streamInitialSendWindowSize, - Http2::initialStreamReceiveWindowSize); + streamInitialReceiveWindowSize); closedStream.state = Stream::halfClosedLocal; activeStreams.insert(promise.reservedID, closedStream); promisedStream = &activeStreams[promise.reservedID]; diff --git a/src/network/access/qhttp2protocolhandler_p.h b/src/network/access/qhttp2protocolhandler_p.h index b52f8ae10c..a006663491 100644 --- a/src/network/access/qhttp2protocolhandler_p.h +++ b/src/network/access/qhttp2protocolhandler_p.h @@ -172,20 +172,38 @@ private: bool continuationExpected = false; std::vector continuedFrames; - // Peer's max number of streams ... - quint32 maxConcurrentStreams = Http2::maxConcurrentStreams; - // Control flow: - // Signed integer, it can become negative (it's still a valid window size): - qint32 sessionRecvWindowSize = Http2::initialSessionReceiveWindowSize; - - // Updated by SETTINGS and WINDOW_UPDATE. + // This is how many concurrent streams our peer expects from us: + // 100 is the default value, can be updated by the server's SETTINGS + // frame(s): + quint32 maxConcurrentStreams = Http2::maxConcurrentStreams; + // While we allow sending SETTTINGS_MAX_CONCURRENT_STREAMS to limit our peer, + // it's just a hint and we do not actually enforce it (and we can continue + // sending requests and creating streams while maxConcurrentStreams allows). + + // This is the max value, we set it in a ctor from Http2::ProtocolParameters, + // it does not change after that. + qint32 maxSessionReceiveWindowSize = Http2::defaultSessionWindowSize; + + // Our session receive window size, default is 64Kb. We'll update it from QNAM's + // Http2::ProtocolParameters. Signed integer since it can become negative + // (it's still a valid window size). + qint32 sessionReceiveWindowSize = Http2::defaultSessionWindowSize; + // Our per-stream receive window size, default is 64 Kb, will be updated + // from QNAM's Http2::ProtocolParameters. Again, signed - can become negative. + qint32 streamInitialReceiveWindowSize = Http2::defaultSessionWindowSize; + + // These are our peer's receive window sizes, they will be updated by the + // peer's SETTINGS and WINDOW_UPDATE frames. qint32 sessionSendWindowSize = Http2::defaultSessionWindowSize; qint32 streamInitialSendWindowSize = Http2::defaultSessionWindowSize; - // It's unlimited by default, but can be changed via SETTINGS. + // Our peer's header size limitations. It's unlimited by default, but can + // be changed via peer's SETTINGS frame. quint32 maxHeaderListSize = (std::numeric_limits::max)(); + // While we can send SETTINGS_MAX_HEADER_LIST_SIZE value (our limit on + // the headers size), we never enforce it, it's just a hint to our peer. Q_INVOKABLE void resumeSuspendedStreams(); // Our stream IDs (all odd), the first valid will be 1. diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp index 0b474ba116..e6a15ccfc4 100644 --- a/src/network/access/qhttpnetworkconnection.cpp +++ b/src/network/access/qhttpnetworkconnection.cpp @@ -1437,6 +1437,23 @@ void QHttpNetworkConnection::setConnectionType(ConnectionType type) d->connectionType = type; } +Http2::ProtocolParameters QHttpNetworkConnection::http2Parameters() const +{ + Q_D(const QHttpNetworkConnection); + return d->http2Parameters; +} + +void QHttpNetworkConnection::setHttp2Parameters(const Http2::ProtocolParameters ¶ms) +{ + Q_D(QHttpNetworkConnection); + if (params.validate()) { + d->http2Parameters = params; + } else { + qCWarning(QT_HTTP2) + << "invalid HTTP/2 parameters, falling back to defaults instead"; + } +} + // SSL support below #ifndef QT_NO_SSL void QHttpNetworkConnection::setSslConfiguration(const QSslConfiguration &config) diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h index f01a2318a5..d3450417aa 100644 --- a/src/network/access/qhttpnetworkconnection_p.h +++ b/src/network/access/qhttpnetworkconnection_p.h @@ -67,6 +67,7 @@ #include #include #include +#include #include @@ -139,6 +140,9 @@ public: ConnectionType connectionType(); void setConnectionType(ConnectionType type); + Http2::ProtocolParameters http2Parameters() const; + void setHttp2Parameters(const Http2::ProtocolParameters ¶ms); + #ifndef QT_NO_SSL void setSslConfiguration(const QSslConfiguration &config); void ignoreSslErrors(int channel = -1); @@ -282,6 +286,8 @@ public: QSharedPointer networkSession; #endif + Http2::ProtocolParameters http2Parameters; + friend class QHttpNetworkConnectionChannel; }; diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp index 2dc3f80998..094a48a603 100644 --- a/src/network/access/qhttpnetworkconnectionchannel.cpp +++ b/src/network/access/qhttpnetworkconnectionchannel.cpp @@ -907,7 +907,9 @@ void QHttpNetworkConnectionChannel::_q_connected() if (tryProtocolUpgrade) { // Let's augment our request with some magic headers and try to // switch to HTTP/2. - Http2::prepare_for_protocol_upgrade(request); + const Http2::ProtocolParameters params(connection->http2Parameters()); + Q_ASSERT(params.validate()); + params.addProtocolUpgradeHeaders(&request); } sendRequest(); } diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp index 3d17664ed4..3204f8da33 100644 --- a/src/network/access/qhttpthreaddelegate.cpp +++ b/src/network/access/qhttpthreaddelegate.cpp @@ -292,7 +292,6 @@ void QHttpThreadDelegate::startRequest() QHttpNetworkConnection::ConnectionType connectionType = httpRequest.isHTTP2Allowed() ? QHttpNetworkConnection::ConnectionTypeHTTP2 : QHttpNetworkConnection::ConnectionTypeHTTP; - #ifndef QT_NO_SSL if (ssl && !incomingSslConfiguration.data()) incomingSslConfiguration.reset(new QSslConfiguration); @@ -334,7 +333,11 @@ void QHttpThreadDelegate::startRequest() httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl, connectionType, networkSession); -#endif +#endif // QT_NO_BEARERMANAGEMENT + if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2 + && http2Parameters.validate()) { + httpConnection->setHttp2Parameters(http2Parameters); + } // else we ignore invalid parameters and use our own defaults. #ifndef QT_NO_SSL // Set the QSslConfiguration from this QNetworkRequest. if (ssl) @@ -360,7 +363,6 @@ void QHttpThreadDelegate::startRequest() } } - // Send the request to the connection httpReply = httpConnection->sendRequest(httpRequest); httpReply->setParent(this); diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h index 2f6954aa3b..da115d6710 100644 --- a/src/network/access/qhttpthreaddelegate_p.h +++ b/src/network/access/qhttpthreaddelegate_p.h @@ -66,6 +66,7 @@ #include #include "private/qnoncontiguousbytedevice_p.h" #include "qnetworkaccessauthenticationmanager_p.h" +#include #ifndef QT_NO_HTTP @@ -115,6 +116,7 @@ public: qint64 removedContentLength; QNetworkReply::NetworkError incomingErrorCode; QString incomingErrorDetail; + Http2::ProtocolParameters http2Parameters; #ifndef QT_NO_BEARERMANAGEMENT QSharedPointer networkSession; #endif diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp index 1e68ee52dc..46a2f2f208 100644 --- a/src/network/access/qnetworkreplyhttpimpl.cpp +++ b/src/network/access/qnetworkreplyhttpimpl.cpp @@ -783,6 +783,10 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq // Create the HTTP thread delegate QHttpThreadDelegate *delegate = new QHttpThreadDelegate; + // Propagate Http/2 settings if any + const QVariant blob(manager->property(Http2::http2ParametersPropertyName)); + if (blob.isValid() && blob.canConvert()) + delegate->http2Parameters = blob.value(); #ifndef QT_NO_BEARERMANAGEMENT delegate->networkSession = managerPrivate->getNetworkSession(); #endif -- cgit v1.2.3 From 81f251660026fff198daf8920edc62bbc782ff7d Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Thu, 12 Oct 2017 14:59:51 -0700 Subject: QRandomGenerator: add system() and global() Right now,this does really nothing. This commit is just to allow us to transition the other modules (besides qtbase) to use the syntax that will become the API. I've marked three places to use the system CSPRNG: 1) the QHash seed 2) QUuid 3) QAuthenticator I didn't think the HTTP multipart boundary needed to be cryptographically safe, so I changed that one to the global generator. Change-Id: Ib17dde1a1dbb49a7bba8fffd14ecf1938bd8ff61 Reviewed-by: Edward Welbourne --- src/network/access/qhttpmultipart.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/network/access') diff --git a/src/network/access/qhttpmultipart.cpp b/src/network/access/qhttpmultipart.cpp index 58d306d5f9..c59df9d8b8 100644 --- a/src/network/access/qhttpmultipart.cpp +++ b/src/network/access/qhttpmultipart.cpp @@ -435,7 +435,7 @@ QHttpMultiPartPrivate::QHttpMultiPartPrivate() : contentType(QHttpMultiPart::Mix { // 24 random bytes, becomes 32 characters when encoded to Base64 quint32 random[6]; - QRandomGenerator::fillRange(random); + QRandomGenerator::global()->fillRange(random); boundary = "boundary_.oOo._" + QByteArray::fromRawData(reinterpret_cast(random), sizeof(random)).toBase64(); -- cgit v1.2.3