1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qhttpserverrouter_p.h"
5
6#include <QtHttpServer/qhttpserverrouter.h>
7#include <QtHttpServer/qhttpserverrouterrule.h>
8#include <QtHttpServer/qhttpserverrequest.h>
9#include <QtHttpServer/qhttpserver.h>
10
11#include <private/qhttpserverrouterrule_p.h>
12
13#include <QtCore/qloggingcategory.h>
14#include <QtCore/qmetatype.h>
15
16QT_BEGIN_NAMESPACE
17
18Q_LOGGING_CATEGORY(lcRouter, "qt.httpserver.router")
19
20using namespace Qt::StringLiterals;
21
22/*!
23 \internal
24*/
25static const QHash<QMetaType, QString> defaultConverters = {
26 { QMetaType::fromType<int>(), u"[+-]?\\d+"_s },
27 { QMetaType::fromType<long>(), u"[+-]?\\d+"_s },
28 { QMetaType::fromType<long long>(), u"[+-]?\\d+"_s },
29 { QMetaType::fromType<short>(), u"[+-]?\\d+"_s },
30
31 { QMetaType::fromType<unsigned int>(), u"[+]?\\d+"_s },
32 { QMetaType::fromType<unsigned long>(), u"[+]?\\d+"_s },
33 { QMetaType::fromType<unsigned long long>(), u"[+]?\\d+"_s },
34 { QMetaType::fromType<unsigned short>(), u"[+]?\\d+"_s },
35
36 { QMetaType::fromType<double>(), u"[+-]?(?:[0-9]+(?:[.][0-9]*)?|[.][0-9]+)"_s },
37 { QMetaType::fromType<float>(), u"[+-]?(?:[0-9]+(?:[.][0-9]*)?|[.][0-9]+)"_s },
38
39 { QMetaType::fromType<QString>(), u"[^/]+"_s },
40 { QMetaType::fromType<QByteArray>(), u"[^/]+"_s },
41
42 { QMetaType::fromType<QUrl>(), u".*"_s },
43
44 { QMetaType::fromType<void>(), u""_s },
45};
46
47/*!
48 \class QHttpServerRouter
49 \since 6.4
50 \brief Provides functions to bind a URL to a \c ViewHandler.
51 \inmodule QtHttpServer
52
53 QHttpServerRouter is a class to distribute http requests to their
54 respective handlers with a rule based system.
55
56 You can register new \l{QHttpServerRouterRule}{QHttpServerRouterRules},
57 that represent a request path and the respective handler. Variable parts in
58 the route can be specified with placeholder in the request path. The
59 handler gets the placeholders value as a \l QRegularExpressionMatch. The
60 arguments can be of any type for which a \l{converters}{converter} is
61 available. The handler creation can be simplified with
62 QHttpServerRouterRule::bindCaptured. A QHttpServerRouter instance must not
63 be modifed by its rules.
64
65 \note This is a low-level routing API for an HTTP server.
66
67 Minimal example:
68
69 \code
70 auto pageView = [] (const quint64 page) {
71 qDebug() << "page" << page;
72 };
73 using ViewHandler = decltype(pageView);
74
75 QHttpServerRouter router;
76
77 // register callback pageView on request "/page/<number>"
78 // for example: "/page/10", "/page/15"
79 router.addRule<ViewHandler>(
80 new QHttpServerRouterRule("/page/", [=] (QRegularExpressionMatch &match,
81 const QHttpServerRequest &,
82 QHttpServerResponder &&) {
83 auto boundView = QHttpServerRouterRule::bindCaptured(pageView, match);
84
85 // it calls pageView
86 boundView();
87 }));
88 \endcode
89*/
90
91/*! \fn template <typename Type> bool QHttpServerRouter::addConverter(QAnyStringView regexp)
92
93 Adds a new converter for \e Type that can be parsed with \a regexp, and
94 returns \c true if this was successful, otherwise returns \c false. If
95 successful, the registered type can be used as argument in handlers for
96 \l{QHttpServerRouterRule}. The regular expression will be used to parse the
97 path pattern of the rule.
98
99 If there is already a converter of type \e Type, that converter's regexp
100 is replaced with \a regexp.
101
102 Minimal example:
103 \code
104 struct CustomArg {
105 int data = 10;
106
107 CustomArg() {} ;
108 CustomArg(const QString &urlArg) : data(urlArg.toInt()) {}
109 };
110 Q_DECLARE_METATYPE(CustomArg);
111
112 QHttpServerRouter router;
113 router.addConverter<CustomArg>(u"[+-]?\\d+"));
114
115 auto pageView = [] (const CustomArg &customArg) {
116 qDebug("data: %d", customArg.data);
117 };
118 using ViewHandler = decltype(pageView);
119
120 auto rule = std::make_unique<QHttpServerRouterRule>(
121 "/<arg>/log",
122 [&router, &pageView] (QRegularExpressionMatch &match,
123 const QHttpServerRequest &request,
124 QHttpServerResponder &&responder) {
125 // Bind and call viewHandler with match's captured string and quint32:
126 QHttpServerRouterRule::bindCaptured(pageView, match)();
127 });
128
129 router.addRule<ViewHandler>(std::move(rule));
130 \endcode
131*/
132
133/*! \fn template <typename ViewHandler, typename ViewTraits = QHttpServerRouterViewTraits<ViewHandler>> bool QHttpServerRouter::addRule(std::unique_ptr<QHttpServerRouterRule> rule)
134 Adds a new \a rule to the router.
135
136 Returns a pointer to the new rule if successful or \c nullptr otherwise.
137
138 Inside addRule, we determine ViewHandler arguments and generate a list of
139 their QMetaType::Type ids. Then we parse the URL and replace each \c <arg>
140 with a regexp for its type from the list. The \a rule must not modify the
141 QHttpServerRouter instance.
142
143 \code
144 QHttpServerRouter router;
145
146 using ViewHandler = decltype([] (const QString &page, const quint32 num) { });
147
148 auto rule = std::make_unique<QHttpServerRouterRule>(
149 "/<arg>/<arg>/log",
150 [] (QRegularExpressionMatch &match,
151 const QHttpServerRequest &request,
152 QHttpServerResponder &&responder) {
153 });
154
155 router.addRule<ViewHandler>(std::move(rule));
156 \endcode
157*/
158
159QHttpServerRouterPrivate::QHttpServerRouterPrivate(QAbstractHttpServer *server)
160 : converters(defaultConverters), server(server)
161{}
162
163/*!
164 Creates a QHttpServerRouter object with default converters.
165
166 \sa converters
167*/
168QHttpServerRouter::QHttpServerRouter(QAbstractHttpServer *server)
169 : d_ptr(new QHttpServerRouterPrivate(server))
170{}
171
172/*!
173 Destroys a QHttpServerRouter.
174*/
175QHttpServerRouter::~QHttpServerRouter()
176{}
177
178/*!
179 Adds a new converter for \a metaType that can be parsed with \a regexp.
180 Having a converter for a \a metaType enables to use this type in a path
181 pattern of a \l QHttpServerRouterRule. The regular expression is used to
182 parse parameters of type \a metaType from the path pattern.
183
184 If there is already a converter of type \a metaType, that converter's
185 regexp is replaced with \a regexp.
186
187 \sa converters, clearConverters
188*/
189void QHttpServerRouter::addConverter(QMetaType metaType, QAnyStringView regexp)
190{
191 Q_D(QHttpServerRouter);
192 d->converters[metaType] = regexp.toString();
193}
194
195/*!
196 Removes the converter for type \a metaType.
197
198 \sa addConverter
199*/
200void QHttpServerRouter::removeConverter(QMetaType metaType)
201{
202 Q_D(QHttpServerRouter);
203 d->converters.remove(key: metaType);
204}
205
206/*!
207 Removes all converters.
208
209 \note clearConverters() does not set up default converters.
210
211 \sa converters, addConverter
212*/
213void QHttpServerRouter::clearConverters()
214{
215 Q_D(QHttpServerRouter);
216 d->converters.clear();
217}
218
219/*!
220 \fn const QHash<QMetaType, QString> &QHttpServerRouter::converters() const &
221 \fn QHash<QMetaType, QString> QHttpServerRouter::converters() &&
222
223 Returns a map of converter types and regular expressions that are registered
224 with this QHttpServerRouter. These are the types that can be used in path
225 patterns of \l{QHttpServerRouterRule}{QHttpServerRouterRules}.
226
227 The following converters are available by default:
228
229 \value QMetaType::Int
230 \value QMetaType::Long
231 \value QMetaType::LongLong
232 \value QMetaType::Short
233 \value QMetaType::UInt
234 \value QMetaType::ULong
235 \value QMetaType::ULongLong
236 \value QMetaType::UShort
237 \value QMetaType::Double
238 \value QMetaType::Float
239 \value QMetaType::QString
240 \value QMetaType::QByteArray
241 \value QMetaType::QUrl
242 \value QMetaType::Void An empty converter.
243
244 \sa addConverter, clearConverters
245*/
246const QHash<QMetaType, QString> &QHttpServerRouter::converters() const &
247{
248 Q_D(const QHttpServerRouter);
249 return d->converters;
250}
251
252QHash<QMetaType, QString> QHttpServerRouter::converters() &&
253{
254 Q_D(QHttpServerRouter);
255 return std::move(d->converters);
256}
257
258QHttpServerRouterRule *QHttpServerRouter::addRuleImpl(std::unique_ptr<QHttpServerRouterRule> rule,
259 std::initializer_list<QMetaType> metaTypes)
260{
261 Q_D(QHttpServerRouter);
262
263 if (!rule->hasValidMethods() || !rule->createPathRegexp(metaTypes, converters: d->converters)) {
264 return nullptr;
265 }
266 if (!d->verifyThreadAffinity(contextObject: rule->contextObject())) {
267 return nullptr;
268 }
269
270 return d->rules.emplace_back(args: std::move(rule)).get();
271}
272
273/*!
274 Handles each new \a request for the HTTP server using \a responder.
275
276 Iterates through the list of rules to find the first that matches,
277 then executes this rule, returning \c true. Returns \c false if no rule
278 matches the request.
279*/
280bool QHttpServerRouter::handleRequest(const QHttpServerRequest &request,
281 QHttpServerResponder &responder) const
282{
283 Q_D(const QHttpServerRouter);
284 for (const auto &rule : d->rules) {
285 if (!rule->contextObject())
286 continue;
287 if (!d->verifyThreadAffinity(contextObject: rule->contextObject()))
288 continue;
289 if (rule->exec(request, responder))
290 return true;
291 }
292
293 return false;
294}
295
296bool QHttpServerRouterPrivate::verifyThreadAffinity(const QObject *contextObject) const
297{
298 if (contextObject && (contextObject->thread() != server->thread())) {
299 qCWarning(lcRouter, "QHttpServerRouter: the context object must reside in the same thread");
300 return false;
301 }
302 return true;
303}
304
305QT_END_NAMESPACE
306

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qthttpserver/src/httpserver/qhttpserverrouter.cpp