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

Introduce QHttpServerRouter

Provide simple API for routing, parsing, capture and call callback

Change-Id: Ibd7c37282d00bd56f96d841db92b473a65a2bf5c
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Paul Wicking <paul.wicking@qt.io>
Reviewed-by: Jesus Fernandez <Jesus.Fernandez@qt.io>
This commit is contained in:
Mikhail Svetkin 2018-12-20 12:23:41 +01:00
parent 74b22d36f8
commit a856556784
13 changed files with 1336 additions and 15 deletions

@ -9,15 +9,23 @@ HEADERS += \
qthttpserverglobal.h \
qabstracthttpserver.h \
qabstracthttpserver_p.h \
qhttpserverhelpers.h \
qhttpserverrequest.h \
qhttpserverrequest_p.h \
qhttpserverresponder.h \
qhttpserverresponder_p.h \
qhttpserverrequest.h \
qhttpserverrequest_p.h
qhttpserverrouter.h \
qhttpserverrouter_p.h \
qhttpserverrouterrule.h \
qhttpserverrouterrule_p.h \
qhttpserverrouterviewtraits.h
SOURCES += \
qabstracthttpserver.cpp \
qhttpserverrequest.cpp \
qhttpserverresponder.cpp \
qhttpserverrequest.cpp
qhttpserverrouter.cpp \
qhttpserverrouterrule.cpp
include(../3rdparty/http-parser.pri)

@ -0,0 +1,74 @@
/****************************************************************************
**
** 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 QHTTPSERVERHELPERS_H
#define QHTTPSERVERHELPERS_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 <QtCore/qglobal.h>
#include <functional>
QT_BEGIN_NAMESPACE
namespace QHttpServerHelpers {
template<int> struct Placeholder {};
} // namespace QHttpServerHelpers
namespace std {
template<int N>
struct is_placeholder<QHttpServerHelpers::Placeholder<N>> : integral_constant<int, N + 1> {};
} // namespace std
QT_END_NAMESPACE
#endif // QHTTPSERVERHELPERS_H

@ -59,21 +59,24 @@ class Q_HTTPSERVER_EXPORT QHttpServerRequest : public QObjectUserData
friend class QAbstractHttpServerPrivate;
friend class QHttpServerResponse;
Q_GADGET
public:
~QHttpServerRequest() override;
enum class Method
{
Unknown = -1,
Get,
Put,
Delete,
Post,
Head,
Options,
Patch
Unknown = 0x0000,
Get = 0x0001,
Put = 0x0002,
Delete = 0x0004,
Post = 0x0008,
Head = 0x0010,
Options = 0x0020,
Patch = 0x0040
};
Q_DECLARE_FLAGS(Methods, Method);
Q_FLAG(Methods)
QString value(const QString &key) const;
QUrl url() const;
@ -96,6 +99,4 @@ private:
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QHttpServerRequest::Method)
#endif // QHTTPSERVERREQUEST_H

@ -0,0 +1,283 @@
/****************************************************************************
**
** 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 "qhttpserverrouter_p.h"
#include <QtHttpServer/qhttpserverrouter.h>
#include <QtHttpServer/qhttpserverrouterrule.h>
#include <QtHttpServer/qhttpserverrequest.h>
#include <private/qhttpserverrouterrule_p.h>
#include <QtCore/qmetatype.h>
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcRouter, "qt.httpserver.router")
namespace {
const QMap<int, QLatin1String> defaultConverters = {
{ QMetaType::Int, QLatin1String("[+-]?\\d+") },
{ QMetaType::Long, QLatin1String("[+-]?\\d+") },
{ QMetaType::LongLong, QLatin1String("[+-]?\\d+") },
{ QMetaType::Short, QLatin1String("[+-]?\\d+") },
{ QMetaType::UInt, QLatin1String("[+]?\\d+") },
{ QMetaType::ULong, QLatin1String("[+]?\\d+") },
{ QMetaType::ULongLong, QLatin1String("[+]?\\d+") },
{ QMetaType::UShort, QLatin1String("[+]?\\d+") },
{ QMetaType::Double, QLatin1String("[+-]?(?:[0-9]+(?:[.][0-9]*)?|[.][0-9]+)") },
{ QMetaType::Float, QLatin1String("[+-]?(?:[0-9]+(?:[.][0-9]*)?|[.][0-9]+)") },
{ QMetaType::QString, QLatin1String("[^/]+") },
{ QMetaType::QByteArray, QLatin1String("[^/]+") },
{ QMetaType::QUrl, QLatin1String(".*") },
{ QMetaType::Void, QLatin1String("") },
};
}
/*!
\class QHttpServerRouter
\brief Provides functions to bind a URL to a \c ViewHandler.
You can register \c ViewHandler as a callback for requests to a specific URL.
Variable parts in the route can be specified by the arguments in ViewHandler.
\note This is a low-level routing API for an HTTP server.
See the following example:
\code
auto pageView = [] (const quint64 page) {
qDebug() << "page" << page;
};
using ViewHandler = decltype(pageView);
QHttpServerRouter router;
// register callback pageView on request "/page/<number>"
// for example: "/page/10", "/page/15"
router.addRoute<ViewHandler>(
new QHttpServerRouterRule("/page/", [=] (QRegularExpressionMatch &match,
const QHttpServerRequest &,
QTcpSocket *) {
auto boundView = router.bindCaptured(pageView, match);
// it calls pageView
boundView();
}));
\endcode
*/
/*! \fn template <typename ViewHandler, typename ViewTraits = QHttpServerRouterViewTraits<ViewHandler>> bool QHttpServerRouter::addRule(QHttpServerRouterRule *rule)
Adds a new \a rule.
Inside addRule, we determine ViewHandler arguments and generate a list of
their QMetaType::Type ids. Then we parse the URL and replace each \c <arg>
with a regexp for its type from the list.
\code
QHttpServerRouter router;
using ViewHandler = decltype([] (const QString &page, const quint32 num) { });
auto rule = new QHttpServerRouterRule(
"/<arg>/<arg>/log",
[] (QRegularExpressionMatch &match,
const QHttpServerRequest &request,
QTcpSocket *socket) {
});
router.addRule<ViewHandler>(rule);
\endcode
\note This function takes over ownership of \a rule.
*/
/*! \fn template<typename ViewHandler, typename ViewTraits = QHttpServerRouterViewTraits<ViewHandler>> auto bindCaptured(ViewHandler &&handler, QRegularExpressionMatch &match) const -> typename ViewTraits::BindableType
Supplies the \a handler with arguments derived from a URL.
Returns the bound function that accepts whatever remaining arguments the handler may take,
supplying them to the handler after the URL-derived values.
Each match of the regex applied to the URL (as a string) is converted to the type
of the handler's parameter at its position, so that passing it works.
\code
QHttpServerRouter router;
auto pageView = [] (const QString &page, const quint32 num) { };
using ViewHandler = decltype(pageView);
auto rule = new QHttpServerRouterRule(
"/<arg>/<arg>/log",
[&router] (QRegularExpressionMatch &match,
const QHttpServerRequest &request,
QTcpSocket *socket) {
// Bind and call viewHandler with match's captured string and quint32:
router.bindCaptured(pageView, match)();
});
router.addRule<ViewHandler>(rule);
\endcode
*/
QHttpServerRouterPrivate::QHttpServerRouterPrivate()
: converters(defaultConverters)
{}
/*!
Creates a QHttpServerRouter object with \c defaultConverters.
\sa defaultConverters()
*/
QHttpServerRouter::QHttpServerRouter()
: d_ptr(new QHttpServerRouterPrivate)
{}
/*!
Destroys a QHttpServerRouter.
*/
QHttpServerRouter::~QHttpServerRouter()
{}
/*!
Adds a new converter for type \a type matching regular expression \a regexp.
If there is already a converter of type \a type, that converter's regexp
is replaced with \a regexp.
*/
void QHttpServerRouter::addConverter(const int type, const QLatin1String &regexp)
{
Q_D(QHttpServerRouter);
d->converters[type] = regexp;
}
/*!
Removes the converter for type \a type.
*/
void QHttpServerRouter::removeConverter(const int type)
{
Q_D(QHttpServerRouter);
d->converters.remove(type);
}
/*!
Removes all converters.
\note clearConverters() does not set up \c defaultConverters.
\sa defaultConverters()
*/
void QHttpServerRouter::clearConverters()
{
Q_D(QHttpServerRouter);
d->converters.clear();
}
/*!
Returns a map of converter type and regexp.
*/
const QMap<int, QLatin1String> &QHttpServerRouter::converters() const
{
Q_D(const QHttpServerRouter);
return d->converters;
}
/*!
Returns a map of default converter type and regexp.
The following converters are available by default:
\value QMetaType::Int
\value QMetaType::Long
\value QMetaType::LongLong
\value QMetaType::Short
\value QMetaType::UInt
\value QMetaType::ULong
\value QMetaType::ULongLong
\value QMetaType::UShort
\value QMetaType::Double
\value QMetaType::Float
\value QMetaType::QString
\value QMetaType::QByteArray
\value QMetaType::QUrl
\value QMetaType::Void An empty converter.
*/
const QMap<int, QLatin1String> &QHttpServerRouter::defaultConverters()
{
return ::defaultConverters;
}
bool QHttpServerRouter::addRuleImpl(QHttpServerRouterRule *rule,
const std::initializer_list<int> &types)
{
Q_D(QHttpServerRouter);
if (!rule->createPathRegexp(types, d->converters)) {
delete rule;
return false;
}
d->rules.emplace_back(rule);
return true;
}
/*!
Handles each new request for the HTTP server.
Iterates through the list of rules to find the first that matches,
then executes this rule, returning \c true. Returns \c false if no rule
matches the request.
*/
bool QHttpServerRouter::handleRequest(const QHttpServerRequest &request,
QTcpSocket *socket) const
{
Q_D(const QHttpServerRouter);
for (const auto &rule : qAsConst(d->rules)) {
if (rule->exec(request, socket))
return true;
}
return false;
}
QT_END_NAMESPACE

@ -0,0 +1,126 @@
/****************************************************************************
**
** 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 QHTTPSERVERROUTER_H
#define QHTTPSERVERROUTER_H
#include <QtHttpServer/qthttpserverglobal.h>
#include <QtHttpServer/qhttpserverhelpers.h>
#include <QtHttpServer/qhttpserverrouterviewtraits.h>
#include <QtCore/qscopedpointer.h>
#include <QtCore/qmetatype.h>
#include <QtCore/qregularexpression.h>
#include <initializer_list>
QT_BEGIN_NAMESPACE
class QTcpSocket;
class QHttpServerRequest;
class QHttpServerRouterRule;
class QHttpServerRouterPrivate;
class Q_HTTPSERVER_EXPORT QHttpServerRouter
{
Q_DECLARE_PRIVATE(QHttpServerRouter)
public:
QHttpServerRouter();
~QHttpServerRouter();
void addConverter(const int type, const QLatin1String &regexp);
void removeConverter(const int);
void clearConverters();
const QMap<int, QLatin1String> &converters() const;
static const QMap<int, QLatin1String> &defaultConverters();
template<typename ViewHandler, typename ViewTraits = QHttpServerRouterViewTraits<ViewHandler>>
bool addRule(QHttpServerRouterRule *rule)
{
return addRuleHelper<ViewTraits>(
rule,
typename QtPrivate::Indexes<ViewTraits::ArgumentCount>::Value{});
}
template<typename ViewHandler, typename ViewTraits = QHttpServerRouterViewTraits<ViewHandler>>
auto bindCaptured(ViewHandler &&handler,
QRegularExpressionMatch &match) const -> typename ViewTraits::BindableType
{
return bindCapturedImpl<ViewHandler, ViewTraits>(
std::forward<ViewHandler>(handler),
match,
typename QtPrivate::Indexes<ViewTraits::ArgumentCapturableCount>::Value{},
typename QtPrivate::Indexes<ViewTraits::ArgumentPlaceholdersCount>::Value{});
}
bool handleRequest(const QHttpServerRequest &request,
QTcpSocket *socket) const;
private:
template<typename ViewTraits, int ... Idx>
bool addRuleHelper(QHttpServerRouterRule *rule,
QtPrivate::IndexesList<Idx...>)
{
const std::initializer_list<int> types = {ViewTraits::template Arg<Idx>::metaTypeId()...};
return addRuleImpl(rule, types);
}
bool addRuleImpl(QHttpServerRouterRule *rule,
const std::initializer_list<int> &metaTypes);
template<typename ViewHandler, typename ViewTraits, int ... Cx, int ... Px>
auto bindCapturedImpl(ViewHandler &&handler,
QRegularExpressionMatch &match,
QtPrivate::IndexesList<Cx...>,
QtPrivate::IndexesList<Px...>) const -> typename ViewTraits::BindableType
{
return std::bind(
std::forward<ViewHandler>(handler),
QVariant(match.captured(Cx + 1)).value<typename ViewTraits::template Arg<Cx>::Type>()...,
QHttpServerHelpers::Placeholder<Px>{}...);
}
QScopedPointer<QHttpServerRouterPrivate> d_ptr;
};
QT_END_NAMESPACE
#endif // QHTTPSERVERROUTER_H

@ -0,0 +1,74 @@
/****************************************************************************
**
** 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 QHTTPSERVERROUTER_P_H
#define QHTTPSERVERROUTER_P_H
#include <QtHttpServer/qhttpserverrouter.h>
#include <QtHttpServer/qhttpserverrouterrule.h>
#include <QtCore/qmap.h>
#include <QtCore/qlist.h>
#include <QtCore/qstring.h>
#include <memory>
//
// 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.
QT_BEGIN_NAMESPACE
struct QHttpServerRouterPrivate
{
QHttpServerRouterPrivate();
QMap<int, QLatin1String> converters;
std::list<std::unique_ptr<QHttpServerRouterRule>> rules;
};
QT_END_NAMESPACE
#endif // QHTTPSERVERROUTER_P_H

@ -0,0 +1,222 @@
/****************************************************************************
**
** 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/qhttpserverrouterrule.h>
#include <private/qhttpserverrouterrule_p.h>
#include <QtCore/qregularexpression.h>
#include <QtCore/qdebug.h>
QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcRouterRule, "qt.httpserver.router.rule")
/*!
\class QHttpServerRouterRule
\brief The QHttpServerRouterRule is the base class for QHttpServerRouter rules.
Use QHttpServerRouterRule to specify expected request parameters:
\value path QUrl::path()
\value HTTP methods QHttpServerRequest::Methods
\value callback User-defined response callback
\note This is a low level API, see QHttpServer for higher level alternatives.
Example of QHttpServerRouterRule and QHttpServerRouter usage:
\code
template<typename ViewHandler>
void route(const char *path, const QHttpServerRequest::Methods methods, ViewHandler &&viewHandler)
{
auto rule = new QHttpServerRouterRule(
path, methods, [this, &viewHandler] (QRegularExpressionMatch &match,
const QHttpServerRequest &request,
QTcpSocket *const socket) {
auto boundViewHandler = router.bindCaptured<ViewHandler>(
std::forward<ViewHandler>(viewHandler), match);
// call viewHandler
boundViewHandler();
});
// QHttpServerRouter
router.addRule<ViewHandler>(rule);
}
// Valid:
route("/user/", [] (qint64 id) { } ); // "/user/1"
// "/user/3"
//
route("/user/<arg>/history", [] (qint64 id) { } ); // "/user/1/history"
// "/user/2/history"
//
route("/user/<arg>/history/", [] (qint64 id, qint64 page) { } ); // "/user/1/history/1"
// "/user/2/history/2"
// Invalid:
route("/user/<arg>", [] () { } ); // ERROR: path pattern has <arg>, but ViewHandler does not have any arguments
route("/user/\\d+", [] () { } ); // ERROR: path pattern does not support manual regexp
\endcode
\note Regular expressions in the path pattern are not supported, but
can be registered (to match a use of "<val>" to a specific type) using
QHttpServerRouter::addConverter().
*/
/*!
Constructs a rule with pathPattern \a pathPattern, and routerHandler \a routerHandler.
The rule accepts any HTTP method.
*/
QHttpServerRouterRule::QHttpServerRouterRule(const QString &pathPattern,
RouterHandler &&routerHandler)
: QHttpServerRouterRule(pathPattern,
QHttpServerRequest::Methods(),
std::forward<RouterHandler>(routerHandler))
{
}
/*!
Constructs a rule with pathPattern \a pathPattern, methods \a methods
and routerHandler \a routerHandler.
*/
QHttpServerRouterRule::QHttpServerRouterRule(const QString &pathPattern,
const QHttpServerRequest::Methods methods,
RouterHandler &&routerHandler)
: QHttpServerRouterRule(
new QHttpServerRouterRulePrivate{pathPattern,
methods,
std::forward<RouterHandler>(routerHandler), {}})
{
}
/*!
\internal
*/
QHttpServerRouterRule::QHttpServerRouterRule(QHttpServerRouterRulePrivate *d)
: d_ptr(d)
{
}
/*!
Destroys a QHttpServerRouterRule.
*/
QHttpServerRouterRule::~QHttpServerRouterRule()
{
}
/*!
This function is called by QHttpServerRouter when a new request is received.
*/
bool QHttpServerRouterRule::exec(const QHttpServerRequest &request,
QTcpSocket *socket) const
{
Q_D(const QHttpServerRouterRule);
QRegularExpressionMatch match;
if (!matches(request, &match))
return false;
d->routerHandler(match, request, socket);
return true;
}
/*!
This virtual function is called by exec() to check if request matches the rule.
*/
bool QHttpServerRouterRule::matches(const QHttpServerRequest &request,
QRegularExpressionMatch *match) const
{
Q_D(const QHttpServerRouterRule);
if (d->methods && !(d->methods & request.method()))
return false;
*match = d->pathRegexp.match(request.url().path());
return (match->hasMatch() && d->pathRegexp.captureCount() == match->lastCapturedIndex());
}
/*!
\internal
*/
bool QHttpServerRouterRule::createPathRegexp(const std::initializer_list<int> &metaTypes,
const QMap<int, QLatin1String> &converters)
{
Q_D(QHttpServerRouterRule);
QString pathRegexp = d->pathPattern;
const QLatin1String arg("<arg>");
for (auto type : metaTypes) {
auto it = converters.constFind(type);
if (it == converters.end()) {
qCWarning(lcRouterRule) << "can not find converter for type:" << type;
continue;
}
if (it->isEmpty())
continue;
const auto index = pathRegexp.indexOf(arg);
const QString &regexp = QLatin1Char('(') % *it % QLatin1Char(')');
if (index == -1)
pathRegexp.append(regexp);
else
pathRegexp.replace(index, arg.size(), regexp);
}
if (pathRegexp.indexOf(arg) != -1) {
qCWarning(lcRouterRule) << "not enough types or one of the types is not supported, regexp:"
<< pathRegexp
<< ", pattern:" << d->pathPattern
<< ", types:" << metaTypes;
return false;
}
if (!pathRegexp.startsWith(QLatin1Char('^')))
pathRegexp = QLatin1Char('^') % pathRegexp;
if (!pathRegexp.endsWith(QLatin1Char('$')))
pathRegexp += QLatin1String("$");
qCDebug(lcRouterRule) << "url pathRegexp:" << pathRegexp;
d->pathRegexp.setPattern(pathRegexp);
d->pathRegexp.optimize();
return true;
}
QT_END_NAMESPACE

@ -0,0 +1,95 @@
/****************************************************************************
**
** 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 QHTTPSERVERROUTERRULE_H
#define QHTTPSERVERROUTERRULE_H
#include <QtHttpServer/qhttpserverrequest.h>
#include <QtCore/qmap.h>
QT_BEGIN_NAMESPACE
class QString;
class QHttpServerRequest;
class QTcpSocket;
class QRegularExpressionMatch;
class QHttpServerRouter;
class QHttpServerRouterRulePrivate;
class Q_HTTPSERVER_EXPORT QHttpServerRouterRule
{
Q_DECLARE_PRIVATE(QHttpServerRouterRule)
public:
using RouterHandler = std::function<void(QRegularExpressionMatch &,
const QHttpServerRequest &,
QTcpSocket *)>;
explicit QHttpServerRouterRule(const QString &pathPattern, RouterHandler &&routerHandler);
explicit QHttpServerRouterRule(const QString &pathPattern,
const QHttpServerRequest::Methods methods,
RouterHandler &&routerHandler);
QHttpServerRouterRule(QHttpServerRouterRule &&other) = delete;
QHttpServerRouterRule &operator=(QHttpServerRouterRule &&other) = delete;
virtual ~QHttpServerRouterRule();
protected:
bool exec(const QHttpServerRequest &request, QTcpSocket *socket) const;
bool createPathRegexp(const std::initializer_list<int> &metaTypes,
const QMap<int, QLatin1String> &converters);
virtual bool matches(const QHttpServerRequest &request,
QRegularExpressionMatch *match) const;
QHttpServerRouterRule(QHttpServerRouterRulePrivate *d);
private:
Q_DISABLE_COPY(QHttpServerRouterRule);
QScopedPointer<QHttpServerRouterRulePrivate> d_ptr;
friend class QHttpServerRouter;
};
QT_END_NAMESPACE
#endif // QHTTPSERVERROUTERRULE_H

@ -0,0 +1,71 @@
/****************************************************************************
**
** 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 QHTTPSERVERROUTERRULE_P_H
#define QHTTPSERVERROUTERRULE_P_H
#include <QtHttpServer/qhttpserverrouterrule.h>
#include <QtCore/qregularexpression.h>
#include <QtCore/qstring.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.
QT_BEGIN_NAMESPACE
struct Q_HTTPSERVER_EXPORT QHttpServerRouterRulePrivate
{
QString pathPattern;
QHttpServerRequest::Methods methods;
QHttpServerRouterRule::RouterHandler routerHandler;
QRegularExpression pathRegexp;
};
QT_END_NAMESPACE
#endif // QHTTPSERVERROUTERRULE_P_H

@ -0,0 +1,136 @@
/****************************************************************************
**
** 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 QHTTPSERVERROUTERVIEWTRAITS_H
#define QHTTPSERVERROUTERVIEWTRAITS_H
#include <QtCore/qglobal.h>
#include <QtCore/qnamespace.h>
#include <functional>
#include <tuple>
#include <type_traits>
QT_BEGIN_NAMESPACE
class QHttpServerRequest;
class QHttpServerResponder;
template <typename ViewHandler>
struct QHttpServerRouterViewTraits : QHttpServerRouterViewTraits<decltype(&ViewHandler::operator())> {};
template <typename ClassType, typename Ret, typename ... Args>
struct QHttpServerRouterViewTraits<Ret(ClassType::*)(Args...) const>
{
static constexpr const auto ArgumentCount = sizeof ... (Args);
template <int I>
struct Arg {
using OriginalType = typename std::tuple_element<I, std::tuple<Args...>>::type;
using Type = typename QtPrivate::RemoveConstRef<OriginalType>::Type;
static constexpr int metaTypeId() noexcept {
return qMetaTypeId<
typename std::conditional<
!QMetaTypeId2<Type>::Defined,
void,
Type>::type>();
}
};
private:
// Tools used to compute ArgumentCapturableCount
template<typename T>
static constexpr typename std::enable_if<QMetaTypeId2<T>::Defined, int>::type
capturable()
{ return 1; }
template<typename T>
static constexpr typename std::enable_if<!QMetaTypeId2<T>::Defined, int>::type
capturable()
{ return 0; }
static constexpr std::size_t sum() noexcept { return 0; }
template<typename ... N>
static constexpr std::size_t sum(const std::size_t it, N ... n) noexcept
{ return it + sum(n...); }
public:
static constexpr const auto ArgumentCapturableCount =
sum(capturable<typename QtPrivate::RemoveConstRef<Args>::Type>()...);
static constexpr const auto ArgumentPlaceholdersCount = ArgumentCount - ArgumentCapturableCount;
private:
// Tools used to get BindableType
template<typename Return, typename ... ArgsX>
struct BindTypeHelper {
using Type = std::function<Return(ArgsX...)>;
};
template<int ... Idx>
static constexpr auto bindTypeHelper(QtPrivate::IndexesList<Idx...>) ->
BindTypeHelper<Ret, typename Arg<ArgumentCapturableCount + Idx>::OriginalType...>;
public:
// C++11 does not allow use of "auto" as a function return type.
// BindableType is an emulation of "auto" for QHttpServerRouter::bindCapture.
using BindableType = typename decltype(
bindTypeHelper(typename QtPrivate::Indexes<ArgumentPlaceholdersCount>::Value{}))::Type;
};
template <typename ClassType, typename Ret>
struct QHttpServerRouterViewTraits<Ret(ClassType::*)() const>
{
static constexpr const int ArgumentCount = 0;
template <int I>
struct Arg {
using Type = void;
};
static constexpr const auto ArgumentCapturableCount = 0;
static constexpr const auto ArgumentPlaceholdersCount = 0;
using BindableType = decltype(std::function<Ret()>{});
};
QT_END_NAMESPACE
#endif // QHTTPSERVERROUTERVIEWTRAITS_H

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

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

@ -0,0 +1,225 @@
/****************************************************************************
**
** 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/qhttpserverresponder.h>
#include <QtHttpServer/qabstracthttpserver.h>
#include <QtHttpServer/qhttpserverrouter.h>
#include <QtHttpServer/qhttpserverrouterrule.h>
#include <QtTest/qsignalspy.h>
#include <QtTest/qtest.h>
#include <QtNetwork/qnetworkaccessmanager.h>
QT_BEGIN_NAMESPACE
Q_DECLARE_METATYPE(QNetworkAccessManager::Operation);
struct HttpServer : QAbstractHttpServer {
QHttpServerRouter router;
HttpServer()
: QAbstractHttpServer()
{
connect(this, &QAbstractHttpServer::missingHandler,
[] (const QHttpServerRequest &request, QTcpSocket *socket) {
makeResponder(request, socket).write(QHttpServerResponder::StatusCode::NotFound);
});
}
template<typename ViewHandler>
void route(const char *path, const QHttpServerRequest::Methods methods, ViewHandler &&viewHandler)
{
auto rule = new QHttpServerRouterRule(
path, methods, [this, &viewHandler] (QRegularExpressionMatch &match,
const QHttpServerRequest &request,
QTcpSocket *socket) {
auto boundViewHandler = router.bindCaptured<ViewHandler>(
std::forward<ViewHandler>(viewHandler), match);
boundViewHandler(makeResponder(request, socket));
});
router.addRule<ViewHandler>(rule);
}
template<typename ViewHandler>
void route(const char *path, ViewHandler &&viewHandler)
{
route(path, QHttpServerRequest::Methods(nullptr), std::forward<ViewHandler>(viewHandler));
}
bool handleRequest(const QHttpServerRequest &request, QTcpSocket *socket) override {
return router.handleRequest(request, socket);
}
};
class tst_QHttpServerRouter : public QObject
{
Q_OBJECT
private slots:
void initTestCase();
void routerRule_data();
void routerRule();
private:
HttpServer httpserver;
QString urlBase;
};
void tst_QHttpServerRouter::initTestCase()
{
httpserver.route("/page/", [] (const quint64 &page, QHttpServerResponder &&responder) {
responder.write(QString("page: %1").arg(page).toUtf8(), "text/plain");
});
httpserver.route("/post-only", QHttpServerRequest::Method::Post,
[] (QHttpServerResponder &&responder) {
responder.write(QString("post-test").toUtf8(), "text/plain");
});
httpserver.route("/get-only", QHttpServerRequest::Method::Get,
[] (QHttpServerResponder &&responder) {
responder.write(QString("get-test").toUtf8(), "text/plain");
});
urlBase = QStringLiteral("http://localhost:%1%2").arg(httpserver.listen());
}
void tst_QHttpServerRouter::routerRule_data()
{
QTest::addColumn<QString>("url");
QTest::addColumn<int>("code");
QTest::addColumn<QString>("type");
QTest::addColumn<QString>("body");
QTest::addColumn<QNetworkAccessManager::Operation>("replyType");
QTest::addRow("/page/1")
<< "/page/1"
<< 200
<< "text/plain"
<< "page: 1"
<< QNetworkAccessManager::GetOperation;
QTest::addRow("/page/-1")
<< "/page/-1"
<< 404
<< "application/x-empty"
<< ""
<< QNetworkAccessManager::GetOperation;
QTest::addRow("/post-only [GET]")
<< "/post-only"
<< 404
<< "application/x-empty"
<< ""
<< QNetworkAccessManager::GetOperation;
QTest::addRow("/post-only [DELETE]")
<< "/post-only"
<< 404
<< "application/x-empty"
<< ""
<< QNetworkAccessManager::DeleteOperation;
QTest::addRow("/post-only [POST]")
<< "/post-only"
<< 200
<< "text/plain"
<< "post-test"
<< QNetworkAccessManager::PostOperation;
QTest::addRow("/get-only [GET]")
<< "/get-only"
<< 200
<< "text/plain"
<< "get-test"
<< QNetworkAccessManager::GetOperation;
QTest::addRow("/get-only [POST]")
<< "/get-only"
<< 404
<< "application/x-empty"
<< ""
<< QNetworkAccessManager::PostOperation;
QTest::addRow("/get-only [DELETE]")
<< "/get-only"
<< 404
<< "application/x-empty"
<< ""
<< QNetworkAccessManager::DeleteOperation;
}
void tst_QHttpServerRouter::routerRule()
{
QFETCH(QString, url);
QFETCH(int, code);
QFETCH(QString, type);
QFETCH(QString, body);
QFETCH(QNetworkAccessManager::Operation, replyType);
QNetworkAccessManager networkAccessManager;
QNetworkReply *reply;
QNetworkRequest request(QUrl(urlBase.arg(url)));
switch (replyType) {
case QNetworkAccessManager::GetOperation:
reply = networkAccessManager.get(request);
break;
case QNetworkAccessManager::PostOperation:
request.setHeader(QNetworkRequest::ContentTypeHeader, type);
reply = networkAccessManager.post(request, QByteArray("post body"));
break;
case QNetworkAccessManager::DeleteOperation:
reply = networkAccessManager.deleteResource(request);
break;
default:
QFAIL("The replyType does not supported");
}
QTRY_VERIFY(reply->isFinished());
QCOMPARE(reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(), code);
QCOMPARE(reply->header(QNetworkRequest::ContentTypeHeader), type);
QCOMPARE(reply->readAll(), body);
}
QT_END_NAMESPACE
QTEST_MAIN(tst_QHttpServerRouter)
#include "tst_qhttpserverrouter.moc"