1// Copyright (C) 2022 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/qabstracthttpserver.h>
6
7#include <QtHttpServer/qhttpserverrequest.h>
8#include <QtHttpServer/qhttpserverresponder.h>
9
10#include <private/qabstracthttpserver_p.h>
11#include <private/qhttpserverhttp1protocolhandler_p.h>
12#include <private/qhttpserverrequest_p.h>
13
14#include <QtCore/qloggingcategory.h>
15#include <QtNetwork/qtcpserver.h>
16#include <QtNetwork/qtcpsocket.h>
17#if QT_CONFIG(localserver)
18#include <QtNetwork/qlocalserver.h>
19#include <QtNetwork/qlocalsocket.h>
20#endif
21
22#if QT_CONFIG(ssl)
23#include <QtNetwork/qsslserver.h>
24#endif
25
26#if QT_CONFIG(http) && QT_CONFIG(ssl)
27#include <private/qhttpserverhttp2protocolhandler_p.h>
28#endif
29
30#include <algorithm>
31
32QT_BEGIN_NAMESPACE
33
34Q_STATIC_LOGGING_CATEGORY(lcHttpServer, "qt.httpserver")
35
36/*!
37 \internal
38*/
39QAbstractHttpServerPrivate::QAbstractHttpServerPrivate()
40{
41 restartHeartbeatTimer();
42}
43
44/*!
45 \internal
46*/
47void QAbstractHttpServerPrivate::handleNewConnections()
48{
49 Q_Q(QAbstractHttpServer);
50
51#if QT_CONFIG(ssl) && QT_CONFIG(http)
52 if (auto *sslServer = qobject_cast<QSslServer *>(object: q->sender())) {
53 while (auto socket = qobject_cast<QSslSocket *>(object: sslServer->nextPendingConnection())) {
54 if (socket->sslConfiguration().nextNegotiatedProtocol()
55 == QSslConfiguration::ALPNProtocolHTTP2) {
56 createHttp2Handler(socket);
57 } else {
58 createHttp1Handler(socket);
59 }
60 }
61 return;
62 }
63#endif
64
65 auto tcpServer = qobject_cast<QTcpServer *>(object: q->sender());
66 Q_ASSERT(tcpServer);
67
68 while (auto socket = tcpServer->nextPendingConnection())
69 createHttp1Handler(socket);
70}
71
72/*!
73 \internal
74*/
75bool QAbstractHttpServerPrivate::verifyThreadAffinity(const QObject *contextObject) const {
76 Q_Q(const QAbstractHttpServer);
77 if (contextObject && (contextObject->thread() != q->thread())) {
78 qCWarning(lcHttpServer, "QAbstractHttpServer: "
79 "the context object must reside in the same thread");
80 return false;
81 }
82 return true;
83}
84
85void QAbstractHttpServerPrivate::createHttp1Handler(QIODevice *socket)
86{
87 Q_Q(QAbstractHttpServer);
88
89 auto handler = new QHttpServerHttp1ProtocolHandler(q, socket, &requestFilter);
90 QObject::connect(sender: &heartbeatTimer, signal: &QTimer::timeout,
91 context: handler, slot: &QHttpServerHttp1ProtocolHandler::checkKeepAliveTimeout);
92}
93
94#if QT_CONFIG(ssl) && QT_CONFIG(http)
95void QAbstractHttpServerPrivate::createHttp2Handler(QIODevice *socket)
96{
97 Q_Q(QAbstractHttpServer);
98
99 auto handler = new QHttpServerHttp2ProtocolHandler(q, socket, &requestFilter);
100 QObject::connect(sender: &heartbeatTimer, signal: &QTimer::timeout,
101 context: handler, slot: &QHttpServerHttp2ProtocolHandler::checkKeepAliveTimeout);
102}
103#endif
104
105void QAbstractHttpServerPrivate::restartHeartbeatTimer()
106{
107 if (heartbeatTimer.isActive())
108 heartbeatTimer.stop();
109
110 const auto timerInterval = qMax(a: configuration.keepAliveTimeout() / 2,
111 b: std::chrono::seconds(5));
112 heartbeatTimer.start(value: timerInterval);
113}
114
115
116#if QT_CONFIG(localserver)
117/*!
118 \internal
119*/
120void QAbstractHttpServerPrivate::handleNewLocalConnections()
121{
122 Q_Q(QAbstractHttpServer);
123 auto localServer = qobject_cast<QLocalServer *>(object: q->sender());
124 Q_ASSERT(localServer);
125
126 while (auto socket = localServer->nextPendingConnection())
127 createHttp1Handler(socket);
128}
129#endif
130
131/*!
132 \class QAbstractHttpServer
133 \since 6.4
134 \inmodule QtHttpServer
135 \brief API to subclass to implement an HTTP server.
136
137 Subclass this class and override handleRequest() and missingHandler() to
138 create an HTTP server. Use bind() to start listening to all the incoming
139 connections to a server.
140
141 This is a low level API, see \l QHttpServer for a higher level API to
142 implement an HTTP server.
143*/
144
145/*!
146 Creates an instance of QAbstractHttpServer with the parent \a parent.
147*/
148QAbstractHttpServer::QAbstractHttpServer(QObject *parent)
149 : QAbstractHttpServer(*new QAbstractHttpServerPrivate, parent)
150{}
151
152/*!
153 Destroys an instance of QAbstractHttpServer.
154*/
155QAbstractHttpServer::~QAbstractHttpServer()
156 = default;
157
158/*!
159 \internal
160*/
161QAbstractHttpServer::QAbstractHttpServer(QAbstractHttpServerPrivate &dd, QObject *parent)
162 : QObject(dd, parent)
163{
164#if defined(QT_WEBSOCKETS_LIB)
165 Q_D(QAbstractHttpServer);
166 connect(&d->websocketServer, &QWebSocketServer::newConnection,
167 this, &QAbstractHttpServer::newWebSocketConnection);
168#endif
169}
170
171/*!
172 Returns the list of ports this instance of QAbstractHttpServer
173 is listening to.
174
175 This function has the same guarantee as QObject::children,
176 the latest server added is the last entry in the vector.
177
178 \sa servers()
179*/
180QList<quint16> QAbstractHttpServer::serverPorts() const
181{
182 QList<quint16> ports;
183 auto children = findChildren<QTcpServer *>();
184 ports.reserve(size: children.size());
185 std::transform(first: children.cbegin(), last: children.cend(), result: std::back_inserter(x&: ports),
186 unary_op: [](const QTcpServer *server) { return server->serverPort(); });
187 return ports;
188}
189
190/*!
191 Bind the given TCP \a server, over which the transmission happens,
192 to the HTTP server. It is possible to call this function
193 multiple times with different instances of TCP \a server to
194 handle multiple connections and ports, for example both SSL and
195 non-encrypted connections.
196
197 After calling this function, every \e new connection will be
198 handled and forwarded by the HTTP server.
199
200 It is the user's responsibility to call QTcpServer::listen() on
201 the \a server before calling this function. If \a server is not
202 listening, nothing will happen and \c false will be returned.
203
204 If successful the \a server will be parented to this HTTP server
205 and \c true is returned.
206
207 To allow usage of HTTP 2, bind to a QSslServer where
208 QSslConfiguration::setAllowedNextProtocols() has been called with
209 the arguments \c {{ QSslConfiguration::ALPNProtocolHTTP2 }}.
210
211 \sa QTcpServer, QTcpServer::listen(), QSslConfiguration::setAllowedNextProtocols()
212*/
213bool QAbstractHttpServer::bind(QTcpServer *server)
214{
215 Q_D(QAbstractHttpServer);
216 if (!server)
217 return false;
218
219 if (!server->isListening()) {
220 qCWarning(lcHttpServer) << "The TCP server" << server << "is not listening.";
221 return false;
222 }
223 server->setParent(this);
224 QObjectPrivate::connect(sender: server, signal: &QTcpServer::pendingConnectionAvailable, receiverPrivate: d,
225 slot: &QAbstractHttpServerPrivate::handleNewConnections,
226 type: Qt::UniqueConnection);
227 return true;
228}
229
230#if QT_CONFIG(localserver)
231/*!
232 Bind the given QLocalServer \a server, over which the transmission
233 happens, to the HTTP server. It is possible to call this function
234 multiple times with different instances of \a server to
235 handle multiple connections.
236
237 After calling this function, every \e new connection will be
238 handled and forwarded by the HTTP server.
239
240 It is the user's responsibility to call QLocalServer::listen() on
241 the \a server before calling this function. If \a server is not
242 listening, nothing will happen and \c false will be returned.
243
244 If the \a server is nullptr false is returned.
245
246 If successful the \a server will be parented to this HTTP server
247 and \c true is returned.
248
249 \sa QLocalServer, QLocalServer::listen()
250*/
251bool QAbstractHttpServer::bind(QLocalServer *server)
252{
253 Q_D(QAbstractHttpServer);
254 if (!server)
255 return false;
256
257 if (!server->isListening()) {
258 qCWarning(lcHttpServer) << "The local server" << server << "is not listening.";
259 return false;
260 }
261 server->setParent(this);
262 QObjectPrivate::connect(sender: server, signal: &QLocalServer::newConnection,
263 receiverPrivate: d, slot: &QAbstractHttpServerPrivate::handleNewLocalConnections,
264 type: Qt::UniqueConnection);
265 return true;
266}
267#endif
268
269/*!
270 Returns the TCP and SSL servers this HTTP server will handle connections from.
271
272 \sa serverPorts()
273 */
274QList<QTcpServer *> QAbstractHttpServer::servers() const
275{
276 return findChildren<QTcpServer *>();
277}
278
279#if QT_CONFIG(localserver)
280/*!
281 Returns the local servers of this HTTP server.
282
283 \sa serverPorts()
284 */
285QList<QLocalServer *> QAbstractHttpServer::localServers() const
286{
287 return findChildren<QLocalServer *>();
288}
289#endif
290
291#if defined(QT_WEBSOCKETS_LIB)
292/*!
293 \fn QAbstractHttpServer::newWebSocketConnection()
294 This signal is emitted every time a new WebSocket connection is
295 available.
296
297 \sa hasPendingWebSocketConnections(), nextPendingWebSocketConnection(),
298 addWebSocketUpgradeVerifier()
299*/
300
301/*!
302 Returns \c true if the server has pending WebSocket connections;
303 otherwise returns \c false.
304
305 \sa newWebSocketConnection(), nextPendingWebSocketConnection(),
306 addWebSocketUpgradeVerifier()
307*/
308bool QAbstractHttpServer::hasPendingWebSocketConnections() const
309{
310 Q_D(const QAbstractHttpServer);
311 return d->websocketServer.hasPendingConnections();
312}
313
314/*!
315 Returns the next pending connection as a connected QWebSocket
316 object. \nullptr is returned if this function is called when
317 there are no pending connections.
318
319 \note The returned QWebSocket object cannot be used from another
320 thread.
321
322 \sa newWebSocketConnection(), hasPendingWebSocketConnections(),
323 addWebSocketUpgradeVerifier()
324*/
325std::unique_ptr<QWebSocket> QAbstractHttpServer::nextPendingWebSocketConnection()
326{
327 Q_D(QAbstractHttpServer);
328 return std::unique_ptr<QWebSocket>(d->websocketServer.nextPendingConnection());
329}
330
331/*!
332 \internal
333*/
334QHttpServerWebSocketUpgradeResponse
335QAbstractHttpServer::verifyWebSocketUpgrade(const QHttpServerRequest &request) const
336{
337 Q_D(const QAbstractHttpServer);
338 QScopedValueRollback guard(d->handlingWebSocketUpgrade, true);
339 for (auto &verifier : d->webSocketUpgradeVerifiers) {
340 if (verifier.context && verifier.slotObject && d->verifyThreadAffinity(verifier.context)) {
341 auto response = QHttpServerWebSocketUpgradeResponse::passToNext();
342 void *args[] = { &response, const_cast<QHttpServerRequest *>(&request) };
343 verifier.slotObject->call(const_cast<QObject *>(verifier.context.data()), args);
344 if (response.type() != QHttpServerWebSocketUpgradeResponse::ResponseType::PassToNext)
345 return response;
346 }
347 }
348 return QHttpServerWebSocketUpgradeResponse::passToNext();
349}
350
351/*!
352 \fn template <typename Handler> void QAbstractHttpServer::addWebSocketUpgradeVerifier(const QObject *context, Handler &&func)
353
354 Adds a callback function \a func that verifies incoming WebSocket
355 upgrades. It is called using the provided \a context object.
356 An upgrade succeeds if at least one registered callback returns
357 \l{QHttpServerWebSocketUpgradeResponse::ResponseType::}{Accept} and
358 no prior callback has returned
359 \l{QHttpServerWebSocketUpgradeResponse::ResponseType::}{Deny}. If no
360 callbacks are registered, or all return
361 \l{QHttpServerWebSocketUpgradeResponse::ResponseType::}{PassToNext},
362 the missingHandler() function is called. Callbacks are executed in the
363 order they were registered, and they cannot call this function
364 themselves.
365
366 \note The \a func has to implement the signature
367 \c{QHttpServerWebSocketUpgradeResponse (*)(const QHttpServerRequest &)}.
368
369 \code
370 server.addWebSocketUpgradeVerifier(
371 &server, [](const QHttpServerRequest &request) {
372 if (request.url().path() == "/allowed"_L1)
373 return QHttpServerWebSocketUpgradeResponse::accept();
374 else
375 return QHttpServerWebSocketUpgradeResponse::passToNext();
376 });
377 \endcode
378
379 \since 6.8
380 \sa QHttpServerRequest, QHttpServerWebSocketUpgradeResponse, hasPendingWebSocketConnections(),
381 nextPendingWebSocketConnection(), newWebSocketConnection(), missingHandler()
382*/
383
384/*!
385 \internal
386*/
387void QAbstractHttpServer::addWebSocketUpgradeVerifierImpl(const QObject *context,
388 QtPrivate::QSlotObjectBase *slotObjRaw)
389{
390 QtPrivate::SlotObjUniquePtr slotObj{slotObjRaw}; // adopts
391 Q_ASSERT(slotObj);
392 Q_D(QAbstractHttpServer);
393 if (d->handlingWebSocketUpgrade) {
394 qWarning("Registering WebSocket upgrade verifiers while handling them is not allowed");
395 return;
396 }
397 d->webSocketUpgradeVerifiers.push_back({context, std::move(slotObj)});
398}
399
400#endif
401
402/*!
403 \fn QAbstractHttpServer::handleRequest(const QHttpServerRequest &request,
404 QHttpServerResponder &responder)
405 Override this function to handle each incoming \a request, by examining
406 the \a request and sending the appropriate response back to \a responder.
407 Return \c true if the \a request was handled successfully. If this method
408 returns \c false, missingHandler() will be called afterwards.
409*/
410
411/*!
412 \fn QAbstractHttpServer::missingHandler(const QHttpServerRequest &request,
413 QHttpServerResponder &responder)
414
415 Override this function to handle each incoming \a request that was not
416 handled by \l handleRequest(). This function is called whenever \l
417 handleRequest() returns \c false, or if there is a WebSocket upgrade
418 attempt and either there are no connections to newWebSocketConnection() or
419 there are no matching WebSocket verifiers. The \a request and \a responder
420 parameters are the same as handleRequest() was called with.
421
422 \sa handleRequest(), addWebSocketUpgradeVerifier()
423*/
424
425#if QT_CONFIG(ssl)
426/*!
427 \since 6.8
428
429 Returns server's HTTP/2 configuration parameters.
430
431 \sa setHttp2Configuration()
432*/
433QHttp2Configuration QAbstractHttpServer::http2Configuration() const
434{
435 Q_D(const QAbstractHttpServer);
436 return d->h2Configuration;
437}
438
439/*!
440 \since 6.8
441
442 Sets server's HTTP/2 configuration parameters.
443
444 The next HTTP/2 connection will use the given \a configuration.
445
446 \sa http2Configuration()
447*/
448void QAbstractHttpServer::setHttp2Configuration(const QHttp2Configuration &configuration)
449{
450 Q_D(QAbstractHttpServer);
451 d->h2Configuration = configuration;
452}
453
454#endif
455
456/*!
457 \since 6.9
458
459 Sets this server's general configuration parameters to \a config.
460
461 \note The new configuration will be applied both to already established
462 connections and all next connections.
463
464 \sa configuration()
465*/
466void QAbstractHttpServer::setConfiguration(const QHttpServerConfiguration &config)
467{
468 Q_D(QAbstractHttpServer);
469 d->configuration = config;
470 d->requestFilter.setConfiguration(config);
471 d->restartHeartbeatTimer();
472}
473
474/*!
475 \since 6.9
476
477 Returns this server's general configuration parameters.
478
479 \sa setConfiguration()
480*/
481QHttpServerConfiguration QAbstractHttpServer::configuration() const
482{
483 Q_D(const QAbstractHttpServer);
484 return d->configuration;
485}
486
487QT_END_NAMESPACE
488
489#include "moc_qabstracthttpserver.cpp"
490

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