summaryrefslogtreecommitdiffstats
path: root/tests/auto/network/access/http2/http2srv.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tests/auto/network/access/http2/http2srv.cpp')
-rwxr-xr-x[-rw-r--r--]tests/auto/network/access/http2/http2srv.cpp183
1 files changed, 106 insertions, 77 deletions
diff --git a/tests/auto/network/access/http2/http2srv.cpp b/tests/auto/network/access/http2/http2srv.cpp
index eb09569cdd..f919937fc3 100644..100755
--- a/tests/auto/network/access/http2/http2srv.cpp
+++ b/tests/auto/network/access/http2/http2srv.cpp
@@ -33,9 +33,14 @@
#include "http2srv.h"
+#ifndef QT_NO_SSL
#include <QtNetwork/qsslconfiguration.h>
-#include <QtNetwork/qhostaddress.h>
+#include <QtNetwork/qsslsocket.h>
#include <QtNetwork/qsslkey.h>
+#endif
+
+#include <QtNetwork/qtcpsocket.h>
+
#include <QtCore/qdebug.h>
#include <QtCore/qlist.h>
#include <QtCore/qfile.h>
@@ -60,8 +65,9 @@ inline bool is_valid_client_stream(quint32 streamID)
}
-Http2Server::Http2Server(const Http2Settings &ss, const Http2Settings &cs)
- : serverSettings(ss)
+Http2Server::Http2Server(bool h2c, const Http2Settings &ss, const Http2Settings &cs)
+ : serverSettings(ss),
+ clearTextHTTP2(h2c)
{
for (const auto &s : cs)
expectedClientSettings[quint16(s.identifier)] = s.value;
@@ -97,6 +103,11 @@ void Http2Server::setResponseBody(const QByteArray &body)
void Http2Server::startServer()
{
+#ifdef QT_NO_SSL
+ // Let the test fail with timeout.
+ if (!clearTextHTTP2)
+ return;
+#endif
if (listen())
emit serverStarted(serverPort());
}
@@ -109,14 +120,14 @@ void Http2Server::sendServerSettings()
if (!serverSettings.size())
return;
- outboundFrame.start(FrameType::SETTINGS, FrameFlag::EMPTY, connectionStreamID);
+ writer.start(FrameType::SETTINGS, FrameFlag::EMPTY, connectionStreamID);
for (const auto &s : serverSettings) {
- outboundFrame.append(s.identifier);
- outboundFrame.append(s.value);
+ writer.append(s.identifier);
+ writer.append(s.value);
if (s.identifier == Settings::INITIAL_WINDOW_SIZE_ID)
streamRecvWindowSize = s.value;
}
- outboundFrame.write(*socket);
+ writer.write(*socket);
// Now, let's update our peer on a session recv window size:
const quint32 updatedSize = 10 * streamRecvWindowSize;
if (sessionRecvWindowSize < updatedSize) {
@@ -134,19 +145,19 @@ void Http2Server::sendGOAWAY(quint32 streamID, quint32 error, quint32 lastStream
{
Q_ASSERT(socket);
- outboundFrame.start(FrameType::GOAWAY, FrameFlag::EMPTY, streamID);
- outboundFrame.append(lastStreamID);
- outboundFrame.append(error);
- outboundFrame.write(*socket);
+ writer.start(FrameType::GOAWAY, FrameFlag::EMPTY, streamID);
+ writer.append(lastStreamID);
+ writer.append(error);
+ writer.write(*socket);
}
void Http2Server::sendRST_STREAM(quint32 streamID, quint32 error)
{
Q_ASSERT(socket);
- outboundFrame.start(FrameType::RST_STREAM, FrameFlag::EMPTY, streamID);
- outboundFrame.append(error);
- outboundFrame.write(*socket);
+ writer.start(FrameType::RST_STREAM, FrameFlag::EMPTY, streamID);
+ writer.append(error);
+ writer.write(*socket);
}
void Http2Server::sendDATA(quint32 streamID, quint32 windowSize)
@@ -160,19 +171,17 @@ void Http2Server::sendDATA(quint32 streamID, quint32 windowSize)
Q_ASSERT(offset < quint32(responseBody.size()));
const quint32 bytes = std::min<quint32>(windowSize, responseBody.size() - offset);
- outboundFrame.start(FrameType::DATA, FrameFlag::EMPTY, streamID);
+ const quint32 frameSizeLimit(clientSetting(Settings::MAX_FRAME_SIZE_ID, Http2::maxFrameSize));
+ const uchar *src = reinterpret_cast<const uchar *>(responseBody.constData() + offset);
const bool last = offset + bytes == quint32(responseBody.size());
- const quint32 frameSizeLimit(clientSetting(Settings::MAX_FRAME_SIZE_ID, Http2::maxFrameSize));
- outboundFrame.writeDATA(*socket, frameSizeLimit,
- reinterpret_cast<const uchar *>(responseBody.constData() + offset),
- bytes);
+ writer.start(FrameType::DATA, FrameFlag::EMPTY, streamID);
+ writer.writeDATA(*socket, frameSizeLimit, src, bytes);
if (last) {
- outboundFrame.start(FrameType::DATA, FrameFlag::END_STREAM, streamID);
- outboundFrame.setPayloadSize(0);
- outboundFrame.write(*socket);
-
+ writer.start(FrameType::DATA, FrameFlag::END_STREAM, streamID);
+ writer.setPayloadSize(0);
+ writer.write(*socket);
suspendedStreams.erase(it);
activeRequests.erase(streamID);
@@ -187,38 +196,52 @@ void Http2Server::sendWINDOW_UPDATE(quint32 streamID, quint32 delta)
{
Q_ASSERT(socket);
- outboundFrame.start(FrameType::WINDOW_UPDATE, FrameFlag::EMPTY, streamID);
- outboundFrame.append(delta);
- outboundFrame.write(*socket);
+ writer.start(FrameType::WINDOW_UPDATE, FrameFlag::EMPTY, streamID);
+ writer.append(delta);
+ writer.write(*socket);
}
void Http2Server::incomingConnection(qintptr socketDescriptor)
{
- socket.reset(new QSslSocket);
- // Add HTTP2 as supported protocol:
- auto conf = QSslConfiguration::defaultConfiguration();
- auto protos = conf.allowedNextProtocols();
- protos.prepend(QSslConfiguration::ALPNProtocolHTTP2);
- conf.setAllowedNextProtocols(protos);
- socket->setSslConfiguration(conf);
- // SSL-related setup ...
- socket->setPeerVerifyMode(QSslSocket::VerifyNone);
- socket->setProtocol(QSsl::TlsV1_2OrLater);
- connect(socket.data(), SIGNAL(sslErrors(QList<QSslError>)),
- this, SLOT(ignoreErrorSlot()));
- QFile file(SRCDIR "certs/fluke.key");
- file.open(QIODevice::ReadOnly);
- QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
- socket->setPrivateKey(key);
- auto localCert = QSslCertificate::fromPath(SRCDIR "certs/fluke.cert");
- socket->setLocalCertificateChain(localCert);
- socket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState);
- // Stop listening.
- close();
- // Start SSL handshake and ALPN:
- connect(socket.data(), SIGNAL(encrypted()),
- this, SLOT(connectionEncrypted()));
- socket->startServerEncryption();
+ if (clearTextHTTP2) {
+ socket.reset(new QTcpSocket);
+ const bool set = socket->setSocketDescriptor(socketDescriptor);
+ Q_UNUSED(set) Q_ASSERT(set);
+ // Stop listening:
+ close();
+ QMetaObject::invokeMethod(this, "connectionEstablished",
+ Qt::QueuedConnection);
+ } else {
+#ifndef QT_NO_SSL
+ socket.reset(new QSslSocket);
+ QSslSocket *sslSocket = static_cast<QSslSocket *>(socket.data());
+ // Add HTTP2 as supported protocol:
+ auto conf = QSslConfiguration::defaultConfiguration();
+ auto protos = conf.allowedNextProtocols();
+ protos.prepend(QSslConfiguration::ALPNProtocolHTTP2);
+ conf.setAllowedNextProtocols(protos);
+ sslSocket->setSslConfiguration(conf);
+ // SSL-related setup ...
+ sslSocket->setPeerVerifyMode(QSslSocket::VerifyNone);
+ sslSocket->setProtocol(QSsl::TlsV1_2OrLater);
+ connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)),
+ this, SLOT(ignoreErrorSlot()));
+ QFile file(SRCDIR "certs/fluke.key");
+ file.open(QIODevice::ReadOnly);
+ QSslKey key(file.readAll(), QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);
+ sslSocket->setPrivateKey(key);
+ auto localCert = QSslCertificate::fromPath(SRCDIR "certs/fluke.cert");
+ sslSocket->setLocalCertificateChain(localCert);
+ sslSocket->setSocketDescriptor(socketDescriptor, QAbstractSocket::ConnectedState);
+ // Stop listening.
+ close();
+ // Start SSL handshake and ALPN:
+ connect(sslSocket, SIGNAL(encrypted()), this, SLOT(connectionEstablished()));
+ sslSocket->startServerEncryption();
+#else
+ Q_UNREACHABLE();
+#endif
+ }
}
quint32 Http2Server::clientSetting(Http2::Settings identifier, quint32 defaultValue)
@@ -229,7 +252,7 @@ quint32 Http2Server::clientSetting(Http2::Settings identifier, quint32 defaultVa
return defaultValue;
}
-void Http2Server::connectionEncrypted()
+void Http2Server::connectionEstablished()
{
using namespace Http2;
@@ -250,7 +273,9 @@ void Http2Server::connectionEncrypted()
void Http2Server::ignoreErrorSlot()
{
- socket->ignoreSslErrors();
+#ifndef QT_NO_SSL
+ static_cast<QSslSocket *>(socket.data())->ignoreSslErrors();
+#endif
}
// Now HTTP2 "server" part:
@@ -273,7 +298,7 @@ void Http2Server::readReady()
if (waitingClientPreface) {
handleConnectionPreface();
} else {
- const auto status = inboundFrame.read(*socket);
+ const auto status = reader.read(*socket);
switch (status) {
case FrameStatus::incompleteFrame:
break;
@@ -324,9 +349,11 @@ void Http2Server::handleIncomingFrame()
// 7. RST_STREAM
// 8. GOAWAY
+ inboundFrame = std::move(reader.inboundFrame());
+
if (continuedRequest.size()) {
- if (inboundFrame.type != FrameType::CONTINUATION ||
- inboundFrame.streamID != continuedRequest.front().streamID) {
+ if (inboundFrame.type() != FrameType::CONTINUATION ||
+ inboundFrame.streamID() != continuedRequest.front().streamID()) {
sendGOAWAY(connectionStreamID, PROTOCOL_ERROR, connectionStreamID);
emit invalidFrame();
connectionError = true;
@@ -334,7 +361,7 @@ void Http2Server::handleIncomingFrame()
}
}
- switch (inboundFrame.type) {
+ switch (inboundFrame.type()) {
case FrameType::SETTINGS:
handleSETTINGS();
break;
@@ -366,9 +393,9 @@ void Http2Server::handleSETTINGS()
{
// SETTINGS is either a part of the connection preface,
// or a SETTINGS ACK.
- Q_ASSERT(inboundFrame.type == FrameType::SETTINGS);
+ Q_ASSERT(inboundFrame.type() == FrameType::SETTINGS);
- if (inboundFrame.flags.testFlag(FrameFlag::ACK)) {
+ if (inboundFrame.flags().testFlag(FrameFlag::ACK)) {
if (!waitingClientAck || inboundFrame.dataSize()) {
emit invalidFrame();
connectionError = true;
@@ -409,17 +436,17 @@ void Http2Server::handleSETTINGS()
}
// Send SETTINGS ACK:
- outboundFrame.start(FrameType::SETTINGS, FrameFlag::ACK, connectionStreamID);
- outboundFrame.write(*socket);
+ writer.start(FrameType::SETTINGS, FrameFlag::ACK, connectionStreamID);
+ writer.write(*socket);
waitingClientSettings = false;
emit clientPrefaceOK();
}
void Http2Server::handleDATA()
{
- Q_ASSERT(inboundFrame.type == FrameType::DATA);
+ Q_ASSERT(inboundFrame.type() == FrameType::DATA);
- const auto streamID = inboundFrame.streamID;
+ const auto streamID = inboundFrame.streamID();
if (!is_valid_client_stream(streamID) ||
closedStreams.find(streamID) != closedStreams.end()) {
@@ -429,7 +456,8 @@ void Http2Server::handleDATA()
return;
}
- if (sessionCurrRecvWindow < inboundFrame.payloadSize) {
+ const auto payloadSize = inboundFrame.payloadSize();
+ if (sessionCurrRecvWindow < payloadSize) {
// Client does not respect our session window size!
emit invalidRequest(streamID);
connectionError = true;
@@ -441,20 +469,21 @@ void Http2Server::handleDATA()
if (it == streamWindows.end())
it = streamWindows.insert(std::make_pair(streamID, streamRecvWindowSize)).first;
- if (it->second < inboundFrame.payloadSize) {
+
+ if (it->second < payloadSize) {
emit invalidRequest(streamID);
connectionError = true;
sendGOAWAY(connectionStreamID, FLOW_CONTROL_ERROR, connectionStreamID);
return;
}
- it->second -= inboundFrame.payloadSize;
+ it->second -= payloadSize;
if (it->second < streamRecvWindowSize / 2) {
sendWINDOW_UPDATE(streamID, streamRecvWindowSize / 2);
it->second += streamRecvWindowSize / 2;
}
- sessionCurrRecvWindow -= inboundFrame.payloadSize;
+ sessionCurrRecvWindow -= payloadSize;
if (sessionCurrRecvWindow < sessionRecvWindowSize / 2) {
// This is some quite naive and trivial logic on when to update.
@@ -463,7 +492,7 @@ void Http2Server::handleDATA()
sessionCurrRecvWindow += sessionRecvWindowSize / 2;
}
- if (inboundFrame.flags.testFlag(FrameFlag::END_STREAM)) {
+ if (inboundFrame.flags().testFlag(FrameFlag::END_STREAM)) {
closedStreams.insert(streamID); // Enter "half-closed remote" state.
streamWindows.erase(it);
emit receivedData(streamID);
@@ -472,7 +501,7 @@ void Http2Server::handleDATA()
void Http2Server::handleWINDOW_UPDATE()
{
- const auto streamID = inboundFrame.streamID;
+ const auto streamID = inboundFrame.streamID();
if (!streamID) // We ignore this for now to keep things simple.
return;
@@ -502,9 +531,9 @@ void Http2Server::sendResponse(quint32 streamID, bool emptyBody)
{
Q_ASSERT(activeRequests.find(streamID) != activeRequests.end());
- outboundFrame.start(FrameType::HEADERS, FrameFlag::END_HEADERS, streamID);
+ writer.start(FrameType::HEADERS, FrameFlag::END_HEADERS, streamID);
if (emptyBody)
- outboundFrame.addFlag(FrameFlag::END_STREAM);
+ writer.addFlag(FrameFlag::END_STREAM);
HttpHeader header = {{":status", "200"}};
if (!emptyBody) {
@@ -512,13 +541,13 @@ void Http2Server::sendResponse(quint32 streamID, bool emptyBody)
QString("%1").arg(responseBody.size()).toLatin1()));
}
- HPack::BitOStream ostream(outboundFrame.rawFrameBuffer());
+ HPack::BitOStream ostream(writer.outboundFrame().buffer);
const bool result = encoder.encodeResponse(ostream, header);
Q_ASSERT(result);
Q_UNUSED(result)
const quint32 maxFrameSize(clientSetting(Settings::MAX_FRAME_SIZE_ID, Http2::maxFrameSize));
- outboundFrame.writeHEADERS(*socket, maxFrameSize);
+ writer.writeHEADERS(*socket, maxFrameSize);
if (!emptyBody) {
Q_ASSERT(suspendedStreams.find(streamID) == suspendedStreams.end());
@@ -538,7 +567,7 @@ void Http2Server::processRequest()
{
Q_ASSERT(continuedRequest.size());
- if (!continuedRequest.back().flags.testFlag(FrameFlag::END_HEADERS))
+ if (!continuedRequest.back().flags().testFlag(FrameFlag::END_HEADERS))
return;
// We test here:
@@ -546,7 +575,7 @@ void Http2Server::processRequest()
// 2. has priority set and dependency (it's 0x0 at the moment).
// 3. header can be decompressed.
const auto &headersFrame = continuedRequest.front();
- const auto streamID = headersFrame.streamID;
+ const auto streamID = headersFrame.streamID();
if (!is_valid_client_stream(streamID)) {
emit invalidRequest(streamID);
connectionError = true;
@@ -600,12 +629,12 @@ void Http2Server::processRequest()
return;
}
- continuedRequest.clear();
// Actually, if needed, we can do a comparison here.
activeRequests[streamID] = decoder.decodedHeader();
- if (headersFrame.flags.testFlag(FrameFlag::END_STREAM))
+ if (headersFrame.flags().testFlag(FrameFlag::END_STREAM))
emit receivedRequest(streamID);
// else - we're waiting for incoming DATA frames ...
+ continuedRequest.clear();
}
QT_END_NAMESPACE