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 /src/httpserver/qhttpserverrouter.cpp | |
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>
Diffstat (limited to 'src/httpserver/qhttpserverrouter.cpp')
-rw-r--r-- | src/httpserver/qhttpserverrouter.cpp | 283 |
1 files changed, 283 insertions, 0 deletions
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 |