summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorTimur Pocheptsov <timur.pocheptsov@qt.io>2019-09-04 15:00:31 +0200
committerTimur Pocheptsov <timur.pocheptsov@qt.io>2019-09-06 03:01:52 +0000
commitd1a22bd29856807805fa56608d2953c072df0cf1 (patch)
tree4e3c3aefd1f6dbed6ccf8d80f5c5b54644873d21 /src
parenta42a4513398a159e4f6749fbcb1309c4a42b4841 (diff)
A follow-up to a recent fix in QHttpNetworkConnectionChannel
While working with HTTP/2, we are not re-sending failed requests. In case we receive a GOAWAY frame, we properly handle it by processing some active streams if possible, and aborting streams that will not proceed further with ContentResendError. But it's possible that some server failed to send us GOAWAY (for example, it died) or closed the connection not finishing the streams that were still active and valid (ID <= value from GOAWAY frame). Now that we will not re-connect, there is no reason to be quiet about us not progressing - emit RemoteHostClosedError on any remaining active stream/request we cannot process further. Fixes: QTBUG-77852 Change-Id: I4cd68a1c8c103b1fbe36c20a1cc406ab2e20dd12 Reviewed-by: MÃ¥rten Nordheim <marten.nordheim@qt.io> Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io> (cherry picked from commit 543769666f18f79bd6ebd6119a39834aafc2b0df)
Diffstat (limited to 'src')
-rw-r--r--src/network/access/qhttp2protocolhandler.cpp25
-rw-r--r--src/network/access/qhttp2protocolhandler_p.h2
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp13
3 files changed, 39 insertions, 1 deletions
diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp
index 5d7fc7506e..6609aa0594 100644
--- a/src/network/access/qhttp2protocolhandler.cpp
+++ b/src/network/access/qhttp2protocolhandler.cpp
@@ -55,6 +55,8 @@
#include <QtNetwork/qnetworkproxy.h>
#endif
+#include <qcoreapplication.h>
+
#include <algorithm>
#include <vector>
@@ -211,6 +213,29 @@ QHttp2ProtocolHandler::QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *chan
}
}
+void QHttp2ProtocolHandler::handleConnectionClosure()
+{
+ // The channel has just received RemoteHostClosedError and since it will
+ // not try (for HTTP/2) to re-connect, it's time to finish all replies
+ // with error.
+
+ // Maybe we still have some data to read and can successfully finish
+ // a stream/request?
+ _q_receiveReply();
+
+ // Finish all still active streams. If we previously had GOAWAY frame,
+ // we probably already closed some (or all) streams with ContentReSend
+ // error, but for those still active, not having any data to finish,
+ // we now report RemoteHostClosedError.
+ const auto errorString = QCoreApplication::translate("QHttp", "Connection closed");
+ for (auto it = activeStreams.begin(), eIt = activeStreams.end(); it != eIt; ++it)
+ finishStreamWithError(it.value(), QNetworkReply::RemoteHostClosedError, errorString);
+
+ // Make sure we'll never try to read anything later:
+ activeStreams.clear();
+ goingAway = true;
+}
+
void QHttp2ProtocolHandler::_q_uploadDataReadyRead()
{
if (!sender()) // QueuedConnection, firing after sender (byte device) was deleted.
diff --git a/src/network/access/qhttp2protocolhandler_p.h b/src/network/access/qhttp2protocolhandler_p.h
index 9165808302..d91853f613 100644
--- a/src/network/access/qhttp2protocolhandler_p.h
+++ b/src/network/access/qhttp2protocolhandler_p.h
@@ -90,6 +90,8 @@ public:
QHttp2ProtocolHandler &operator = (const QHttp2ProtocolHandler &rhs) = delete;
QHttp2ProtocolHandler &operator = (QHttp2ProtocolHandler &&rhs) = delete;
+ Q_INVOKABLE void handleConnectionClosure();
+
private slots:
void _q_uploadDataReadyRead();
void _q_replyDestroyed(QObject* reply);
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index e7af7b648e..1fac24ab49 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -967,7 +967,18 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
if (!reply && state == QHttpNetworkConnectionChannel::IdleState) {
// Not actually an error, it is normal for Keep-Alive connections to close after some time if no request
// is sent on them. No need to error the other replies below. Just bail out here.
- // The _q_disconnected will handle the possibly pipelined replies
+ // The _q_disconnected will handle the possibly pipelined replies. HTTP/2 is special for now,
+ // we do not resend, but must report errors if any request is in progress (note, while
+ // not in its sendRequest(), protocol handler switches the channel to IdleState, thus
+ // this check is under this condition in 'if'):
+ if (protocolHandler.data()) {
+ if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct
+ || connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2) {
+ auto h2Handler = static_cast<QHttp2ProtocolHandler *>(protocolHandler.data());
+ h2Handler->handleConnectionClosure();
+ protocolHandler.reset();
+ }
+ }
return;
} else if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) {
// Try to reconnect/resend before sending an error.