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

Provided by KDAB

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

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