diff options
author | Sylvain Garcia <garcia.6l20@gmail.com> | 2019-09-26 14:27:07 +0200 |
---|---|---|
committer | Sylvain Garcia <garcia.6l20@gmail.com> | 2019-10-04 17:16:09 +0200 |
commit | f04a6809b1d83bb7c0ae0f42251d3a31510f1be9 (patch) | |
tree | 37ba38933e9d671faab80980df06190809b61daa | |
parent | 73175545e69cc5f07a7a1447a6b8c4c74d9795c8 (diff) |
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>
-rw-r--r-- | src/httpserver/httpserver.pro | 1 | ||||
-rw-r--r-- | src/httpserver/qabstracthttpserver.cpp | 29 | ||||
-rw-r--r-- | src/httpserver/qabstracthttpserver.h | 12 | ||||
-rw-r--r-- | src/httpserver/qabstracthttpserver_p.h | 5 | ||||
-rw-r--r-- | src/src.pro | 7 | ||||
-rw-r--r-- | src/sslserver/qsslserver.cpp | 73 | ||||
-rw-r--r-- | src/sslserver/qsslserver.h | 62 | ||||
-rw-r--r-- | src/sslserver/qsslserver_p.h | 46 | ||||
-rw-r--r-- | src/sslserver/qtsslserverglobal.h | 49 | ||||
-rw-r--r-- | src/sslserver/sslserver.pro | 14 | ||||
-rw-r--r-- | sync.profile | 1 | ||||
-rw-r--r-- | tests/auto/auto.pro | 2 | ||||
-rw-r--r-- | tests/auto/qhttpserver/tst_qhttpserver.cpp | 261 |
13 files changed, 512 insertions, 50 deletions
diff --git a/src/httpserver/httpserver.pro b/src/httpserver/httpserver.pro index 1ba716b..20f567f 100644 --- a/src/httpserver/httpserver.pro +++ b/src/httpserver/httpserver.pro @@ -4,6 +4,7 @@ INCLUDEPATH += . QT = network core-private qtHaveModule(websockets): QT += websockets-private +qtConfig(ssl): QT += sslserver HEADERS += \ qthttpserverglobal.h \ diff --git a/src/httpserver/qabstracthttpserver.cpp b/src/httpserver/qabstracthttpserver.cpp index 1ccd91d..26e6e97 100644 --- a/src/httpserver/qabstracthttpserver.cpp +++ b/src/httpserver/qabstracthttpserver.cpp @@ -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 diff --git a/src/httpserver/qabstracthttpserver.h b/src/httpserver/qabstracthttpserver.h index 6b59115..ffcdc2a 100644 --- a/src/httpserver/qabstracthttpserver.h +++ b/src/httpserver/qabstracthttpserver.h @@ -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); diff --git a/src/httpserver/qabstracthttpserver_p.h b/src/httpserver/qabstracthttpserver_p.h index 69732ff..2c02b19 100644 --- a/src/httpserver/qabstracthttpserver_p.h +++ b/src/httpserver/qabstracthttpserver_p.h @@ -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 diff --git a/src/src.pro b/src/src.pro index 68d9ed3..7a4c276 100644 --- a/src/src.pro +++ b/src/src.pro @@ -1,4 +1,11 @@ TEMPLATE = subdirs +QT = network + SUBDIRS = \ httpserver + +qtConfig(ssl) { + SUBDIRS += sslserver + httpserver.depends = sslserver +} diff --git a/src/sslserver/qsslserver.cpp b/src/sslserver/qsslserver.cpp new file mode 100644 index 0000000..b22cadb --- /dev/null +++ b/src/sslserver/qsslserver.cpp @@ -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 diff --git a/src/sslserver/qsslserver.h b/src/sslserver/qsslserver.h new file mode 100644 index 0000000..13b01d1 --- /dev/null +++ b/src/sslserver/qsslserver.h @@ -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 diff --git a/src/sslserver/qsslserver_p.h b/src/sslserver/qsslserver_p.h new file mode 100644 index 0000000..4556c3d --- /dev/null +++ b/src/sslserver/qsslserver_p.h @@ -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 diff --git a/src/sslserver/qtsslserverglobal.h b/src/sslserver/qtsslserverglobal.h new file mode 100644 index 0000000..8f9b55d --- /dev/null +++ b/src/sslserver/qtsslserverglobal.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 diff --git a/src/sslserver/sslserver.pro b/src/sslserver/sslserver.pro new file mode 100644 index 0000000..6d09233 --- /dev/null +++ b/src/sslserver/sslserver.pro @@ -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) diff --git a/sync.profile b/sync.profile index b4dfb24..80eeaf9 100644 --- a/sync.profile +++ b/sync.profile @@ -1,3 +1,4 @@ %modules = ( # path to module name map "QtHttpServer" => "$basedir/src/httpserver", + "QtSslServer" => "$basedir/src/sslserver", ); diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro index 473a337..af38d7a 100644 --- a/tests/auto/auto.pro +++ b/tests/auto/auto.pro @@ -1,5 +1,7 @@ TEMPLATE = subdirs +QT = network + SUBDIRS = \ cmake \ qabstracthttpserver \ diff --git a/tests/auto/qhttpserver/tst_qhttpserver.cpp b/tests/auto/qhttpserver/tst_qhttpserver.cpp index a0a437c..ef5290e 100644 --- a/tests/auto/qhttpserver/tst_qhttpserver.cpp +++ b/tests/auto/qhttpserver/tst_qhttpserver.cpp @@ -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 |