4
0
mirror of https://github.com/QuasarApp/qthttpserver.git synced 2025-05-09 08:09:33 +00:00

HTTPS support

Added new `QAbstractHttpServer::sslSetup` which enables HTTPS usage.
Added new `QSslServer` which inherits from `QTcpServer` and configures
incoming TCP clients to use SSL.

[ChangeLog][QHttpServer][Https support] Https support added to
QAbstractHttpServer class

Change-Id: I536cf48b86b246e3f4b9d960f793b93670afe06f
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
Reviewed-by: Guy Poizat <gerrit.qt@gmail.com>
Reviewed-by: Mikhail Svetkin <mikhail.svetkin@gmail.com>
This commit is contained in:
Sylvain Garcia 2019-09-26 14:27:07 +02:00
parent 73175545e6
commit f04a6809b1
13 changed files with 512 additions and 50 deletions

@ -4,6 +4,7 @@ INCLUDEPATH += .
QT = network core-private
qtHaveModule(websockets): QT += websockets-private
qtConfig(ssl): QT += sslserver
HEADERS += \
qthttpserverglobal.h \

@ -148,11 +148,20 @@ QAbstractHttpServer::QAbstractHttpServer(QAbstractHttpServerPrivate &dd, QObject
*/
int QAbstractHttpServer::listen(const QHostAddress &address, quint16 port)
{
#if QT_CONFIG(ssl)
Q_D(QAbstractHttpServer);
QTcpServer *tcpServer = d->sslEnabled ? new QSslServer(d->sslConfiguration, this)
: new QTcpServer(this);
#else
auto tcpServer = new QTcpServer(this);
#endif
const auto listening = tcpServer->listen(address, port);
if (listening) {
bind(tcpServer);
return tcpServer->serverPort();
} else {
qCCritical(lcHttpServer, "listen failed: %s",
tcpServer->errorString().toStdString().c_str());
}
delete tcpServer;
@ -254,4 +263,24 @@ QHttpServerResponder QAbstractHttpServer::makeResponder(const QHttpServerRequest
return QHttpServerResponder(request, socket);
}
#if QT_CONFIG(ssl)
void QAbstractHttpServer::sslSetup(const QSslCertificate &certificate,
const QSslKey &privateKey,
QSsl::SslProtocol protocol)
{
QSslConfiguration conf;
conf.setLocalCertificate(certificate);
conf.setPrivateKey(privateKey);
conf.setProtocol(protocol);
sslSetup(conf);
}
void QAbstractHttpServer::sslSetup(const QSslConfiguration &sslConfiguration)
{
Q_D(QAbstractHttpServer);
d->sslConfiguration = sslConfiguration;
d->sslEnabled = true;
}
#endif
QT_END_NAMESPACE

@ -36,6 +36,12 @@
#include <QtNetwork/qhostaddress.h>
#if QT_CONFIG(ssl)
#include <QtSslServer/qsslserver.h>
#include <QSslCertificate>
#include <QSslKey>
#endif
QT_BEGIN_NAMESPACE
class QHttpServerRequest;
@ -57,6 +63,12 @@ public:
void bind(QTcpServer *server = nullptr);
QVector<QTcpServer *> servers() const;
#if QT_CONFIG(ssl)
void sslSetup(const QSslCertificate &certificate, const QSslKey &privateKey,
QSsl::SslProtocol protocol = QSsl::SecureProtocols);
void sslSetup(const QSslConfiguration &sslConfiguration);
#endif
Q_SIGNALS:
void missingHandler(const QHttpServerRequest &request, QTcpSocket *socket);

@ -70,6 +70,11 @@ public:
void handleNewConnections();
void handleReadyRead(QTcpSocket *socket,
QHttpServerRequest *request);
#if QT_CONFIG(ssl)
QSslConfiguration sslConfiguration;
bool sslEnabled = false;
#endif
};
QT_END_NAMESPACE

@ -1,4 +1,11 @@
TEMPLATE = subdirs
QT = network
SUBDIRS = \
httpserver
qtConfig(ssl) {
SUBDIRS += sslserver
httpserver.depends = sslserver
}

@ -0,0 +1,73 @@
/****************************************************************************
**
** Copyright (C) 2019 Sylvain Garcia <garcia.6l20@gmail.com>.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtHttpServer module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <private/qsslserver_p.h>
#include <QtCore/qloggingcategory.h>
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcSS, "qt.sslserver");
QSslServer::QSslServer(QObject *parent):
QTcpServer (QAbstractSocket::TcpSocket, *new QSslServerPrivate, parent)
{
}
QSslServer::QSslServer(const QSslConfiguration &sslConfiguration,
QObject *parent):
QTcpServer (QAbstractSocket::TcpSocket, *new QSslServerPrivate, parent)
{
Q_D(QSslServer);
d->sslConfiguration = sslConfiguration;
}
void QSslServer::incomingConnection(qintptr handle)
{
Q_D(QSslServer);
QSslSocket *socket = new QSslSocket(this);
connect(socket, QOverload<const QList<QSslError>&>::of(&QSslSocket::sslErrors),
[this, socket](const QList<QSslError> &errors) {
for (auto &err: errors)
qCCritical(lcSS) << err;
Q_EMIT sslErrors(socket, errors);
});
socket->setSocketDescriptor(handle);
socket->setSslConfiguration(d->sslConfiguration);
socket->startServerEncryption();
addPendingConnection(socket);
}
void QSslServer::setSslConfiguration(const QSslConfiguration &sslConfiguration)
{
Q_D(QSslServer);
d->sslConfiguration = sslConfiguration;
}
QT_END_NAMESPACE

@ -0,0 +1,62 @@
/****************************************************************************
**
** Copyright (C) 2019 Sylvain Garcia <garcia.6l20@gmail.com>.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtHttpServer module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QSSLSERVER_H
#define QSSLSERVER_H
#include <QtSslServer/qtsslserverglobal.h>
#include <QtNetwork/qtcpserver.h>
#include <QtNetwork/qsslconfiguration.h>
QT_BEGIN_NAMESPACE
class QSslServerPrivate;
class Q_SSLSERVER_EXPORT QSslServer : public QTcpServer
{
Q_OBJECT
public:
QSslServer(QObject *parent = nullptr);
QSslServer(const QSslConfiguration &sslConfiguration, QObject *parent = nullptr);
void setSslConfiguration(const QSslConfiguration &sslConfiguration);
Q_SIGNALS:
void sslErrors(QSslSocket *socket, const QList<QSslError> &errors);
protected:
void incomingConnection(qintptr handle) override final;
private:
Q_DECLARE_PRIVATE(QSslServer)
};
QT_END_NAMESPACE
#endif // QSSLSERVER_HPP

@ -0,0 +1,46 @@
/****************************************************************************
**
** Copyright (C) 2019 Sylvain Garcia <garcia.6l20@gmail.com>.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtHttpServer module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QSSLSERVER_P_H
#define QSSLSERVER_P_H
#include <QtSslServer/qsslserver.h>
#include <private/qtcpserver_p.h>
QT_BEGIN_NAMESPACE
class QSslServerPrivate: public QTcpServerPrivate {
public:
QSslConfiguration sslConfiguration;
};
QT_END_NAMESPACE
#endif // QSSLSERVER_P_H

@ -0,0 +1,49 @@
/****************************************************************************
**
** Copyright (C) 2019 Sylvain Garcia <garcia.6l20@gmail.com>.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtHttpServer module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) any later version
** approved by the KDE Free Qt Foundation. The licenses are as published by
** the Free Software Foundation and appearing in the file LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QTSSLSERVERGLOBAL_H
#define QTSSLSERVERGLOBAL_H
#include <QtCore/qglobal.h>
QT_BEGIN_NAMESPACE
#ifndef QT_STATIC
# if defined(QT_BUILD_SSLSERVER_LIB)
# define Q_SSLSERVER_EXPORT Q_DECL_EXPORT
# else
# define Q_SSLSERVER_EXPORT Q_DECL_IMPORT
# endif
#else
# define Q_SSLSERVER_EXPORT
#endif
QT_END_NAMESPACE
#endif // QTSSLSERVERGLOBAL_H

@ -0,0 +1,14 @@
TARGET = QtSslServer
INCLUDEPATH += .
QT = network network-private core-private
HEADERS += \
qsslserver.h \
qtsslserverglobal.h \
qsslserver_p.h
SOURCES += \
qsslserver.cpp
load(qt_module)

@ -1,3 +1,4 @@
%modules = ( # path to module name map
"QtHttpServer" => "$basedir/src/httpserver",
"QtSslServer" => "$basedir/src/sslserver",
);

@ -1,5 +1,7 @@
TEMPLATE = subdirs
QT = network
SUBDIRS = \
cmake \
qabstracthttpserver \

@ -51,6 +51,41 @@
#include <QtNetwork/qnetworkreply.h>
#include <QtNetwork/qnetworkrequest.h>
static const char g_privateKey[] = R"(-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDykG51ZjNJra8iS27g3DJojH1qG8C3Z+Avo5U6Qz6NkOsjvr22
gXqOS4uwVUXdCAKxsP0Wwn2zGz5vxGpLPVKtbAmaqHYZuipMG/Qun3t+QYBgR+9t
lmHdI8TNP2Om8stDO5uQyBH7DcMjPyIgpfc8fBoNLhCn4oC2n6JK9EMuhQIDAQAB
AoGAUHTLzrEJjgTINI3kxz0Ck18WMl3mPG9+Ew8lbl/jnb1V4VNhReoIpq40NVbz
h28ixaG5MRVt8Dy3Jwd1YmOCylHSujdFQ2u0pcHFmERgDS2bOMwMTRoFOj2qgMGS
9SM+iXlPY5AQY8nEg7rLjMSfaC/8Hq4RXpkj4PeHh6N7AzkCQQD++HzM3xBr+Gvh
zco9Kt8IiKNlfeiA5gUQq1UPJzcWIEgW1Tgr5UzMUOcZ0HfYwhqL3+wMhzN4sba+
1plB1QRXAkEA84sfM0jm9BRSqtYTPlhsYAmuPjeo24Pxel8ijEkToAu0ppEC6AQ3
zfwZD0ISgaWQ7af5TN/RCsoNVX79twP6gwJBANbtB+Z6shERm38ARdZB6Tf8ViAb
fn4JZ4OhqVXYrKrOE3aLzYnTBGXGXMh53kytcksuOoBlB5JZ274Kj63arokCQFPo
9xMAZzJpXiImJ/MvHAfqzfH501/ukeCLrqeO9ggKgG9zPwEZkvCRj0DGjwHEPa7k
VOy7oJaLDxUJ7/iCkmkCQQCtTLsvDbGH4tyFK5VIPJbUcccIib+dTzSTeONdUxKL
Yk+C6o7OpaUWX+ikp4Ow/6iHOAgXaeA2OolDer/NspUy
-----END RSA PRIVATE KEY-----)";
static const char g_certificate[] = R"(-----BEGIN CERTIFICATE-----
MIICrjCCAhegAwIBAgIUcuXjCSkJ2+v/Rqv/UHThTRGFlpswDQYJKoZIhvcNAQEL
BQAwaDELMAkGA1UEBhMCRlIxDzANBgNVBAgMBkZyYW5jZTERMA8GA1UEBwwIR3Jl
bm9ibGUxFjAUBgNVBAoMDVF0Q29udHJpYnV0b3IxHTAbBgNVBAMMFHFodHRwc3Nl
cnZlcnRlc3QuY29tMCAXDTE5MDkyNjA4NTc1MloYDzIyNTUwMzEzMDg1NzUyWjBo
MQswCQYDVQQGEwJGUjEPMA0GA1UECAwGRnJhbmNlMREwDwYDVQQHDAhHcmVub2Js
ZTEWMBQGA1UECgwNUXRDb250cmlidXRvcjEdMBsGA1UEAwwUcWh0dHBzc2VydmVy
dGVzdC5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAPKQbnVmM0mtryJL
buDcMmiMfWobwLdn4C+jlTpDPo2Q6yO+vbaBeo5Li7BVRd0IArGw/RbCfbMbPm/E
aks9Uq1sCZqodhm6Kkwb9C6fe35BgGBH722WYd0jxM0/Y6byy0M7m5DIEfsNwyM/
IiCl9zx8Gg0uEKfigLafokr0Qy6FAgMBAAGjUzBRMB0GA1UdDgQWBBTDMYCcl2jz
UUWByEzTj5Ew/LWkeDAfBgNVHSMEGDAWgBTDMYCcl2jzUUWByEzTj5Ew/LWkeDAP
BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4GBAMNupAOXoBih6RvuAn3w
W8jOIZfkn5CMYdbUSndY/Wrt4p07M8r9uFPWG4bXSwG6n9Nzl75X9b0ka/jqPjQ3
X769simPygCblBp2xwE6w14aHEBx4kcF1p2QbC1vHynszJxyVLvHqUjuJwVAoPrM
Imy6LOiw2tRTHPsj7UH16M6C
-----END CERTIFICATE-----)";
QT_BEGIN_NAMESPACE
class QueryRequireRouterRule : public QHttpServerRouterRule
@ -101,6 +136,8 @@ private:
private:
QHttpServer httpserver;
QString urlBase;
QString sslUrlBase;
QNetworkAccessManager networkAccessManager;
};
struct CustomArg {
@ -243,7 +280,52 @@ void tst_QHttpServer::initTestCase()
return resp;
});
urlBase = QStringLiteral("http://localhost:%1%2").arg(httpserver.listen());
int port = httpserver.listen();
if (port < 0)
qCritical() << "Http server listen failed";
urlBase = QStringLiteral("http://localhost:%1%2").arg(port);
#if QT_CONFIG(ssl)
httpserver.sslSetup(QSslCertificate(g_certificate),
QSslKey(g_privateKey, QSsl::Rsa));
port = httpserver.listen();
if (port < 0)
qCritical() << "Http server listen failed";
sslUrlBase = QStringLiteral("https://localhost:%1%2").arg(port);
QList<QSslError> expectedSslErrors;
// Non-OpenSSL backends are not able to report a specific error code
// for self-signed certificates.
#ifndef QT_NO_OPENSSL
# define FLUKE_CERTIFICATE_ERROR QSslError::SelfSignedCertificate
#else
# define FLUKE_CERTIFICATE_ERROR QSslError::CertificateUntrusted
#endif
expectedSslErrors.append(QSslError(FLUKE_CERTIFICATE_ERROR,
QSslCertificate(g_certificate)));
expectedSslErrors.append(QSslError(QSslError::HostNameMismatch,
QSslCertificate(g_certificate)));
connect(&networkAccessManager, &QNetworkAccessManager::sslErrors,
[expectedSslErrors](QNetworkReply *reply,
const QList<QSslError> &errors) {
for (const auto &error: errors) {
for (const auto &expectedError: expectedSslErrors) {
if (error.error() != expectedError.error() ||
error.certificate() != expectedError.certificate()) {
qCritical() << "Got unexpected ssl error:"
<< error << error.certificate();
}
}
}
reply->ignoreSslErrors(expectedSslErrors);
});
#endif
}
void tst_QHttpServer::routeGet_data()
@ -254,177 +336,207 @@ void tst_QHttpServer::routeGet_data()
QTest::addColumn<QString>("body");
QTest::addRow("hello world")
<< "/"
<< urlBase.arg("/")
<< 200
<< "text/plain"
<< "Hello world get";
QTest::addRow("test msg")
<< "/test"
<< urlBase.arg("/test")
<< 200
<< "text/html"
<< "test msg";
QTest::addRow("not found")
<< "/not-found"
<< urlBase.arg("/not-found")
<< 404
<< "application/x-empty"
<< "";
QTest::addRow("arg:int")
<< "/page/10"
<< urlBase.arg("/page/10")
<< 200
<< "text/plain"
<< "page: 10";
QTest::addRow("arg:-int")
<< "/page/-10"
<< urlBase.arg("/page/-10")
<< 200
<< "text/plain"
<< "page: -10";
QTest::addRow("arg:uint")
<< "/page/10/detail"
<< urlBase.arg("/page/10/detail")
<< 200
<< "text/plain"
<< "page: 10 detail";
QTest::addRow("arg:-uint")
<< "/page/-10/detail"
<< urlBase.arg("/page/-10/detail")
<< 404
<< "application/x-empty"
<< "";
QTest::addRow("arg:string")
<< "/user/test"
<< urlBase.arg("/user/test")
<< 200
<< "text/plain"
<< "test";
QTest::addRow("arg:string")
<< "/user/test test ,!a+."
<< urlBase.arg("/user/test test ,!a+.")
<< 200
<< "text/plain"
<< "test test ,!a+.";
QTest::addRow("arg:string,ba")
<< "/user/james/bond"
<< urlBase.arg("/user/james/bond")
<< 200
<< "text/plain"
<< "james-bond";
QTest::addRow("arg:url")
<< "/test/api/v0/cmds?val=1"
<< urlBase.arg("/test/api/v0/cmds?val=1")
<< 200
<< "text/plain"
<< "path: api/v0/cmds";
QTest::addRow("arg:float 5.1")
<< "/api/v5.1"
<< urlBase.arg("/api/v5.1")
<< 200
<< "text/plain"
<< "api 5.1v";
QTest::addRow("arg:float 5.")
<< "/api/v5."
<< urlBase.arg("/api/v5.")
<< 200
<< "text/plain"
<< "api 5v";
QTest::addRow("arg:float 6.0")
<< "/api/v6.0"
<< urlBase.arg("/api/v6.0")
<< 200
<< "text/plain"
<< "api 6v";
QTest::addRow("arg:float,uint")
<< "/api/v5.1/user/10"
<< urlBase.arg("/api/v5.1/user/10")
<< 200
<< "text/plain"
<< "api 5.1v, user id - 10";
QTest::addRow("arg:float,uint,query")
<< "/api/v5.2/user/11/settings?role=admin" << 200
<< urlBase.arg("/api/v5.2/user/11/settings?role=admin")
<< 200
<< "text/plain"
<< "api 5.2v, user id - 11, set settings role=admin#''";
// The fragment isn't actually sent via HTTP (it's information for the user agent)
QTest::addRow("arg:float,uint, query+fragment")
<< "/api/v5.2/user/11/settings?role=admin#tag"
<< 200 << "text/plain"
<< urlBase.arg("/api/v5.2/user/11/settings?role=admin#tag")
<< 200
<< "text/plain"
<< "api 5.2v, user id - 11, set settings role=admin#''";
QTest::addRow("custom route rule")
<< "/custom/15"
<< urlBase.arg("/custom/15")
<< 404
<< "application/x-empty"
<< "";
QTest::addRow("custom route rule + query")
<< "/custom/10?key=11&g=1"
<< urlBase.arg("/custom/10?key=11&g=1")
<< 200
<< "text/plain"
<< "Custom router rule: 10, key=11";
QTest::addRow("custom route rule + query key req")
<< "/custom/10?g=1&key=12"
<< urlBase.arg("/custom/10?g=1&key=12")
<< 200
<< "text/plain"
<< "Custom router rule: 10, key=12";
QTest::addRow("post-and-get, get")
<< "/post-and-get"
<< urlBase.arg("/post-and-get")
<< 200
<< "text/plain"
<< "Hello world get";
QTest::addRow("invalid-rule-method, get")
<< "/invalid-rule-method"
<< urlBase.arg("/invalid-rule-method")
<< 404
<< "application/x-empty"
<< "";
QTest::addRow("check custom type, data=1")
<< "/check-custom-type/1"
<< urlBase.arg("/check-custom-type/1")
<< 200
<< "text/plain"
<< "data = 1";
QTest::addRow("any, get")
<< "/any"
<< urlBase.arg("/any")
<< 200
<< "text/plain"
<< "Get";
QTest::addRow("response from html file")
<< "/file/text.html"
<< urlBase.arg("/file/text.html")
<< 200
<< "text/html"
<< "<html></html>";
QTest::addRow("response from json file")
<< "/file/application.json"
<< urlBase.arg("/file/application.json")
<< 200
<< "application/json"
<< "{ \"key\": \"value\" }";
QTest::addRow("json-object")
<< "/json-object/"
<< urlBase.arg("/json-object/")
<< 200
<< "application/json"
<< "{\"property\":\"test\",\"value\":1}";
QTest::addRow("json-array")
<< "/json-array/"
<< urlBase.arg("/json-array/")
<< 200
<< "application/json"
<< "[1,\"2\",{\"name\":\"test\"}]";
QTest::addRow("chunked")
<< "/chunked/"
<< urlBase.arg("/chunked/")
<< 200
<< "text/plain"
<< "part 1 of the message, part 2 of the message";
#if QT_CONFIG(ssl)
QTest::addRow("hello world, ssl")
<< sslUrlBase.arg("/")
<< 200
<< "text/plain"
<< "Hello world get";
QTest::addRow("post-and-get, get, ssl")
<< sslUrlBase.arg("/post-and-get")
<< 200
<< "text/plain"
<< "Hello world get";
QTest::addRow("invalid-rule-method, get, ssl")
<< sslUrlBase.arg("/invalid-rule-method")
<< 404
<< "application/x-empty"
<< "";
QTest::addRow("check custom type, data=1, ssl")
<< sslUrlBase.arg("/check-custom-type/1")
<< 200
<< "text/plain"
<< "data = 1";
#endif // QT_CONFIG(ssl)
}
void tst_QHttpServer::routeGet()
@ -434,15 +546,15 @@ void tst_QHttpServer::routeGet()
QFETCH(QString, type);
QFETCH(QString, body);
QNetworkAccessManager networkAccessManager;
const QUrl requestUrl(urlBase.arg(url));
auto reply = networkAccessManager.get(QNetworkRequest(requestUrl));
auto reply = networkAccessManager.get(QNetworkRequest(url));
QTRY_VERIFY(reply->isFinished());
QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), type);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), code);
QCOMPARE(reply->readAll().trimmed(), body);
reply->deleteLater();
}
void tst_QHttpServer::routeKeepAlive()
@ -458,7 +570,6 @@ void tst_QHttpServer::routeKeepAlive()
.arg(static_cast<int>(req.method()));
});
QNetworkAccessManager networkAccessManager;
QNetworkRequest request(urlBase.arg("/keep-alive"));
request.setRawHeader(QByteArray("Connection"), QByteArray("keep-alive"));
@ -506,28 +617,28 @@ void tst_QHttpServer::routePost_data()
QTest::addColumn<QString>("body");
QTest::addRow("hello world")
<< "/"
<< urlBase.arg("/")
<< 200
<< "text/plain"
<< ""
<< "Hello world post";
QTest::addRow("post-and-get, post")
<< "/post-and-get"
<< urlBase.arg("/post-and-get")
<< 200
<< "text/plain"
<< ""
<< "Hello world post";
QTest::addRow("any, post")
<< "/any"
<< urlBase.arg("/any")
<< 200
<< "text/plain"
<< ""
<< "Post";
QTest::addRow("post-body")
<< "/post-body"
<< urlBase.arg("/post-body")
<< 200
<< "text/plain"
<< "some post data"
@ -538,11 +649,43 @@ void tst_QHttpServer::routePost_data()
body.append(QString::number(i));
QTest::addRow("post-body - huge body, chunk test")
<< "/post-body"
<< urlBase.arg("/post-body")
<< 200
<< "text/plain"
<< body
<< body;
#if QT_CONFIG(ssl)
QTest::addRow("post-and-get, post, ssl")
<< sslUrlBase.arg("/post-and-get")
<< 200
<< "text/plain"
<< ""
<< "Hello world post";
QTest::addRow("any, post, ssl")
<< sslUrlBase.arg("/any")
<< 200
<< "text/plain"
<< ""
<< "Post";
QTest::addRow("post-body, ssl")
<< sslUrlBase.arg("/post-body")
<< 200
<< "text/plain"
<< "some post data"
<< "some post data";
QTest::addRow("post-body - huge body, chunk test, ssl")
<< sslUrlBase.arg("/post-body")
<< 200
<< "text/plain"
<< body
<< body;
#endif // QT_CONFIG(ssl)
}
void tst_QHttpServer::routePost()
@ -553,8 +696,7 @@ void tst_QHttpServer::routePost()
QFETCH(QString, data);
QFETCH(QString, body);
QNetworkAccessManager networkAccessManager;
QNetworkRequest request(QUrl(urlBase.arg(url)));
QNetworkRequest request(url);
if (data.size()) {
request.setHeader(QNetworkRequest::ContentTypeHeader,
QHttpServerLiterals::contentTypeTextHtml());
@ -567,6 +709,8 @@ void tst_QHttpServer::routePost()
QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), type);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), code);
QCOMPARE(reply->readAll(), body);
reply->deleteLater();
}
void tst_QHttpServer::routeDelete_data()
@ -577,16 +721,32 @@ void tst_QHttpServer::routeDelete_data()
QTest::addColumn<QString>("data");
QTest::addRow("post-and-get, delete")
<< "/post-and-get"
<< urlBase.arg("/post-and-get")
<< 404
<< "application/x-empty"
<< "";
QTest::addRow("any, delete")
<< "/any"
<< urlBase.arg("/any")
<< 200
<< "text/plain"
<< "Delete";
#if QT_CONFIG(ssl)
QTest::addRow("post-and-get, delete, ssl")
<< sslUrlBase.arg("/post-and-get")
<< 404
<< "application/x-empty"
<< "";
QTest::addRow("any, delete, ssl")
<< sslUrlBase.arg("/any")
<< 200
<< "text/plain"
<< "Delete";
#endif // QT_CONFIG(ssl)
}
void tst_QHttpServer::routeDelete()
@ -596,19 +756,18 @@ void tst_QHttpServer::routeDelete()
QFETCH(QString, type);
QFETCH(QString, data);
QNetworkAccessManager networkAccessManager;
const QUrl requestUrl(urlBase.arg(url));
auto reply = networkAccessManager.deleteResource(QNetworkRequest(requestUrl));
auto reply = networkAccessManager.deleteResource(QNetworkRequest(url));
QTRY_VERIFY(reply->isFinished());
QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), type);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), code);
reply->deleteLater();
}
void tst_QHttpServer::routeExtraHeaders()
{
QNetworkAccessManager networkAccessManager;
const QUrl requestUrl(urlBase.arg("/extra-headers"));
auto reply = networkAccessManager.get(QNetworkRequest(requestUrl));
@ -669,8 +828,8 @@ void tst_QHttpServer::checkRouteLambdaCapture()
return msg;
});
QNetworkAccessManager networkAccessManager;
checkReply(networkAccessManager.get(QNetworkRequest(QUrl(urlBase.arg("/capture-this/")))),
checkReply(networkAccessManager.get(
QNetworkRequest(QUrl(urlBase.arg("/capture-this/")))),
urlBase);
if (QTest::currentTestFailed())
return;
@ -688,6 +847,8 @@ void tst_QHttpServer::checkReply(QNetworkReply *reply, const QString &response)
QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), "text/plain");
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), 200);
QCOMPARE(reply->readAll(), response);
reply->deleteLater();
};
QT_END_NAMESPACE