diff options
author | Mikhail Svetkin <mikhail.svetkin@qt.io> | 2018-12-20 12:23:41 +0100 |
---|---|---|
committer | Mikhail Svetkin <mikhail.svetkin@qt.io> | 2019-01-23 10:46:43 +0000 |
commit | a8565567840b23013d5ae1b41159986fe481a0f0 (patch) | |
tree | c1e96cf5c09bcbdca5ccbb5d951e1b8dc3487f1d | |
parent | 74b22d36f87c0a457609c0920aeec11df82f85e7 (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.pro | 14 | ||||
-rw-r--r-- | src/httpserver/qhttpserverhelpers.h | 74 | ||||
-rw-r--r-- | src/httpserver/qhttpserverrequest.h | 23 | ||||
-rw-r--r-- | src/httpserver/qhttpserverrouter.cpp | 283 | ||||
-rw-r--r-- | src/httpserver/qhttpserverrouter.h | 126 | ||||
-rw-r--r-- | src/httpserver/qhttpserverrouter_p.h | 74 | ||||
-rw-r--r-- | src/httpserver/qhttpserverrouterrule.cpp | 222 | ||||
-rw-r--r-- | src/httpserver/qhttpserverrouterrule.h | 95 | ||||
-rw-r--r-- | src/httpserver/qhttpserverrouterrule_p.h | 71 | ||||
-rw-r--r-- | src/httpserver/qhttpserverrouterviewtraits.h | 136 | ||||
-rw-r--r-- | tests/auto/auto.pro | 3 | ||||
-rw-r--r-- | tests/auto/qhttpserverrouter/qhttpserverrouter.pro | 5 | ||||
-rw-r--r-- | tests/auto/qhttpserverrouter/tst_qhttpserverrouter.cpp | 225 |
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 ®exp) +{ + 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 ®exp); + 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 ®exp = 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" |