1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include <QtHttpServer/qhttpserver.h>
5
6#include <QtHttpServer/qhttpserverrequest.h>
7#include <QtHttpServer/qhttpserverresponder.h>
8#include <QtHttpServer/qhttpserverresponse.h>
9
10#include <private/qhttpserver_p.h>
11#include <private/qhttpserverstream_p.h>
12
13#include <QtCore/qloggingcategory.h>
14
15#include <QtNetwork/qtcpsocket.h>
16
17QT_BEGIN_NAMESPACE
18
19Q_LOGGING_CATEGORY(lcHS, "qt.httpserver");
20
21QHttpServerPrivate::QHttpServerPrivate(QHttpServer *p) :
22 router(p)
23{
24}
25
26void QHttpServerPrivate::callMissingHandler(const QHttpServerRequest &request,
27 QHttpServerResponder &responder)
28{
29 Q_Q(QHttpServer);
30
31 if (missingHandler.context && missingHandler.slotObject &&
32 verifyThreadAffinity(contextObject: missingHandler.context)) {
33 void *args[] = { nullptr, const_cast<QHttpServerRequest *>(&request), &responder };
34 missingHandler.slotObject->call(r: const_cast<QObject *>(missingHandler.context.data()), a: args);
35 } else {
36 qCDebug(lcHS) << "missing handler:" << request.url().path();
37 q->sendResponse(response: QHttpServerResponder::StatusCode::NotFound, request, responder: std::move(responder));
38 }
39}
40
41/*!
42 \class QHttpServer
43 \since 6.4
44 \inmodule QtHttpServer
45 \brief QHttpServer is a simplified API for QAbstractHttpServer and QHttpServerRouter.
46
47 QHttpServer allows to create a simple Http server by setting a range of
48 request handlers.
49
50 The \l route function can be used to conveniently add rules to
51 the servers \l QHttpServerRouter. To register a handler to be called after
52 every request use \l addAfterRequestHandler and to register a handler for
53 all unhandled requests use \l setMissingHandler.
54
55 Minimal example:
56
57 \code
58
59 QHttpServer server;
60
61 server.route("/", [] () {
62 return "hello world";
63 });
64
65 auto tcpserver = new QTcpServer();
66 if (!tcpserver->listen() || !server.bind(tcpserver.get())) {
67 delete tcpserver;
68 return -1;
69 }
70 qDebug() << "Listening on port" << tcpserver->serverPort();
71
72 \endcode
73*/
74
75/*!
76 Creates an instance of QHttpServer with parent \a parent.
77*/
78QHttpServer::QHttpServer(QObject *parent)
79 : QAbstractHttpServer(*new QHttpServerPrivate(this), parent)
80{
81}
82
83/*! \fn template <typename Rule = QHttpServerRouterRule, typename Functor> Rule *QHttpServer::route(const QString &pathPattern, QHttpServerRequest::Methods method, const QObject *receiver, Functor &&slot)
84
85 This is a convenience method to add a new \c Rule to the server's
86 \l{QHttpServerRouter}. The Rule template parameter can be any custom class
87 derived from QHttpServerRouterRule.
88
89 This function takes a \a pathPattern and a \a method that represent a set
90 of requests and creates a new \l{QHttpServerRouterRule} (or custom Rule if
91 specified in the template parameters) that forwards all respective requests
92 to the provided \a receiver and \a slot. The rule is added to the
93 \l{router}. For details on valid patterns in \a pathPattern, see the
94 \l{QHttpServerRouterRule} documentation.
95
96 \a slot can be a member function pointer of \a receiver. It can also be
97 a function pointer, a non-mutable lambda, or any other copiable callable
98 with const call operator. In that case \a receiver has to be a \l QObject
99 pointer. The rule will be valid for the lifetime duration of the \a
100 receiver. The receiver must share the same thread affinity as the
101 QHttpServer for the registration to be successful and for the rule to be
102 executed.
103
104 The slot can express its response with a return statement. The function has
105 to return QHttpServerResponse or any type that can be converted to
106 QHttpServerResponse. A large range of conversion constructors are available,
107 see \l{QHttpServerResponse}.
108
109 \code
110 QHttpServer server;
111 server.route("/test", this, [] () { return ""; });
112 \endcode
113
114 Alternatively, an optional last function argument \c {QHttpServerResponder&}
115 can be provided on which the response has to be written. If the response is
116 written to a \c {QHttpServerResponder&} the function must return \c void.
117
118 \code
119 server.route("/test2", this, [] (QHttpServerResponder &responder) {
120 responder.write(QHttpServerResponder::StatusCode::Forbidden); });
121 \endcode
122
123 The slot can further have a \c {const QHttpServerRequest&} as a
124 second to last parameter to get detailed information on the request
125
126 \code
127 server.route("/test3", this, [] (const QHttpServerRequest &request,
128 QHttpServerResponder &responder) {
129 responder.write(req.body(), "text/plain"_ba);});
130 \endcode
131
132 Finally, the callback can contain an arbitrary amount of copiable
133 parameters that are registered with the QHttpServerRouter::converters. By
134 default, these are most integer types, float, double, QString, QByteArray,
135 and QUrl. Additional converters can be registered, see
136 \l{QHttpServerRouter::addConverter}. These parameters must have a
137 corresponding placeholder in the \a pathPattern. For details on
138 placeholders and pathPattern see \l{QHttpServerRouterRule}.
139
140 \code
141 QHttpServer server;
142 server.route("/test/<arg>", this, [] (const int page) { return ""; });
143 \endcode
144
145 This function returns, if successful, a pointer to the newly created Rule,
146 otherwise a \c nullptr. The pointer can be used to set parameters on any
147 custom \l{QHttpServerRouter} class:
148
149 \code
150 auto rule = server.route<MyRule>("/test", this, [] () {return "";});
151 rule->setParameter("test");
152 \endcode
153
154. This function must not be called from any \l route callback.
155
156 \note If a request was processed by a handler accepting \c
157 {QHttpServerResponder&} as an argument, none of the after request handlers
158 (see \l addAfterRequestHandler) will be called.
159
160 Requests are processed sequentially inside the \c {QHttpServer}'s thread
161 by default. The request handler may return \c {QFuture<QHttpServerResponse>}
162 if asynchronous processing is desired:
163
164 \code
165 server.route("/feature/", [] (int id) {
166 return QtConcurrent::run([] () {
167 return QHttpServerResponse("the future is coming");
168 });
169 });
170 \endcode
171
172 The body of \c QFuture is executed asynchronously, but all the network
173 communication is executed sequentially in the thread the \c {QHttpServer}
174 belongs to. The \c {QHttpServerResponder&} special argument is not
175 available for routes returning a \c {QFuture}.
176
177 \sa QHttpServerRouter::addRule, addAfterRequestHandler
178*/
179
180/*! \fn template <typename Rule = QHttpServerRouterRule, typename Functor> Rule *QHttpServer::route(const QString &pathPattern, const QObject *receiver, Functor &&slot)
181
182 \overload
183
184 Overload of \l QHttpServer::route to create a Rule for \a pathPattern and
185 \l QHttpServerRequest::Method::AnyKnown. All requests are forwarded
186 to \a receiver and \a slot.
187*/
188
189/*! \fn template <typename Rule = QHttpServerRouterRule, typename Functor> Rule *QHttpServer::route(const QString &pathPattern, QHttpServerRequest::Methods method, Functor &&handler)
190
191 \overload
192
193 Overload of \l QHttpServer::route to create a Rule for \a pathPattern and
194 \a method. All requests are forwarded to \a handler, which can be a
195 function pointer, a non-mutable lambda, or any other copiable callable with
196 const call operator. The rule will be valid until the QHttpServer is
197 destroyed.
198*/
199
200/*! \fn template <typename Rule = QHttpServerRouterRule, typename Functor> Rule *QHttpServer::route(const QString &pathPattern, Functor &&handler)
201
202 \overload
203
204 Overload of \l QHttpServer::route to create a Rule for \a pathPattern and
205 \l QHttpServerRequest::Method::AnyKnown. All requests are forwarded to \a
206 handler, which can be a function pointer, a non-mutable lambda, or any
207 other copiable callable with const call operator. The rule will be valid
208 until the QHttpServer is destroyed.
209*/
210
211/*!
212 Destroys a QHttpServer.
213*/
214QHttpServer::~QHttpServer()
215{
216}
217
218/*!
219 Returns a pointer to the router object.
220*/
221QHttpServerRouter *QHttpServer::router()
222{
223 Q_D(QHttpServer);
224 return &d->router;
225}
226
227/*!
228 Returns a pointer to the constant router object.
229*/
230const QHttpServerRouter *QHttpServer::router() const
231{
232 Q_D(const QHttpServer);
233 return &d->router;
234}
235
236/*! \fn template <typename Functor> void QHttpServer::setMissingHandler(const QObject *receiver, Functor &&slot)
237 Set a handler for unhandled requests.
238
239 All unhandled requests will be forwarded to the \a{receiver}'s \a slot.
240
241 The \a slot has to implement the signature \c{void (*)(const
242 QHttpServerRequest &, QHttpServerResponder &)}. The \a slot can also be a
243 function pointer, non-mutable lambda, or any other copiable callable with
244 const call operator. In that case the \a receiver will be a context object.
245 The handler will be valid until the receiver object is destroyed.
246
247 The default handler replies with status 404: Not Found.
248*/
249
250/*!
251 \internal
252*/
253void QHttpServer::setMissingHandlerImpl(const QObject *context, QtPrivate::QSlotObjectBase *handler)
254{
255 Q_D(QHttpServer);
256 auto slot = QtPrivate::SlotObjUniquePtr(handler);
257 if (!d->verifyThreadAffinity(contextObject: context))
258 return;
259 d->missingHandler = {.context: context, .slotObject: std::move(slot)};
260}
261
262/*!
263 Resets the handler to the default one that produces replies with
264 status 404 Not Found.
265*/
266void QHttpServer::clearMissingHandler()
267{
268 Q_D(QHttpServer);
269 d->missingHandler.slotObject.reset();
270}
271
272
273/*! \fn template <typename Functor> void QHttpServer::addAfterRequestHandler(const QObject *receiver, Functor &&slot)
274 Register a \a receiver and \a slot to be called after every request is
275 handled.
276
277 The \a slot has to implement the signature \c{void (*)(const QHttpServerRequest &,
278 QHttpServerResponse &)}.
279
280 The \a slot can also be a function pointer, non-mutable lambda, or any other
281 copiable callable with const call operator. In that case the \a receiver will
282 be a context object and the handler will be valid until the context
283 object is destroyed.
284
285 Example:
286
287 \code
288 QHttpServer server;
289 server.addAfterRequestHandler(&server, [] (const QHttpServerRequest &req, QHttpServerResponse &resp) {
290 resp.write(req.body(), "text/plain"_ba);
291 }
292 \endcode
293
294 \note These handlers won't be called for requests, processed by handlers
295 with \c {QHttpServerResponder&} argument.
296*/
297
298/*!
299 \internal
300*/
301void QHttpServer::addAfterRequestHandlerImpl(const QObject *context, QtPrivate::QSlotObjectBase *handler)
302{
303 Q_D(QHttpServer);
304 auto slot = QtPrivate::SlotObjUniquePtr(handler);
305 if (!d->verifyThreadAffinity(contextObject: context))
306 return;
307 d->afterRequestHandlers.push_back(x: {.context: context, .slotObject: std::move(slot)});
308}
309
310/*!
311 \internal
312*/
313void QHttpServer::sendResponse(QHttpServerResponse &&response, const QHttpServerRequest &request,
314 QHttpServerResponder &&responder)
315{
316 Q_D(QHttpServer);
317 for (auto &afterRequestHandler : d->afterRequestHandlers) {
318 if (afterRequestHandler.context && afterRequestHandler.slotObject &&
319 d->verifyThreadAffinity(contextObject: afterRequestHandler.context)) {
320 void *args[] = { nullptr, const_cast<QHttpServerRequest *>(&request), &response };
321 afterRequestHandler.slotObject->call(r: const_cast<QObject *>(afterRequestHandler.context.data()), a: args);
322 }
323 }
324 responder.sendResponse(response);
325}
326
327#if QT_CONFIG(future)
328void QHttpServer::sendResponse(QFuture<QHttpServerResponse> &&response,
329 const QHttpServerRequest &request, QHttpServerResponder &&responder)
330{
331 response.then(context: this,
332 function: [this, &request,
333 responder = std::move(responder)](QHttpServerResponse &&response) mutable {
334 sendResponse(response: std::move(response), request, responder: std::move(responder));
335 });
336}
337#endif // QT_CONFIG(future)
338
339/*!
340 \internal
341*/
342bool QHttpServer::handleRequest(const QHttpServerRequest &request, QHttpServerResponder &responder)
343{
344 Q_D(QHttpServer);
345 return d->router.handleRequest(request, responder);
346}
347
348/*!
349 \internal
350*/
351void QHttpServer::missingHandler(const QHttpServerRequest &request, QHttpServerResponder &responder)
352{
353 Q_D(QHttpServer);
354 return d->callMissingHandler(request, responder);
355}
356
357QT_END_NAMESPACE
358
359#include "moc_qhttpserver.cpp"
360

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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