From 5147076e5ede09fafa96e147ab41dbb5744b8655 Mon Sep 17 00:00:00 2001 From: Mikhail Svetkin Date: Mon, 25 Mar 2019 16:09:50 +0100 Subject: Fix support for keep-alive connection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QAbstractHttpServer did not clear a request's internal properties (headers, url, body). If a request has a keep-alive header, QAbstractHttpServer should clear the request's internal properties. Change-Id: I2dfd0565369bd3291cd8d9900045c5a7f9d43ca3 Reviewed-by: MÃ¥rten Nordheim --- src/httpserver/qabstracthttpserver.cpp | 3 ++ src/httpserver/qhttpserverrequest.cpp | 8 ++++ src/httpserver/qhttpserverrequest_p.h | 2 + tests/auto/qhttpserver/tst_qhttpserver.cpp | 62 ++++++++++++++++++++++++++++-- 4 files changed, 72 insertions(+), 3 deletions(-) diff --git a/src/httpserver/qabstracthttpserver.cpp b/src/httpserver/qabstracthttpserver.cpp index a00bbf4..c07e687 100644 --- a/src/httpserver/qabstracthttpserver.cpp +++ b/src/httpserver/qabstracthttpserver.cpp @@ -94,6 +94,9 @@ void QAbstractHttpServerPrivate::handleReadyRead() if (!socket->isTransactionStarted()) socket->startTransaction(); + if (requestPrivate->state == QHttpServerRequestPrivate::State::OnMessageComplete) + requestPrivate->clear(); + if (!requestPrivate->parse(socket)) { socket->disconnect(); return; diff --git a/src/httpserver/qhttpserverrequest.cpp b/src/httpserver/qhttpserverrequest.cpp index f6c52dd..b92024a 100644 --- a/src/httpserver/qhttpserverrequest.cpp +++ b/src/httpserver/qhttpserverrequest.cpp @@ -116,6 +116,14 @@ uint QHttpServerRequestPrivate::headerHash(const QString &key) const return qHash(key.toLower(), headersSeed); } +void QHttpServerRequestPrivate::clear() +{ + url.clear(); + lastHeader.clear(); + headers.clear(); + body.clear(); +} + bool QHttpServerRequestPrivate::parseUrl(const char *at, size_t length, bool connect, QUrl *url) { static const std::map> functions { diff --git a/src/httpserver/qhttpserverrequest_p.h b/src/httpserver/qhttpserverrequest_p.h index e128663..0a493f0 100644 --- a/src/httpserver/qhttpserverrequest_p.h +++ b/src/httpserver/qhttpserverrequest_p.h @@ -85,6 +85,8 @@ public: const uint headersSeed = uint(qGlobalQHashSeed()); uint headerHash(const QString &key) const; + void clear(); + private: static http_parser_settings httpParserSettings; static bool parseUrl(const char *at, size_t length, bool connect, QUrl *url); diff --git a/tests/auto/qhttpserver/tst_qhttpserver.cpp b/tests/auto/qhttpserver/tst_qhttpserver.cpp index aeb3a92..9fb33d1 100644 --- a/tests/auto/qhttpserver/tst_qhttpserver.cpp +++ b/tests/auto/qhttpserver/tst_qhttpserver.cpp @@ -84,6 +84,7 @@ private slots: void initTestCase(); void routeGet_data(); void routeGet(); + void routeKeepAlive(); void routePost_data(); void routePost(); void routeDelete_data(); @@ -180,9 +181,6 @@ void tst_QHttpServer::initTestCase() .arg(request.query().queryItemValue("key")); }); - urlBase = QStringLiteral("http://localhost:%1%2").arg(httpserver.listen()); - - httpserver.router()->addConverter(QLatin1String("[+-]?\\d+")); httpserver.route("/check-custom-type/", [] (const CustomArg &customArg) { return QString("data = %1").arg(customArg.data); @@ -364,6 +362,64 @@ void tst_QHttpServer::routeGet() QCOMPARE(reply->readAll(), body); } +void tst_QHttpServer::routeKeepAlive() +{ + httpserver.route("/keep-alive", [] (const QHttpServerRequest &req) -> QHttpServerResponse { + if (req.headers()["Connection"] != "keep-alive") + return QHttpServerResponse::StatusCode::NotFound; + + return QString("header: %1, query: %2, body: %3, method: %4") + .arg(req.value("CustomHeader"), + req.url().query(), + req.body()) + .arg(static_cast(req.method())); + }); + + QNetworkAccessManager networkAccessManager; + QNetworkRequest request(urlBase.arg("/keep-alive")); + request.setRawHeader(QByteArray("Connection"), QByteArray("keep-alive")); + + auto checkReply = [] (QNetworkReply *reply, const QString &response) { + QTRY_VERIFY(reply->isFinished()); + + QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), "text/html"); + QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200); + QCOMPARE(reply->readAll(), response); + }; + + checkReply(networkAccessManager.get(request), + QString("header: , query: , body: , method: %1") + .arg(static_cast(QHttpServerRequest::Method::Get))); + if (QTest::currentTestFailed()) + return; + + request.setUrl(urlBase.arg("/keep-alive?po=98")); + request.setRawHeader("CustomHeader", "1"); + request.setHeader(QNetworkRequest::ContentTypeHeader, "text/html"); + + checkReply(networkAccessManager.post(request, QByteArray("test")), + QString("header: 1, query: po=98, body: test, method: %1") + .arg(static_cast(QHttpServerRequest::Method::Post))); + if (QTest::currentTestFailed()) + return; + + request = QNetworkRequest(urlBase.arg("/keep-alive")); + request.setRawHeader(QByteArray("Connection"), QByteArray("keep-alive")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "text/html"); + + checkReply(networkAccessManager.post(request, QByteArray("")), + QString("header: , query: , body: , method: %1") + .arg(static_cast(QHttpServerRequest::Method::Post))); + if (QTest::currentTestFailed()) + return; + + checkReply(networkAccessManager.get(request), + QString("header: , query: , body: , method: %1") + .arg(static_cast(QHttpServerRequest::Method::Get))); + if (QTest::currentTestFailed()) + return; +} + void tst_QHttpServer::routePost_data() { QTest::addColumn("url"); -- cgit v1.2.3