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 | |
29 | QT_BEGIN_NAMESPACE |
30 | |
31 | Q_LOGGING_CATEGORY(lcHttpServer, "qt.httpserver" ) |
32 | |
33 | /*! |
34 | \internal |
35 | */ |
36 | QAbstractHttpServerPrivate::QAbstractHttpServerPrivate() |
37 | { |
38 | } |
39 | |
40 | /*! |
41 | \internal |
42 | */ |
43 | void 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 | */ |
71 | bool 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 | */ |
86 | void 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 | */ |
114 | QAbstractHttpServer::QAbstractHttpServer(QObject *parent) |
115 | : QAbstractHttpServer(*new QAbstractHttpServerPrivate, parent) |
116 | {} |
117 | |
118 | /*! |
119 | Destroys an instance of QAbstractHttpServer. |
120 | */ |
121 | QAbstractHttpServer::~QAbstractHttpServer() |
122 | = default; |
123 | |
124 | /*! |
125 | \internal |
126 | */ |
127 | QAbstractHttpServer::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 | */ |
146 | QList<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 | */ |
179 | bool 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 | */ |
217 | bool 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 | */ |
240 | QList<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 | */ |
251 | QList<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 | */ |
274 | bool 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 | */ |
291 | std::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 | */ |
300 | QHttpServerWebSocketUpgradeResponse |
301 | QAbstractHttpServer::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 | */ |
352 | void 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 | */ |
400 | QHttp2Configuration 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 | */ |
415 | void QAbstractHttpServer::setHttp2Configuration(const QHttp2Configuration &configuration) |
416 | { |
417 | Q_D(QAbstractHttpServer); |
418 | d->h2Configuration = configuration; |
419 | } |
420 | |
421 | #endif |
422 | |
423 | QT_END_NAMESPACE |
424 | |
425 | #include "moc_qabstracthttpserver.cpp" |
426 | |