Fix support for keep-alive connection

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 <marten.nordheim@qt.io>
This commit is contained in:
Mikhail Svetkin 2019-03-25 16:09:50 +01:00
parent be06bd66b8
commit 5147076e5e
4 changed files with 72 additions and 3 deletions

View File

@ -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;

View File

@ -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<std::size_t, std::function<void(const QString &, QUrl *)>> functions {

View File

@ -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);

View File

@ -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<CustomArg>(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<int>(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<int>(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<int>(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<int>(QHttpServerRequest::Method::Post)));
if (QTest::currentTestFailed())
return;
checkReply(networkAccessManager.get(request),
QString("header: , query: , body: , method: %1")
.arg(static_cast<int>(QHttpServerRequest::Method::Get)));
if (QTest::currentTestFailed())
return;
}
void tst_QHttpServer::routePost_data()
{
QTest::addColumn<QString>("url");