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

Introduce QHttpServer

QHttpServer provides a simplified interface to QAbstractHttpServer and
QHttpServerRouter.

Change-Id: I884204aa8140bbab4c0d6a8ab4c798e0b2470e29
Reviewed-by: Paul Wicking <paul.wicking@qt.io>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
This commit is contained in:
Mikhail Svetkin 2018-10-30 16:23:23 +01:00
parent a856556784
commit 540daf882f
17 changed files with 1238 additions and 0 deletions

4
examples/examples.pro Normal file

@ -0,0 +1,4 @@
TEMPLATE = subdirs
SUBDIRS = \
httpserver

@ -0,0 +1,5 @@
TEMPLATE = subdirs
SUBDIRS = \
simple

@ -0,0 +1,118 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtHttpServer module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** 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.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtCore>
#include <QtHttpServer>
static inline QString host(const QHttpServerRequest &request)
{
return request.headers()[QStringLiteral("Host")].toString();
}
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
QHttpServer httpServer;
httpServer.route("/", []() {
return "Hello world";
});
httpServer.route("/query", [] (const QHttpServerRequest &request) {
return QString("%1/query/").arg(host(request));
});
httpServer.route("/query/", [] (qint32 id, const QHttpServerRequest &request) {
return QString("%1/query/%2").arg(host(request)).arg(id);
});
httpServer.route("/query/<arg>/log", [] (qint32 id, const QHttpServerRequest &request) {
return QString("%1/query/%2/log").arg(host(request)).arg(id);
});
httpServer.route("/query/<arg>/log/", [] (qint32 id, float threshold,
const QHttpServerRequest &request) {
return QString("%1/query/%2/log/%3").arg(host(request)).arg(id).arg(threshold);
});
httpServer.route("/user/", [] (const qint32 id) {
return QString("User %1").arg(id);
});
httpServer.route("/user/<arg>/detail", [] (const qint32 id) {
return QString("User %1 detail").arg(id);
});
httpServer.route("/user/<arg>/detail/", [] (const qint32 id, const qint32 year) {
return QString("User %1 detail year - %2").arg(id).arg(year);
});
httpServer.route("/json/", [] {
return QJsonObject{
{
{"key1", "1"},
{"key2", "2"},
{"key3", "3"}
}
};
});
const auto port = httpServer.listen(QHostAddress::Any);
if (port == -1) {
qDebug() << QCoreApplication::translate(
"QHttpServerExample", "Could not run on http://127.0.0.1:%1/").arg(port);
return 0;
}
qDebug() << QCoreApplication::translate(
"QHttpServerExample", "Running on http://127.0.0.1:%1/ (Press CTRL+C to quit)").arg(port);
return app.exec();
}

@ -0,0 +1,11 @@
requires(qtHaveModule(httpserver))
TEMPLATE = app
QT += httpserver
SOURCES += \
main.cpp
target.path = $$[QT_INSTALL_EXAMPLES]/httpserver/simple
INSTALLS += target

@ -9,11 +9,15 @@ HEADERS += \
qthttpserverglobal.h \
qabstracthttpserver.h \
qabstracthttpserver_p.h \
qhttpserver.h \
qhttpserver_p.h \
qhttpserverhelpers.h \
qhttpserverrequest.h \
qhttpserverrequest_p.h \
qhttpserverresponder.h \
qhttpserverresponder_p.h \
qhttpserverresponse.h \
qhttpserverresponse_p.h \
qhttpserverrouter.h \
qhttpserverrouter_p.h \
qhttpserverrouterrule.h \
@ -22,8 +26,10 @@ HEADERS += \
SOURCES += \
qabstracthttpserver.cpp \
qhttpserver.cpp \
qhttpserverrequest.cpp \
qhttpserverresponder.cpp \
qhttpserverresponse.cpp \
qhttpserverrouter.cpp \
qhttpserverrouterrule.cpp

@ -0,0 +1,150 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** $QT_BEGIN_LICENSE:LGPL$
** 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 Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or 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.GPL2 and 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-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtHttpServer/qhttpserver.h>
#include <QtHttpServer/qhttpserverrequest.h>
#include <QtHttpServer/qhttpserverresponder.h>
#include <QtHttpServer/qhttpserverresponse.h>
#include <private/qhttpserver_p.h>
#include <QtCore/qloggingcategory.h>
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcHS, "qt.httpserver");
/*!
\class QHttpServer
\brief QHttpServer is a simplified API for QAbstractHttpServer and QHttpServerRouter.
\code
QHttpServer server;
server.route("/", [] () {
return "hello world";
});
server.listen();
\endcode
*/
QHttpServer::QHttpServer(QObject *parent)
: QAbstractHttpServer(*new QHttpServerPrivate, parent)
{
connect(this, &QAbstractHttpServer::missingHandler, this,
[=] (const QHttpServerRequest &request, QTcpSocket *socket) {
qCDebug(lcHS) << tr("missing handler:") << request.url().path();
sendResponse(
QHttpServerResponse(QHttpServerResponder::StatusCode::NotFound), request, socket);
});
}
/*! \fn template<typename Rule = QHttpServerRouterRule, typename ... Args> bool route(Args && ... args)
This function is just a wrapper to simplify the router API.
This function takes variadic arguments. The last argument is \c a callback (ViewHandler).
The remaining arguments are used to create a new \a Rule (the default is QHttpServerRouterRule).
This is in turn added to the QHttpServerRouter.
\c ViewHandler can only be a lambda. The lambda definition can take an optional special argument,
either \c {const QHttpServerRequest&} or \c {QHttpServerResponder&&}.
This special argument must be the last in the parameter list.
Examples:
\code
QHttpServer server;
// Valid:
server.route("test", [] (const int page) { return ""; });
server.route("test", [] (const int page, const QHttpServerRequest &request) { return ""; });
server.route("test", [] (QHttpServerResponder &&responder) { return ""; });
// Invalid (compile time error):
server.route("test", [] (const QHttpServerRequest &request, const int page) { return ""; }); // request must be last
server.route("test", [] (QHttpServerRequest &request) { return ""; }); // request must be passed by const reference
server.route("test", [] (QHttpServerResponder &responder) { return ""; }); // responder must be passed by universal reference
\endcode
\sa QHttpServerRouter::addRule
*/
/*!
Destroys a QHttpServer.
*/
QHttpServer::~QHttpServer()
{
}
/*!
Returns the router object.
*/
QHttpServerRouter *QHttpServer::router()
{
Q_D(QHttpServer);
return &d->router;
}
/*!
\internal
*/
void QHttpServer::sendResponse(const QHttpServerResponse &response,
const QHttpServerRequest &request,
QTcpSocket *socket)
{
auto responder = makeResponder(request, socket);
responder.write(response.data(),
response.mimeType(),
response.statusCode());
}
/*!
\internal
*/
bool QHttpServer::handleRequest(const QHttpServerRequest &request, QTcpSocket *socket)
{
Q_D(QHttpServer);
return d->router.handleRequest(request, socket);
}
QT_END_NAMESPACE

@ -0,0 +1,144 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtHttpServer module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** 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 Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or 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.GPL2 and 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-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QHTTPSERVER_H
#define QHTTPSERVER_H
#include <QtHttpServer/qabstracthttpserver.h>
#include <QtHttpServer/qhttpserverrouter.h>
#include <QtHttpServer/qhttpserverrouterrule.h>
#include <QtHttpServer/qhttpserverresponse.h>
#include <tuple>
QT_BEGIN_NAMESPACE
class QHttpServerPrivate;
class Q_HTTPSERVER_EXPORT QHttpServer final : public QAbstractHttpServer
{
Q_OBJECT
Q_DECLARE_PRIVATE(QHttpServer)
template<int I, typename ... Ts>
struct VariadicTypeAt { using Type = typename std::tuple_element<I, std::tuple<Ts...>>::type; };
template<typename ... Ts>
struct VariadicTypeLast {
using Type = typename VariadicTypeAt<sizeof ... (Ts) - 1, Ts...>::Type;
};
public:
explicit QHttpServer(QObject *parent = nullptr);
~QHttpServer();
QHttpServerRouter *router();
template<typename Rule = QHttpServerRouterRule, typename ... Args>
bool route(Args && ... args)
{
using ViewHandler = typename VariadicTypeLast<Args...>::Type;
using ViewTraits = QHttpServerRouterViewTraits<ViewHandler>;
static_assert(ViewTraits::Arguments::compileCheck(),
"ViewHandler arguments are in the wrong order or not supported");
return routeHelper<Rule, ViewHandler, ViewTraits>(
QtPrivate::makeIndexSequence<sizeof ... (Args) - 1>{},
std::forward<Args>(args)...);
}
private:
template<typename Rule, typename ViewHandler, typename ViewTraits, int ... I, typename ... Args>
bool routeHelper(QtPrivate::IndexesList<I...>, Args &&... args)
{
return routeImpl<Rule,
ViewHandler,
ViewTraits,
typename VariadicTypeAt<I, Args...>::Type...>(std::forward<Args>(args)...);
}
template<typename Rule, typename ViewHandler, typename ViewTraits, typename ... Args>
bool routeImpl(Args &&...args, ViewHandler &&viewHandler)
{
return router()->addRule<ViewHandler, ViewTraits>(
new Rule(std::forward<Args>(args)..., [this, &viewHandler] (
QRegularExpressionMatch &match,
const QHttpServerRequest &request,
QTcpSocket *socket) {
auto boundViewHandler = router()->bindCaptured<ViewHandler, ViewTraits>(
std::forward<ViewHandler>(viewHandler), match);
responseImpl<ViewTraits>(boundViewHandler, request, socket);
}));
}
template<typename ViewTraits, typename T>
typename std::enable_if<ViewTraits::IsLastArgNonSpecial, void>::type
responseImpl(T &boundViewHandler,
const QHttpServerRequest &request,
QTcpSocket *socket)
{
const QHttpServerResponse response(boundViewHandler());
sendResponse(response, request, socket);
}
template<typename ViewTraits, typename T>
typename std::enable_if<ViewTraits::IsLastArgRequest, void>::type
responseImpl(T &boundViewHandler, const QHttpServerRequest &request, QTcpSocket *socket)
{
const QHttpServerResponse response(boundViewHandler(request));
sendResponse(response, request, socket);
}
template<typename ViewTraits, typename T>
typename std::enable_if<ViewTraits::IsLastArgResponder, void>::type
responseImpl(T &boundViewHandler,
const QHttpServerRequest &request,
QTcpSocket *socket)
{
boundViewHandler(makeResponder(request, socket));
}
bool handleRequest(const QHttpServerRequest &request, QTcpSocket *socket) final;
void sendResponse(const QHttpServerResponse &response,
const QHttpServerRequest &request,
QTcpSocket *socket);
};
QT_END_NAMESPACE
#endif // QHTTPSERVER_H

@ -0,0 +1,72 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtHttpServer module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** 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 Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or 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.GPL2 and 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-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QHTTPSERVER_P_H
#define QHTTPSERVER_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
// of QHttpServer. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
#include <private/qabstracthttpserver_p.h>
#include <QtHttpServer/qhttpserver.h>
#include <QtHttpServer/qhttpserverresponse.h>
#include <QtHttpServer/qhttpserverrequest.h>
#include <QtHttpServer/qhttpserverrouter.h>
#include <QtCore/qmap.h>
class QHttpServerPrivate: public QAbstractHttpServerPrivate
{
Q_DECLARE_PUBLIC(QHttpServer)
public:
QHttpServerPrivate() = default;
QHttpServerRouter router;
};
#endif // QHTTPSERVER_P_H

@ -267,6 +267,11 @@ QUrl QHttpServerRequest::url() const
return d->url;
}
QUrlQuery QHttpServerRequest::query() const
{
return QUrlQuery(d->url.query());
}
QHttpServerRequest::Method QHttpServerRequest::method() const
{
switch (d->httpParser.method) {

@ -46,6 +46,7 @@
#include <QtCore/qglobal.h>
#include <QtCore/qshareddata.h>
#include <QtCore/qurl.h>
#include <QtCore/qurlquery.h>
QT_BEGIN_NAMESPACE
@ -80,6 +81,7 @@ public:
QString value(const QString &key) const;
QUrl url() const;
QUrlQuery query() const;
Method method() const;
QVariantMap headers() const;
QByteArray body() const;

@ -0,0 +1,120 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtHttpServer module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** 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 Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or 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.GPL2 and 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-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtHttpServer/qhttpserverresponse.h>
#include <private/qhttpserverresponse_p.h>
#include <QtCore/qjsondocument.h>
#include <QtCore/qjsonobject.h>
QT_BEGIN_NAMESPACE
namespace {
const QByteArray mimeTextHtml("text/html");
const QByteArray mimeApplicationJson("application/json");
}
QHttpServerResponse::QHttpServerResponse(QHttpServerResponse &&other)
: d_ptr(other.d_ptr.take())
{
}
QHttpServerResponse::QHttpServerResponse(const QHttpServerResponse::StatusCode statusCode)
: QHttpServerResponse(mimeTextHtml, QByteArray(), statusCode)
{
}
QHttpServerResponse::QHttpServerResponse(const char *data)
: QHttpServerResponse(mimeTextHtml, QByteArray(data))
{
}
QHttpServerResponse::QHttpServerResponse(const QString &data)
: QHttpServerResponse(mimeTextHtml, data.toUtf8())
{
}
QHttpServerResponse::QHttpServerResponse(const QByteArray &data)
: QHttpServerResponse(mimeTextHtml, data)
{
}
QHttpServerResponse::QHttpServerResponse(const QJsonObject &data)
: QHttpServerResponse(mimeApplicationJson, QJsonDocument(data).toJson())
{
}
QHttpServerResponse::QHttpServerResponse(const QByteArray &mimeType,
const QByteArray &data,
const StatusCode status)
: QHttpServerResponse(new QHttpServerResponsePrivate{mimeType, data, status})
{
}
QHttpServerResponse::~QHttpServerResponse()
{
}
QHttpServerResponse::QHttpServerResponse(QHttpServerResponsePrivate *d)
: d_ptr(d)
{
}
QByteArray QHttpServerResponse::data() const
{
Q_D(const QHttpServerResponse);
return d->data;
}
QByteArray QHttpServerResponse::mimeType() const
{
Q_D(const QHttpServerResponse);
return d->mimeType;
}
QHttpServerResponse::StatusCode QHttpServerResponse::statusCode() const
{
Q_D(const QHttpServerResponse);
return d->statusCode;
}
QT_END_NAMESPACE

@ -0,0 +1,89 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtHttpServer module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** 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 Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or 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.GPL2 and 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-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QHTTPSERVERRESPONSE_H
#define QHTTPSERVERRESPONSE_H
#include <QtHttpServer/qhttpserverresponder.h>
#include <QtCore/qscopedpointer.h>
QT_BEGIN_NAMESPACE
class QJsonObject;
class QHttpServerResponsePrivate;
class Q_HTTPSERVER_EXPORT QHttpServerResponse
{
Q_DECLARE_PRIVATE(QHttpServerResponse)
public:
using StatusCode = QHttpServerResponder::StatusCode;
QHttpServerResponse() = delete;
QHttpServerResponse(const QHttpServerResponse &other) = delete;
QHttpServerResponse& operator=(const QHttpServerResponse &other) = delete;
QHttpServerResponse(QHttpServerResponse &&other);
QHttpServerResponse& operator=(QHttpServerResponse &&other) = delete;
QHttpServerResponse(const StatusCode statusCode);
QHttpServerResponse(const char *data);
QHttpServerResponse(const QString &data);
explicit QHttpServerResponse(const QByteArray &data);
QHttpServerResponse(const QJsonObject &data);
QHttpServerResponse(const QByteArray &mimeType,
const QByteArray &data,
const StatusCode status = StatusCode::Ok);
virtual ~QHttpServerResponse();
QByteArray data() const;
QByteArray mimeType() const;
StatusCode statusCode() const;
private:
QHttpServerResponse(QHttpServerResponsePrivate *d);
QScopedPointer<QHttpServerResponsePrivate> d_ptr;
};
QT_END_NAMESPACE
#endif // QHTTPSERVERRESPONSE_H

@ -0,0 +1,64 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtHttpServer module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** 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 Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or 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.GPL2 and 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-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QHTTPSERVERRESPONSE_P_H
#define QHTTPSERVERRESPONSE_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
// of QHttpServerResponse. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
#include <private/qabstracthttpserver_p.h>
#include <QtHttpServer/qhttpserverresponse.h>
struct QHttpServerResponsePrivate
{
QByteArray mimeType;
QByteArray data;
QHttpServerResponse::StatusCode statusCode;
};
#endif // QHTTPSERVERRESPONSE_P_H

@ -74,6 +74,82 @@ struct QHttpServerRouterViewTraits<Ret(ClassType::*)(Args...) const>
}
};
private:
// Tools used to check position of special arguments (QHttpServerResponder, QHttpServerRequest)
// and unsupported types
template<bool Last, typename Arg>
static constexpr bool checkArgument() noexcept
{
static_assert(Last || !std::is_same<Arg, const QHttpServerRequest &>::value,
"ViewHandler arguments error: "
"QHttpServerRequest can only be the last argument");
static_assert(Last || !std::is_same<Arg, QHttpServerResponder &&>::value,
"ViewHandler arguments error: "
"QHttpServerResponder can only be the last argument");
static_assert(!std::is_same<Arg, QHttpServerRequest &&>::value,
"ViewHandler arguments error: "
"QHttpServerRequest can only be passed as a const reference");
static_assert(!std::is_same<Arg, QHttpServerRequest &>::value,
"ViewHandler arguments error: "
"QHttpServerRequest can only be passed as a const reference");
static_assert(!std::is_same<Arg, const QHttpServerRequest *>::value,
"ViewHandler arguments error: "
"QHttpServerRequest can only be passed as a const reference");
static_assert(!std::is_same<Arg, QHttpServerRequest const*>::value,
"ViewHandler arguments error: "
"QHttpServerRequest can only be passed as a const reference");
static_assert(!std::is_same<Arg, QHttpServerRequest *>::value,
"ViewHandler arguments error: "
"QHttpServerRequest can only be passed as a const reference");
static_assert(!std::is_same<Arg, QHttpServerResponder &>::value,
"ViewHandler arguments error: "
"QHttpServerResponder can only be passed as a universal reference");
static_assert(!std::is_same<Arg, const QHttpServerResponder *const>::value,
"ViewHandler arguments error: "
"QHttpServerResponder can only be passed as a universal reference");
static_assert(!std::is_same<Arg, const QHttpServerResponder *>::value,
"ViewHandler arguments error: "
"QHttpServerResponder can only be passed as a universal reference");
static_assert(!std::is_same<Arg, QHttpServerResponder const*>::value,
"ViewHandler arguments error: "
"QHttpServerResponder can only be passed as a universal reference");
static_assert(!std::is_same<Arg, QHttpServerResponder *>::value,
"ViewHandler arguments error: "
"QHttpServerResponder can only be passed as a universal reference");
using Type = typename std::remove_cv<typename std::remove_reference<Arg>::type>::type;
static_assert(QMetaTypeId2<Type>::Defined
|| std::is_same<Type, QHttpServerResponder>::value
|| std::is_same<Type, QHttpServerRequest>::value,
"ViewHandler arguments error: "
"Type is not registered, please use the Q_DECLARE_METATYPE macro "
"to make it known to Qt's meta-object system");
return true;
}
public:
template<typename Arg, typename ... ArgX>
struct ArgumentsCheck {
static constexpr bool compileCheck()
{
return checkArgument<false, Arg>() && ArgumentsCheck<ArgX...>::compileCheck();
}
};
template<typename Arg>
struct ArgumentsCheck<Arg> {
static constexpr bool compileCheck()
{
return checkArgument<true, Arg>();
}
};
using Arguments = ArgumentsCheck<Args...>;
private:
// Tools used to compute ArgumentCapturableCount
template<typename T>
@ -113,6 +189,14 @@ public:
// BindableType is an emulation of "auto" for QHttpServerRouter::bindCapture.
using BindableType = typename decltype(
bindTypeHelper(typename QtPrivate::Indexes<ArgumentPlaceholdersCount>::Value{}))::Type;
static constexpr bool IsLastArgRequest = std::is_same<
typename Arg<ArgumentCount - 1>::Type, QHttpServerRequest>::value;
static constexpr bool IsLastArgResponder = std::is_same<
typename Arg<ArgumentCount - 1>::Type, QHttpServerResponder&&>::value;
static constexpr bool IsLastArgNonSpecial = !(IsLastArgRequest || IsLastArgResponder);
};
template <typename ClassType, typename Ret>
@ -129,6 +213,16 @@ struct QHttpServerRouterViewTraits<Ret(ClassType::*)() const>
static constexpr const auto ArgumentPlaceholdersCount = 0;
using BindableType = decltype(std::function<Ret()>{});
static constexpr bool IsLastArgRequest = false;
static constexpr bool IsLastArgResponder = false;
static constexpr bool IsLastArgNonSpecial = true;
struct ArgumentsCheck {
static constexpr bool compileCheck() { return true; }
};
using Arguments = ArgumentsCheck;
};
QT_END_NAMESPACE

@ -3,5 +3,6 @@ TEMPLATE = subdirs
SUBDIRS = \
cmake \
qabstracthttpserver \
qhttpserver \
qhttpserverresponder \
qhttpserverrouter

@ -0,0 +1,5 @@
CONFIG += testcase
TARGET = tst_qhttpserver
SOURCES += tst_qhttpserver.cpp
QT = httpserver httpserver-private testlib

@ -0,0 +1,348 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** $QT_BEGIN_LICENSE:LGPL$
** 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 Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or 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.GPL2 and 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-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtHttpServer/qhttpserver.h>
#include <QtHttpServer/qhttpserverrequest.h>
#include <QtHttpServer/qhttpserverrouterrule.h>
#include <private/qhttpserverrouterrule_p.h>
#include <QtTest/qtest.h>
#include <QtTest/qsignalspy.h>
#include <QtCore/qurl.h>
#include <QtCore/qstring.h>
#include <QtCore/qlist.h>
#include <QtCore/qbytearray.h>
#include <QtNetwork/qnetworkaccessmanager.h>
#include <QtNetwork/qnetworkreply.h>
#include <QtNetwork/qnetworkrequest.h>
QT_BEGIN_NAMESPACE
class QueryRequireRouterRule : public QHttpServerRouterRule
{
public:
QueryRequireRouterRule(const QString &pathPattern,
const char *queryKey,
RouterHandler &&routerHandler)
: QHttpServerRouterRule(pathPattern, std::forward<RouterHandler>(routerHandler)),
m_queryKey(queryKey)
{
}
bool matches(const QHttpServerRequest &request, QRegularExpressionMatch *match) const override
{
if (QHttpServerRouterRule::matches(request, match)) {
if (request.query().hasQueryItem(m_queryKey))
return true;
}
return false;
}
private:
const char * m_queryKey;
};
class tst_QHttpServer final : public QObject
{
Q_OBJECT
signals:
void test();
private slots:
void initTestCase();
void routeGet_data();
void routeGet();
void routePost_data();
void routePost();
private:
QHttpServer httpserver;
QString urlBase;
};
void tst_QHttpServer::initTestCase()
{
httpserver.route("/test", [] (QHttpServerResponder &&responder) {
responder.write("test msg", "text/html");
});
httpserver.route("/", QHttpServerRequest::Method::Get, [] () {
return "Hello world get";
});
httpserver.route("/", QHttpServerRequest::Method::Post, [] () {
return "Hello world post";
});
httpserver.route("/page/", [] (const qint32 number) {
return QString("page: %1").arg(number);
});
httpserver.route("/page/<arg>/detail", [] (const quint32 number) {
return QString("page: %1 detail").arg(number);
});
httpserver.route("/user/", [] (const QString &name) {
return QString("%1").arg(name);
});
httpserver.route("/user/<arg>/", [] (const QString &name, const QByteArray &ba) {
return QString("%1-%2").arg(name).arg(QString::fromLatin1(ba));
});
httpserver.route("/test/", [] (const QUrl &url) {
return QString("path: %1").arg(url.path());
});
httpserver.route("/api/v", [] (const float api) {
return QString("api %1v").arg(api);
});
httpserver.route("/api/v<arg>/user/", [] (const float api, const quint64 user) {
return QString("api %1v, user id - %2").arg(api).arg(user);
});
httpserver.route("/api/v<arg>/user/<arg>/settings", [] (const float api, const quint64 user,
const QHttpServerRequest &request) {
const auto &role = request.query().queryItemValue(QString::fromLatin1("role"));
const auto &fragment = request.url().fragment();
return QString("api %1v, user id - %2, set settings role=%3#'%4'")
.arg(api).arg(user).arg(role, fragment);
});
httpserver.route<QueryRequireRouterRule>(
"/custom/",
"key",
[] (const quint64 num, const QHttpServerRequest &request) {
return QString("Custom router rule: %1, key=%2")
.arg(num)
.arg(request.query().queryItemValue("key"));
});
urlBase = QStringLiteral("http://localhost:%1%2").arg(httpserver.listen());
}
void tst_QHttpServer::routeGet_data()
{
QTest::addColumn<QString>("url");
QTest::addColumn<int>("code");
QTest::addColumn<QString>("type");
QTest::addColumn<QString>("body");
QTest::addRow("hello world")
<< "/"
<< 200
<< "text/html"
<< "Hello world get";
QTest::addRow("test msg")
<< "/test"
<< 200
<< "text/html"
<< "test msg";
QTest::addRow("not found")
<< "/not-found"
<< 404
<< "text/html"
<< "";
QTest::addRow("arg:int")
<< "/page/10"
<< 200
<< "text/html"
<< "page: 10";
QTest::addRow("arg:-int")
<< "/page/-10"
<< 200
<< "text/html"
<< "page: -10";
QTest::addRow("arg:uint")
<< "/page/10/detail"
<< 200
<< "text/html"
<< "page: 10 detail";
QTest::addRow("arg:-uint")
<< "/page/-10/detail"
<< 404
<< "text/html"
<< "";
QTest::addRow("arg:string")
<< "/user/test"
<< 200
<< "text/html"
<< "test";
QTest::addRow("arg:string")
<< "/user/test test ,!a+."
<< 200
<< "text/html"
<< "test test ,!a+.";
QTest::addRow("arg:string,ba")
<< "/user/james/bond"
<< 200
<< "text/html"
<< "james-bond";
QTest::addRow("arg:url")
<< "/test/api/v0/cmds?val=1"
<< 200
<< "text/html"
<< "path: api/v0/cmds";
QTest::addRow("arg:float 5.1")
<< "/api/v5.1"
<< 200
<< "text/html"
<< "api 5.1v";
QTest::addRow("arg:float 5.")
<< "/api/v5."
<< 200
<< "text/html"
<< "api 5v";
QTest::addRow("arg:float 6.0")
<< "/api/v6.0"
<< 200
<< "text/html"
<< "api 6v";
QTest::addRow("arg:float,uint")
<< "/api/v5.1/user/10"
<< 200
<< "text/html"
<< "api 5.1v, user id - 10";
QTest::addRow("arg:float,uint,query")
<< "/api/v5.2/user/11/settings?role=admin" << 200
<< "text/html"
<< "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/html"
<< "api 5.2v, user id - 11, set settings role=admin#''";
QTest::addRow("custom route rule")
<< "/custom/15"
<< 404
<< "text/html"
<< "";
QTest::addRow("custom route rule + query")
<< "/custom/10?key=11&g=1"
<< 200
<< "text/html"
<< "Custom router rule: 10, key=11";
QTest::addRow("custom route rule + query key req")
<< "/custom/10?g=1&key=12"
<< 200
<< "text/html"
<< "Custom router rule: 10, key=12";
}
void tst_QHttpServer::routeGet()
{
QFETCH(QString, url);
QFETCH(int, code);
QFETCH(QString, type);
QFETCH(QString, body);
QNetworkAccessManager networkAccessManager;
const QUrl requestUrl(urlBase.arg(url));
auto reply = networkAccessManager.get(QNetworkRequest(requestUrl));
QTRY_VERIFY(reply->isFinished());
QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), type);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), code);
QCOMPARE(reply->readAll(), body);
}
void tst_QHttpServer::routePost_data()
{
QTest::addColumn<QString>("url");
QTest::addColumn<int>("code");
QTest::addColumn<QString>("type");
QTest::addColumn<QString>("data");
QTest::addColumn<QString>("body");
QTest::addRow("hello world")
<< "/"
<< 200
<< "text/html"
<< ""
<< "Hello world post";
}
void tst_QHttpServer::routePost()
{
QFETCH(QString, url);
QFETCH(int, code);
QFETCH(QString, type);
QFETCH(QString, data);
QFETCH(QString, body);
QNetworkAccessManager networkAccessManager;
const QUrl requestUrl(urlBase.arg(url));
auto reply = networkAccessManager.post(QNetworkRequest(requestUrl), data.toUtf8());
QTRY_VERIFY(reply->isFinished());
QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), type);
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), code);
QCOMPARE(reply->readAll(), body);
}
QT_END_NAMESPACE
QTEST_MAIN(tst_QHttpServer)
#include "tst_qhttpserver.moc"