summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMikhail Svetkin <mikhail.svetkin@qt.io>2018-12-20 12:23:41 +0100
committerMikhail Svetkin <mikhail.svetkin@qt.io>2019-01-23 10:46:43 +0000
commita8565567840b23013d5ae1b41159986fe481a0f0 (patch)
treec1e96cf5c09bcbdca5ccbb5d951e1b8dc3487f1d
parent74b22d36f87c0a457609c0920aeec11df82f85e7 (diff)
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>
-rw-r--r--src/httpserver/httpserver.pro14
-rw-r--r--src/httpserver/qhttpserverhelpers.h74
-rw-r--r--src/httpserver/qhttpserverrequest.h23
-rw-r--r--src/httpserver/qhttpserverrouter.cpp283
-rw-r--r--src/httpserver/qhttpserverrouter.h126
-rw-r--r--src/httpserver/qhttpserverrouter_p.h74
-rw-r--r--src/httpserver/qhttpserverrouterrule.cpp222
-rw-r--r--src/httpserver/qhttpserverrouterrule.h95
-rw-r--r--src/httpserver/qhttpserverrouterrule_p.h71
-rw-r--r--src/httpserver/qhttpserverrouterviewtraits.h136
-rw-r--r--tests/auto/auto.pro3
-rw-r--r--tests/auto/qhttpserverrouter/qhttpserverrouter.pro5
-rw-r--r--tests/auto/qhttpserverrouter/tst_qhttpserverrouter.cpp225
13 files changed, 1336 insertions, 15 deletions
diff --git a/src/httpserver/httpserver.pro b/src/httpserver/httpserver.pro
index 070d946..7fdb32c 100644
--- a/src/httpserver/httpserver.pro
+++ b/src/httpserver/httpserver.pro
@@ -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)
diff --git a/src/httpserver/qhttpserverhelpers.h b/src/httpserver/qhttpserverhelpers.h
new file mode 100644
index 0000000..7788de4
--- /dev/null
+++ b/src/httpserver/qhttpserverhelpers.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 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
diff --git a/src/httpserver/qhttpserverrequest.h b/src/httpserver/qhttpserverrequest.h
index cfc8d7a..4b7afb3 100644
--- a/src/httpserver/qhttpserverrequest.h
+++ b/src/httpserver/qhttpserverrequest.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
diff --git a/src/httpserver/qhttpserverrouter.cpp b/src/httpserver/qhttpserverrouter.cpp
new file mode 100644
index 0000000..9c7fbab
--- /dev/null
+++ b/src/httpserver/qhttpserverrouter.cpp
@@ -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
diff --git a/src/httpserver/qhttpserverrouter.h b/src/httpserver/qhttpserverrouter.h
new file mode 100644
index 0000000..dc17683
--- /dev/null
+++ b/src/httpserver/qhttpserverrouter.h
@@ -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
diff --git a/src/httpserver/qhttpserverrouter_p.h b/src/httpserver/qhttpserverrouter_p.h
new file mode 100644
index 0000000..345a8c3
--- /dev/null
+++ b/src/httpserver/qhttpserverrouter_p.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
diff --git a/src/httpserver/qhttpserverrouterrule.cpp b/src/httpserver/qhttpserverrouterrule.cpp
new file mode 100644
index 0000000..9dfb349
--- /dev/null
+++ b/src/httpserver/qhttpserverrouterrule.cpp
@@ -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
diff --git a/src/httpserver/qhttpserverrouterrule.h b/src/httpserver/qhttpserverrouterrule.h
new file mode 100644
index 0000000..4a380ed
--- /dev/null
+++ b/src/httpserver/qhttpserverrouterrule.h
@@ -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
diff --git a/src/httpserver/qhttpserverrouterrule_p.h b/src/httpserver/qhttpserverrouterrule_p.h
new file mode 100644
index 0000000..75d0185
--- /dev/null
+++ b/src/httpserver/qhttpserverrouterrule_p.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
diff --git a/src/httpserver/qhttpserverrouterviewtraits.h b/src/httpserver/qhttpserverrouterviewtraits.h
new file mode 100644
index 0000000..aa67bb4
--- /dev/null
+++ b/src/httpserver/qhttpserverrouterviewtraits.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
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
index fdd7afa..c01fa6f 100644
--- a/tests/auto/auto.pro
+++ b/tests/auto/auto.pro
@@ -3,4 +3,5 @@ TEMPLATE = subdirs
SUBDIRS = \
cmake \
qabstracthttpserver \
- qhttpserverresponder
+ qhttpserverresponder \
+ qhttpserverrouter
diff --git a/tests/auto/qhttpserverrouter/qhttpserverrouter.pro b/tests/auto/qhttpserverrouter/qhttpserverrouter.pro
new file mode 100644
index 0000000..c928c6a
--- /dev/null
+++ b/tests/auto/qhttpserverrouter/qhttpserverrouter.pro
@@ -0,0 +1,5 @@
+CONFIG += testcase
+TARGET = tst_qhttpserverrouter
+SOURCES += tst_qhttpserverrouter.cpp
+
+QT = httpserver testlib
diff --git a/tests/auto/qhttpserverrouter/tst_qhttpserverrouter.cpp b/tests/auto/qhttpserverrouter/tst_qhttpserverrouter.cpp
new file mode 100644
index 0000000..183a631
--- /dev/null
+++ b/tests/auto/qhttpserverrouter/tst_qhttpserverrouter.cpp
@@ -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"