1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5#include <QtHttpServer/qhttpserver.h>
6
7#include <QtHttpServer/qhttpserverrequest.h>
8#include <QtHttpServer/qhttpserverresponder.h>
9#include <QtHttpServer/qhttpserverresponse.h>
10
11#include <private/qhttpserver_p.h>
12#include <private/qhttpserverstream_p.h>
13
14#include <QtCore/qloggingcategory.h>
15
16#include <QtNetwork/qtcpsocket.h>
17
18QT_BEGIN_NAMESPACE
19
20Q_STATIC_LOGGING_CATEGORY(lcHS, "qt.httpserver");
21
22QHttpServerPrivate::QHttpServerPrivate(QHttpServer *p) :
23 router(p)
24{
25}
26
27void QHttpServerPrivate::callMissingHandler(const QHttpServerRequest &request,
28 QHttpServerResponder &responder)
29{
30 Q_Q(QHttpServer);
31
32 if (missingHandler.context && missingHandler.slotObject &&
33 verifyThreadAffinity(contextObject: missingHandler.context)) {
34 void *args[] = { nullptr, const_cast<QHttpServerRequest *>(&request), &responder };
35 missingHandler.slotObject->call(r: const_cast<QObject *>(missingHandler.context.data()), a: args);
36 } else {
37 qCDebug(lcHS) << "missing handler:" << request.url().path();
38 q->sendResponse(response: QHttpServerResponder::StatusCode::NotFound, request, responder: std::move(responder));
39 }
40}
41
42/*!
43 \class QHttpServer
44 \since 6.4
45 \inmodule QtHttpServer
46 \brief QHttpServer is a simplified API for QAbstractHttpServer and QHttpServerRouter.
47
48 QHttpServer is used to create a simple HTTP server by registering a range
49 of request handlers.
50
51 The \l route function can be used to conveniently add rules to the
52 server's \l QHttpServerRouter. To register a handler that is called after
53 every request to further process the response use \l
54 addAfterRequestHandler, but this mechanism only works for routes returning
55 \l QHttpServerResponse or \c {QFuture<QHttpServerResponse>}. To register a
56 handler for all unhandled requests use \l setMissingHandler.
57
58 Minimal example:
59
60 \code
61
62 QHttpServer server;
63
64 server.route("/", [] () {
65 return "hello world";
66 });
67
68 auto tcpserver = new QTcpServer();
69 if (!tcpserver->listen() || !server.bind(tcpserver)) {
70 delete tcpserver;
71 return -1;
72 }
73 qDebug() << "Listening on port" << tcpserver->serverPort();
74
75 \endcode
76*/
77
78/*!
79 Creates an instance of QHttpServer with parent \a parent.
80*/
81QHttpServer::QHttpServer(QObject *parent)
82 : QAbstractHttpServer(*new QHttpServerPrivate(this), parent)
83{
84}
85
86/*! \fn template <typename Rule = QHttpServerRouterRule, typename Functor> Rule *QHttpServer::route(const QString &pathPattern, QHttpServerRequest::Methods method, const QObject *context, Functor &&slot)
87
88 This method adds a new routing \c Rule to the server's
89 \l{QHttpServerRouter} member. The \c Rule template parameter can be any
90 class derived from \l{QHttpServerRouterRule}. The parameters are passed
91 to \c Rule. The server matches incoming HTTP requests against registered
92 rules based on the URL path and HTTP method, and the first match of both
93 is executed.
94
95 The \a pathPattern parameter is compared with the \l{QUrl::}{path()} of
96 the URL of the incoming request. The \a method parameter is compared with
97 the HTTP method of the incoming request. The \a slot parameter is the
98 request handler. It can be a member function pointer of \a context, a
99 function pointer, non-mutable lambda, or any copyable callable with a
100 const call operator. If \a context is provided, the rule remains valid
101 as long as context exists. The \a context must share the same thread
102 affinity as QHttpServer.
103
104 The \a slot takes as arguments any number of parsed arguments, that are
105 extracted from the \a pathPattern by matching the \c "<arg>" placeholders,
106 followed by an optional QHttpServerRequest and optional
107 QHttpServerResponder. These two classes are called specials.
108
109 The \a slot can return a QHttpServerResponse or a convertible type:
110
111 \code
112 QHttpServer server;
113 server.route("/test/", this, [] () { return ""; });
114 \endcode
115
116 Alternatively, if an optional \l{QHttpServerResponder}& argument is
117 provided, the response has to be written using it and the function
118 must return \c void.
119
120 \code
121 server.route("/test2", this, [] (QHttpServerResponder &responder) {
122 responder.write(QHttpServerResponder::StatusCode::Forbidden); });
123 \endcode
124
125 The QHttpServerRequest object can be used to access the body of the request:
126
127 \code
128 server.route("/test3", QHttpServerRequest::Method::Post, this,
129 [] (const QHttpServerRequest &request, QHttpServerResponder &responder) {
130 responder.write(request.body(), "text/plain"_ba);
131 });
132 \endcode
133
134 Any placeholder ( \c{"<arg>"} ) in \a pathPattern is automatically
135 converted to match the handler's argument types. Supported types
136 include integers, floating point numbers, QString, QByteArray, and
137 QUrl. The QUrl class can be used as the last parameter to handle the end
138 of the \a pathPattern, and by splitting it an arbitrary number of
139 arguments can be supported. Custom converters can be added using
140 \l{QHttpServerRouter::addConverter()}.
141
142 Each registered type has an associated regex that is used to match and
143 convert placeholders in the \a pathPattern. These regex patterns
144 are combined to construct a parser for the entire path. The resulting
145 parser is then used to verify if the path matches the pattern.
146 If parsing succeeds, the corresponding function is called with the
147 converted parameters. If parsing fails, the next registered callback is
148 attempted. If parsing fails for all callbacks, the missingHandler is
149 called.
150
151 In the example below, the value in the request path replacing \c "<arg>"
152 is converted to an \c int because the lambda expects an \c int parameter.
153 When an HTTP request matches the route, the converted value is passed to
154 the lambda's \c page argument:
155 \code
156 QHttpServer server;
157 server.route("/showpage/<arg>", this, [] (int page) { return getPage(page); });
158 \endcode
159
160 This function returns, if successful, a pointer to the newly created Rule,
161 otherwise a \c nullptr. The pointer can be used to set parameters on any
162 custom \l{QHttpServerRouter} class:
163
164 \code
165 auto rule = server.route<MyRule>("/test4", this, [] () {return "";});
166 rule->setParameter("test");
167 \endcode
168
169 \note This function, \l route(), must not be called from \a slot, so no
170 route handlers can register other route handlers.
171
172 \note If a request was processed by a \a slot accepting \l
173 {QHttpServerResponder}& as an argument, none of the after request handlers
174 (see \l addAfterRequestHandler) will be called.
175
176 Requests are processed sequentially inside the \l {QHttpServer}'s thread
177 by default. The request handler may return \c {QFuture<QHttpServerResponse>}
178 if concurrent processing is desired:
179
180 \code
181 server.route("/feature/<arg>", [] (int ms) {
182 return QtConcurrent::run([ms] () {
183 QThread::msleep(ms);
184 return QHttpServerResponse("the future is coming");
185 });
186 });
187 \endcode
188
189 The lambda of the QtConcurrent::run() is executed concurrently,
190 but all the network communication is executed sequentially in the
191 thread the \c {QHttpServer} belongs to after the QFuture is done.
192 Be aware that any QHttpServerRequest object is passed by reference to
193 the callback. Extract all needed content before QtConcurrent::run()
194 is called.
195
196 The \l{QHttpServerResponder}& special argument is only available for
197 routes returning \c void. When using a responder object the response
198 is returned using it.
199
200 \sa QHttpServerRouter::addRule, addAfterRequestHandler
201*/
202
203/*! \fn template <typename Rule = QHttpServerRouterRule, typename Functor> Rule *QHttpServer::route(const QString &pathPattern, const QObject *context, Functor &&slot)
204
205 \overload
206
207 Overload of \l QHttpServer::route to create a Rule for \a pathPattern and
208 the method \l{QHttpServerRequest::Method::AnyKnown}. All requests are
209 forwarded to \a context and \a slot.
210*/
211
212/*! \fn template <typename Rule = QHttpServerRouterRule, typename Functor> Rule *QHttpServer::route(const QString &pathPattern, QHttpServerRequest::Methods method, Functor &&handler)
213
214 \overload
215
216 Overload of \l QHttpServer::route to create a Rule for \a pathPattern and
217 \a method. All requests are forwarded to \a handler, which can be a
218 function pointer, a non-mutable lambda, or any other copyable callable with
219 const call operator. The rule will be valid until the QHttpServer is
220 destroyed.
221*/
222
223/*! \fn template <typename Rule = QHttpServerRouterRule, typename Functor> Rule *QHttpServer::route(const QString &pathPattern, Functor &&handler)
224
225 \overload
226
227 Overload of \l QHttpServer::route to create a Rule for \a pathPattern and
228 \l QHttpServerRequest::Method::AnyKnown. All requests are forwarded to \a
229 handler, which can be a function pointer, a non-mutable lambda, or any
230 other copyable callable with const call operator. The rule will be valid
231 until the QHttpServer is destroyed.
232*/
233
234/*!
235 Destroys a QHttpServer.
236*/
237QHttpServer::~QHttpServer()
238{
239}
240
241/*!
242 Returns a pointer to the router object.
243*/
244QHttpServerRouter *QHttpServer::router()
245{
246 Q_D(QHttpServer);
247 return &d->router;
248}
249
250/*!
251 Returns a pointer to the constant router object.
252*/
253const QHttpServerRouter *QHttpServer::router() const
254{
255 Q_D(const QHttpServer);
256 return &d->router;
257}
258
259/*! \fn template <typename Functor> void QHttpServer::setMissingHandler(const QObject *context, Functor &&slot)
260 Set a handler for unhandled requests.
261
262 All unhandled requests will be forwarded to the \a{context}'s \a slot.
263
264 The \a slot has to implement the signature \c{void (*)(const
265 QHttpServerRequest &, QHttpServerResponder &)}. The \a slot can also be a
266 function pointer, non-mutable lambda, or any other copyable callable with
267 const call operator. In that case the \a context will be a context object.
268 The handler will be valid until the context object is destroyed.
269
270 The default handler replies with status \c{404 Not Found}.
271
272 \sa clearMissingHandler
273*/
274
275/*!
276 \internal
277*/
278void QHttpServer::setMissingHandlerImpl(const QObject *context, QtPrivate::QSlotObjectBase *handler)
279{
280 Q_D(QHttpServer);
281 auto slot = QtPrivate::SlotObjUniquePtr(handler);
282 if (!d->verifyThreadAffinity(contextObject: context))
283 return;
284 d->missingHandler = {.context: context, .slotObject: std::move(slot)};
285}
286
287/*!
288 Resets the handler to the default one that produces replies with
289 status \c{404 Not Found}.
290
291 \sa setMissingHandler
292*/
293void QHttpServer::clearMissingHandler()
294{
295 Q_D(QHttpServer);
296 d->missingHandler.slotObject.reset();
297}
298
299
300/*! \fn template <typename Functor> void QHttpServer::addAfterRequestHandler(const QObject *context, Functor &&slot)
301 Register a \a context and \a slot to be called after each request is
302 handled.
303
304 The \a slot has to implement the signature \c{void (*)(const QHttpServerRequest &,
305 QHttpServerResponse &)}.
306
307 The \a slot can also be a function pointer, non-mutable lambda, or any other
308 copyable callable with const call operator. In that case the \a context will
309 be a context object and the handler will be valid until the context
310 object is destroyed.
311
312 Example:
313
314 \code
315 server.addAfterRequestHandler(&server, [] (const QHttpServerRequest &req, QHttpServerResponse &resp) {
316 auto h = resp.headers();
317 h.append(QHttpHeaders::WellKnownHeader::Cookie, "PollyWants=Cracker");
318 resp.setHeaders(std::move(h));
319 }
320 \endcode
321
322 \note These handlers will only be called for requests that are processed
323 by route handlers that either return QHttpServerResponse or
324 QFuture<QHttpServerResponse>, and therefore do not take a
325 QHttpServerResponder argument.
326*/
327
328/*!
329 \internal
330*/
331void QHttpServer::addAfterRequestHandlerImpl(const QObject *context, QtPrivate::QSlotObjectBase *handler)
332{
333 Q_D(QHttpServer);
334 auto slot = QtPrivate::SlotObjUniquePtr(handler);
335 if (!d->verifyThreadAffinity(contextObject: context))
336 return;
337 d->afterRequestHandlers.push_back(x: {.context: context, .slotObject: std::move(slot)});
338}
339
340/*!
341 \internal
342*/
343void QHttpServer::sendResponse(QHttpServerResponse &&response, const QHttpServerRequest &request,
344 QHttpServerResponder &&responder)
345{
346 Q_D(QHttpServer);
347 for (auto &afterRequestHandler : d->afterRequestHandlers) {
348 if (afterRequestHandler.context && afterRequestHandler.slotObject &&
349 d->verifyThreadAffinity(contextObject: afterRequestHandler.context)) {
350 void *args[] = { nullptr, const_cast<QHttpServerRequest *>(&request), &response };
351 afterRequestHandler.slotObject->call(r: const_cast<QObject *>(afterRequestHandler.context.data()), a: args);
352 }
353 }
354 responder.sendResponse(response);
355}
356
357#if QT_CONFIG(future)
358/*!
359 \internal
360*/
361void QHttpServer::sendResponse(QFuture<QHttpServerResponse> &&response,
362 const QHttpServerRequest &request, QHttpServerResponder &&responder)
363{
364 response.then(context: this,
365 function: [this, request,
366 responder = std::move(responder)](QHttpServerResponse &&response) mutable {
367 sendResponse(response: std::move(response), request, responder: std::move(responder));
368 });
369}
370#endif // QT_CONFIG(future)
371
372/*!
373 \internal
374*/
375bool QHttpServer::handleRequest(const QHttpServerRequest &request, QHttpServerResponder &responder)
376{
377 Q_D(QHttpServer);
378 return d->router.handleRequest(request, responder);
379}
380
381/*!
382 \internal
383*/
384void QHttpServer::missingHandler(const QHttpServerRequest &request, QHttpServerResponder &responder)
385{
386 Q_D(QHttpServer);
387 return d->callMissingHandler(request, responder);
388}
389
390QT_END_NAMESPACE
391
392#include "moc_qhttpserver.cpp"
393

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