summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTimur Pocheptsov <timur.pocheptsov@qt.io>2017-10-12 15:02:09 +0200
committerTimur Pocheptsov <timur.pocheptsov@qt.io>2017-10-16 16:54:32 +0000
commita520c179b89610248ef97f614d6b831ccee54c6b (patch)
tree553faf7adb5aa9339ed23f062975fa85a11d8431 /src
parent65eed6d5978738b06e047c9bd5d162b2596759f7 (diff)
HTTP/2 protocol handler: tweak receive window sizes
We were using the default ones, provided by RFC7540. It appears they are way too restrictive and conservative: when downloading something relatively big, a stream keeps spending the whole session/its own 'recv' windows and thus we have to constantly send WINDOW_UPDATE frames. This significantly slows down our HTTP/2 implementation, making it orders of magnitude slower than HTTP/1.1. To fix this: - We send SETTINGS_INITIAL_WINDOW_SIZE in the first SETTINGS frame to inform our peer that per-stream WINDOW is bigger than 64Kb - We increase the session's receive window size. Task-number: QTBUG-63722 Change-Id: I31312fcfd5f0fc0aee6aaa5d3562cc7d1b931adc Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/network/access/http2/http2protocol.cpp2
-rw-r--r--src/network/access/http2/http2protocol_p.h14
-rw-r--r--src/network/access/qhttp2protocolhandler.cpp31
-rw-r--r--src/network/access/qhttp2protocolhandler_p.h8
4 files changed, 30 insertions, 25 deletions
diff --git a/src/network/access/http2/http2protocol.cpp b/src/network/access/http2/http2protocol.cpp
index 54811aeab0..bb3d6bf575 100644
--- a/src/network/access/http2/http2protocol.cpp
+++ b/src/network/access/http2/http2protocol.cpp
@@ -216,6 +216,8 @@ Frame default_SETTINGS_frame()
// 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()));
diff --git a/src/network/access/http2/http2protocol_p.h b/src/network/access/http2/http2protocol_p.h
index b26ff0e9f4..c64e960002 100644
--- a/src/network/access/http2/http2protocol_p.h
+++ b/src/network/access/http2/http2protocol_p.h
@@ -129,10 +129,20 @@ enum Http2PredefinedParameters
maxConcurrentStreams = 100 // HTTP/2, 6.5.2
};
-// It's int, it has internal linkage, it's ok to have it in headers -
-// no ODR violation is possible.
+// These are ints, const, they have internal linkage, it's ok to have them in
+// headers - no ODR violation.
const quint32 lastValidStreamID((quint32(1) << 31) - 1); // HTTP/2, 5.1.1
+// The default size of 64K is too small and limiting: if we use it, we end up
+// 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;
+
extern const Q_AUTOTEST_EXPORT char Http2clientPreface[clientPrefaceLength];
void prepare_for_protocol_upgrade(QHttpNetworkRequest &request);
diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp
index a651fc4092..461f2429b3 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)
@@ -374,12 +372,10 @@ bool QHttp2ProtocolHandler::sendClientPreface()
if (!frameWriter.write(*m_socket))
return false;
- sessionRecvWindowSize = sessionMaxRecvWindowSize;
- if (defaultSessionWindowSize < sessionMaxRecvWindowSize) {
- const auto delta = sessionMaxRecvWindowSize - defaultSessionWindowSize;
- if (!sendWINDOW_UPDATE(connectionStreamID, delta))
- return false;
- }
+ sessionRecvWindowSize = Http2::initialSessionReceiveWindowSize;
+ const auto delta = Http2::initialSessionReceiveWindowSize - Http2::defaultSessionWindowSize;
+ if (!sendWINDOW_UPDATE(Http2::connectionStreamID, delta))
+ return false;
prefaceSent = true;
waitingForSettingsACK = true;
@@ -549,20 +545,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 < Http2::initialStreamReceiveWindowSize / 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, Http2::initialStreamReceiveWindowSize - stream.recvWindow));
+ stream.recvWindow = Http2::initialStreamReceiveWindowSize;
}
}
}
- if (sessionRecvWindowSize < sessionMaxRecvWindowSize / 2) {
+ if (sessionRecvWindowSize < Http2::initialSessionReceiveWindowSize / 2) {
QMetaObject::invokeMethod(this, "sendWINDOW_UPDATE", Qt::QueuedConnection,
Q_ARG(quint32, connectionStreamID),
- Q_ARG(quint32, sessionMaxRecvWindowSize - sessionRecvWindowSize));
- sessionRecvWindowSize = sessionMaxRecvWindowSize;
+ Q_ARG(quint32, Http2::initialSessionReceiveWindowSize - sessionRecvWindowSize));
+ sessionRecvWindowSize = Http2::initialSessionReceiveWindowSize;
}
}
@@ -1203,7 +1199,7 @@ quint32 QHttp2ProtocolHandler::createNewStream(const HttpMessagePair &message, b
const Stream newStream(message, newStreamID,
streamInitialSendWindowSize,
- streamInitialRecvWindowSize);
+ Http2::initialStreamReceiveWindowSize);
if (!uploadDone) {
if (auto src = newStream.data()) {
@@ -1398,7 +1394,8 @@ 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,
+ Http2::initialStreamReceiveWindowSize));
return true;
}
@@ -1432,7 +1429,7 @@ void QHttp2ProtocolHandler::initReplyFromPushPromise(const HttpMessagePair &mess
// Let's pretent we're sending a request now:
Stream closedStream(message, promise.reservedID,
streamInitialSendWindowSize,
- streamInitialRecvWindowSize);
+ Http2::initialStreamReceiveWindowSize);
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 82eea21818..b52f8ae10c 100644
--- a/src/network/access/qhttp2protocolhandler_p.h
+++ b/src/network/access/qhttp2protocolhandler_p.h
@@ -176,13 +176,9 @@ private:
quint32 maxConcurrentStreams = Http2::maxConcurrentStreams;
// Control flow:
- static const qint32 sessionMaxRecvWindowSize = Http2::defaultSessionWindowSize * 10;
- // Signed integer, it can become negative (it's still a valid window size):
- qint32 sessionRecvWindowSize = sessionMaxRecvWindowSize;
- // We do not negotiate this window size
- // We have to send WINDOW_UPDATE frames to our peer also.
- static const qint32 streamInitialRecvWindowSize = Http2::defaultSessionWindowSize;
+ // Signed integer, it can become negative (it's still a valid window size):
+ qint32 sessionRecvWindowSize = Http2::initialSessionReceiveWindowSize;
// Updated by SETTINGS and WINDOW_UPDATE.
qint32 sessionSendWindowSize = Http2::defaultSessionWindowSize;