summaryrefslogtreecommitdiffstats
path: root/src/network
diff options
context:
space:
mode:
authorMatthias Rauter <matthias.rauter@qt.io>2024-02-20 16:30:59 +0100
committerMatthias Rauter <matthias.rauter@qt.io>2024-03-04 20:14:36 +0100
commit3f26fdebbc75ceb30bd15bf4ef0059aea04c96c4 (patch)
tree5e93f9063fc01548275b602da347600056c6e47a /src/network
parent302823d73b8ca27e67e703de8316092d8b4d5715 (diff)
Implement ping reply in QHttp2Connection and add test
Fixes: QTBUG-122338 Change-Id: I1e8dfa8a93c45dbe12a628d4d5e79d494d8f6032 Reviewed-by: Øystein Heskestad <oystein.heskestad@qt.io> Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
Diffstat (limited to 'src/network')
-rw-r--r--src/network/access/qhttp2connection.cpp51
-rw-r--r--src/network/access/qhttp2connection_p.h11
2 files changed, 56 insertions, 6 deletions
diff --git a/src/network/access/qhttp2connection.cpp b/src/network/access/qhttp2connection.cpp
index 81f7e2da61..ed2874c55a 100644
--- a/src/network/access/qhttp2connection.cpp
+++ b/src/network/access/qhttp2connection.cpp
@@ -9,6 +9,7 @@
#include <QtCore/private/qiodevice_p.h>
#include <QtCore/private/qnoncontiguousbytedevice_p.h>
#include <QtCore/qcoreapplication.h>
+#include <QtCore/QRandomGenerator>
#include <QtCore/qloggingcategory.h>
#include <algorithm>
@@ -922,6 +923,32 @@ bool QHttp2Connection::serverCheckClientPreface()
return true;
}
+bool QHttp2Connection::sendPing()
+{
+ std::array<char, 8> data;
+
+ QRandomGenerator gen;
+ gen.generate(data.begin(), data.end());
+ return sendPing(data);
+}
+
+bool QHttp2Connection::sendPing(QByteArrayView data)
+{
+ frameWriter.start(FrameType::PING, FrameFlag::EMPTY, connectionStreamID);
+
+ Q_ASSERT(data.length() == 8);
+ if (!m_lastPingSignature) {
+ m_lastPingSignature = data.toByteArray();
+ } else {
+ qCWarning(qHttp2ConnectionLog, "[%p] No PING is sent while waiting for the previous PING.", this);
+ return false;
+ }
+
+ frameWriter.append((uchar*)data.data(), (uchar*)data.end());
+ frameWriter.write(*getSocket());
+ return true;
+}
+
/*!
This function must be called when you have received a readyRead signal
(or equivalent) from the QIODevice. It will read and process any incoming
@@ -1389,18 +1416,30 @@ void QHttp2Connection::handlePUSH_PROMISE()
void QHttp2Connection::handlePING()
{
- // @future[server]
- // Since we're implementing a client and not
- // a server, we only reply to a PING, ACKing it.
Q_ASSERT(inboundFrame.type() == FrameType::PING);
+ Q_ASSERT(inboundFrame.dataSize() == 8);
if (inboundFrame.streamID() != connectionStreamID)
return connectionError(PROTOCOL_ERROR, "PING on invalid stream");
- if (inboundFrame.flags() & FrameFlag::ACK)
- return connectionError(PROTOCOL_ERROR, "unexpected PING ACK");
+ if (inboundFrame.flags() & FrameFlag::ACK) {
+ QByteArrayView pingSignature(reinterpret_cast<const char *>(inboundFrame.dataBegin()), 8);
+ if (!m_lastPingSignature.has_value()) {
+ emit pingFrameRecived(PingState::PongNoPingSent);
+ qCWarning(qHttp2ConnectionLog, "[%p] PING with ACK received but no PING was sent.", this);
+ } else if (pingSignature != m_lastPingSignature) {
+ emit pingFrameRecived(PingState::PongSignatureChanged);
+ qCWarning(qHttp2ConnectionLog, "[%p] PING signature does not match the last PING.", this);
+ } else {
+ emit pingFrameRecived(PingState::PongSignatureIdentical);
+ }
+ m_lastPingSignature.reset();
+ return;
+ } else {
+ emit pingFrameRecived(PingState::Ping);
+
+ }
- Q_ASSERT(inboundFrame.dataSize() == 8);
frameWriter.start(FrameType::PING, FrameFlag::ACK, connectionStreamID);
frameWriter.append(inboundFrame.dataBegin(), inboundFrame.dataBegin() + 8);
diff --git a/src/network/access/qhttp2connection_p.h b/src/network/access/qhttp2connection_p.h
index a8193f7438..219e93cedc 100644
--- a/src/network/access/qhttp2connection_p.h
+++ b/src/network/access/qhttp2connection_p.h
@@ -197,6 +197,13 @@ public:
};
Q_ENUM(CreateStreamError)
+ enum class PingState {
+ Ping,
+ PongSignatureIdentical,
+ PongSignatureChanged,
+ PongNoPingSent, // We got an ACKed ping but had not sent any
+ };
+
// For a pre-established connection:
[[nodiscard]] static QHttp2Connection *
createUpgradedConnection(QIODevice *socket, const QHttp2Configuration &config);
@@ -232,9 +239,12 @@ Q_SIGNALS:
void errorReceived(/*@future: add as needed?*/); // Connection errors only, no stream-specific errors
void connectionClosed();
void settingsFrameReceived();
+ void pingFrameRecived(PingState state);
void errorOccurred(Http2::Http2Error errorCode, const QString &errorString);
void receivedGOAWAY(quint32 errorCode, quint32 lastStreamID);
public Q_SLOTS:
+ bool sendPing();
+ bool sendPing(QByteArrayView data);
void handleReadyRead();
void handleConnectionClosure();
@@ -295,6 +305,7 @@ private:
QHash<quint32, QPointer<QHttp2Stream>> m_streams;
QHash<QUrl, quint32> m_promisedStreams;
QVarLengthArray<quint32> m_resetStreamIDs;
+ std::optional<QByteArray> m_lastPingSignature = std::nullopt;
quint32 m_nextStreamID = 1;
// Peer's max frame size (this min is the default value