summaryrefslogtreecommitdiffstats
path: root/src/network/access/qhttp2protocolhandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/access/qhttp2protocolhandler.cpp')
-rw-r--r--src/network/access/qhttp2protocolhandler.cpp104
1 files changed, 70 insertions, 34 deletions
diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp
index 9dfcec8311..42f1343c52 100644
--- a/src/network/access/qhttp2protocolhandler.cpp
+++ b/src/network/access/qhttp2protocolhandler.cpp
@@ -161,8 +161,6 @@ bool sum_will_overflow(qint32 windowSize, qint32 delta)
using namespace Http2;
const std::deque<quint32>::size_type QHttp2ProtocolHandler::maxRecycledStreams = 10000;
-const qint32 QHttp2ProtocolHandler::sessionMaxRecvWindowSize;
-const qint32 QHttp2ProtocolHandler::streamInitialRecvWindowSize;
const quint32 QHttp2ProtocolHandler::maxAcceptableTableSize;
QHttp2ProtocolHandler::QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *channel)
@@ -170,10 +168,47 @@ QHttp2ProtocolHandler::QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *chan
decoder(HPack::FieldLookupTable::DefaultSize),
encoder(HPack::FieldLookupTable::DefaultSize, true)
{
+ Q_ASSERT(channel && m_connection);
+
continuedFrames.reserve(20);
- bool ok = false;
- const int env = qEnvironmentVariableIntValue("QT_HTTP2_ENABLE_PUSH_PROMISE", &ok);
- pushPromiseEnabled = ok && env;
+
+ 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
+ // as HTTP/1.1 request. The response with status code 101 triggered
+ // protocol switch and now we are waiting for the real response, sent
+ // as HTTP/2 frames.
+ Q_ASSERT(channel->reply);
+ const quint32 initialStreamID = createNewStream(HttpMessagePair(channel->request, channel->reply),
+ true /* uploaded by HTTP/1.1 */);
+ Q_ASSERT(initialStreamID == 1);
+ Stream &stream = activeStreams[initialStreamID];
+ stream.state = Stream::halfClosedLocal;
+ }
}
void QHttp2ProtocolHandler::_q_uploadDataReadyRead()
@@ -350,28 +385,27 @@ 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.start(FrameType::SETTINGS, FrameFlag::EMPTY, Http2::connectionStreamID);
- // MAX frame size (16 kb), enable/disable PUSH
- frameWriter.append(Settings::MAX_FRAME_SIZE_ID);
- frameWriter.append(quint32(Http2::maxFrameSize));
- frameWriter.append(Settings::ENABLE_PUSH_ID);
- frameWriter.append(quint32(pushPromiseEnabled));
+ 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 = sessionMaxRecvWindowSize;
- if (defaultSessionWindowSize < sessionMaxRecvWindowSize) {
- const auto delta = sessionMaxRecvWindowSize - defaultSessionWindowSize;
- if (!sendWINDOW_UPDATE(connectionStreamID, delta))
- return false;
- }
+ 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;
prefaceSent = true;
waitingForSettingsACK = true;
@@ -519,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];
@@ -541,20 +575,20 @@ void QHttp2ProtocolHandler::handleDATA()
if (inboundFrame.flags().testFlag(FrameFlag::END_STREAM)) {
finishStream(stream);
deleteActiveStream(stream.streamID);
- } else if (stream.recvWindow < streamInitialRecvWindowSize / 2) {
+ } else if (stream.recvWindow < streamInitialReceiveWindowSize / 2) {
QMetaObject::invokeMethod(this, "sendWINDOW_UPDATE", Qt::QueuedConnection,
Q_ARG(quint32, stream.streamID),
- Q_ARG(quint32, streamInitialRecvWindowSize - stream.recvWindow));
- stream.recvWindow = streamInitialRecvWindowSize;
+ Q_ARG(quint32, streamInitialReceiveWindowSize - stream.recvWindow));
+ stream.recvWindow = streamInitialReceiveWindowSize;
}
}
}
- if (sessionRecvWindowSize < sessionMaxRecvWindowSize / 2) {
+ if (sessionReceiveWindowSize < maxSessionReceiveWindowSize / 2) {
QMetaObject::invokeMethod(this, "sendWINDOW_UPDATE", Qt::QueuedConnection,
Q_ARG(quint32, connectionStreamID),
- Q_ARG(quint32, sessionMaxRecvWindowSize - sessionRecvWindowSize));
- sessionRecvWindowSize = sessionMaxRecvWindowSize;
+ Q_ARG(quint32, maxSessionReceiveWindowSize - sessionReceiveWindowSize));
+ sessionReceiveWindowSize = maxSessionReceiveWindowSize;
}
}
@@ -1176,7 +1210,7 @@ void QHttp2ProtocolHandler::finishStreamWithError(Stream &stream, QNetworkReply:
<< "finished with error:" << message;
}
-quint32 QHttp2ProtocolHandler::createNewStream(const HttpMessagePair &message)
+quint32 QHttp2ProtocolHandler::createNewStream(const HttpMessagePair &message, bool uploadDone)
{
const qint32 newStreamID = allocateStreamID();
if (!newStreamID)
@@ -1195,12 +1229,14 @@ quint32 QHttp2ProtocolHandler::createNewStream(const HttpMessagePair &message)
const Stream newStream(message, newStreamID,
streamInitialSendWindowSize,
- streamInitialRecvWindowSize);
+ streamInitialReceiveWindowSize);
- if (auto src = newStream.data()) {
- connect(src, SIGNAL(readyRead()), this,
- SLOT(_q_uploadDataReadyRead()), Qt::QueuedConnection);
- src->setProperty("HTTP2StreamID", newStreamID);
+ if (!uploadDone) {
+ if (auto src = newStream.data()) {
+ connect(src, SIGNAL(readyRead()), this,
+ SLOT(_q_uploadDataReadyRead()), Qt::QueuedConnection);
+ src->setProperty("HTTP2StreamID", newStreamID);
+ }
}
activeStreams.insert(newStreamID, newStream);
@@ -1388,7 +1424,7 @@ bool QHttp2ProtocolHandler::tryReserveStream(const Http2::Frame &pushPromiseFram
promise.reservedID = reservedID;
promise.pushHeader = requestHeader;
- activeStreams.insert(reservedID, Stream(urlKey, reservedID, streamInitialRecvWindowSize));
+ activeStreams.insert(reservedID, Stream(urlKey, reservedID, streamInitialReceiveWindowSize));
return true;
}
@@ -1422,7 +1458,7 @@ void QHttp2ProtocolHandler::initReplyFromPushPromise(const HttpMessagePair &mess
// Let's pretent we're sending a request now:
Stream closedStream(message, promise.reservedID,
streamInitialSendWindowSize,
- streamInitialRecvWindowSize);
+ streamInitialReceiveWindowSize);
closedStream.state = Stream::halfClosedLocal;
activeStreams.insert(promise.reservedID, closedStream);
promisedStream = &activeStreams[promise.reservedID];