1 | // Copyright (C) 2022 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include <private/qhttpserverstream_p.h> |
5 | |
6 | #include <QtHttpServer/qhttpserverrequest.h> |
7 | #include <QtHttpServer/qabstracthttpserver.h> |
8 | #include <QtHttpServer/qhttpserverresponder.h> |
9 | #include <QtCore/qmetaobject.h> |
10 | #include <QtCore/qthread.h> |
11 | #include <QtCore/qloggingcategory.h> |
12 | #include <QtNetwork/qtcpsocket.h> |
13 | |
14 | #include <private/qhttpserverrequest_p.h> |
15 | #include <private/qabstracthttpserver_p.h> |
16 | |
17 | QT_BEGIN_NAMESPACE |
18 | |
19 | Q_LOGGING_CATEGORY(lcHttpServerStream, "qt.httpserver.stream" ) |
20 | |
21 | void QHttpServerStream::handleReadyRead() |
22 | { |
23 | if (handlingRequest) |
24 | return; |
25 | |
26 | if (!socket->isTransactionStarted()) |
27 | socket->startTransaction(); |
28 | |
29 | if (!request.d->parse(socket)) { |
30 | socket->disconnectFromHost(); |
31 | return; |
32 | } |
33 | |
34 | if (request.d->state != QHttpServerRequestPrivate::State::AllDone) |
35 | return; // Partial read |
36 | |
37 | qCDebug(lcHttpServerStream) << "Request:" << request; |
38 | |
39 | QHttpServerResponder responder(this); |
40 | |
41 | #if defined(QT_WEBSOCKETS_LIB) |
42 | if (request.d->upgrade) { // Upgrade |
43 | const auto &upgradeValue = request.value(QByteArrayLiteral("upgrade" )); |
44 | if (upgradeValue.compare(QByteArrayLiteral("websocket" ), Qt::CaseInsensitive) == 0) { |
45 | static const auto signal = |
46 | QMetaMethod::fromSignal(&QAbstractHttpServer::newWebSocketConnection); |
47 | if (server->isSignalConnected(signal) && server->handleRequest(request, responder)) { |
48 | // Socket will now be managed by websocketServer |
49 | socket->disconnect(); |
50 | socket->rollbackTransaction(); |
51 | server->d_func()->websocketServer.handleConnection(socket); |
52 | Q_EMIT socket->readyRead(); |
53 | } else { |
54 | qWarning(lcHttpServerStream, |
55 | "WebSocket received but no slots connected to " |
56 | "QWebSocketServer::newConnection or request not handled" ); |
57 | server->missingHandler(request, std::move(responder)); |
58 | socket->disconnectFromHost(); |
59 | } |
60 | return; |
61 | } |
62 | } |
63 | #endif |
64 | |
65 | socket->commitTransaction(); |
66 | |
67 | if (!server->handleRequest(request, responder)) |
68 | server->missingHandler(request, responder: std::move(responder)); |
69 | |
70 | if (handlingRequest) |
71 | disconnect(sender: socket, signal: &QTcpSocket::readyRead, receiver: this, slot: &QHttpServerStream::handleReadyRead); |
72 | else if (socket->bytesAvailable() > 0) |
73 | QMetaObject::invokeMethod(object: socket, function: &QAbstractSocket::readyRead, type: Qt::QueuedConnection); |
74 | } |
75 | |
76 | void QHttpServerStream::socketDisconnected() |
77 | { |
78 | if (!handlingRequest) |
79 | deleteLater(); |
80 | } |
81 | |
82 | QHttpServerStream::QHttpServerStream(QAbstractHttpServer *server, QTcpSocket *socket) |
83 | : QObject(server), |
84 | server(server), |
85 | socket(socket), |
86 | request(socket->peerAddress(), socket->peerPort(), socket->localAddress(), |
87 | socket->localPort()) |
88 | { |
89 | socket->setParent(this); |
90 | |
91 | qCDebug(lcHttpServerStream) << "Connection from:" << socket->peerAddress(); |
92 | connect(sender: socket, signal: &QTcpSocket::readyRead, context: this, slot: &QHttpServerStream::handleReadyRead); |
93 | connect(sender: socket, signal: &QTcpSocket::disconnected, context: this, slot: &QHttpServerStream::socketDisconnected); |
94 | } |
95 | |
96 | void QHttpServerStream::write(const QByteArray &ba) |
97 | { |
98 | Q_ASSERT(QThread::currentThread() == thread()); |
99 | socket->write(data: ba); |
100 | } |
101 | |
102 | void QHttpServerStream::write(const char *body, qint64 size) |
103 | { |
104 | Q_ASSERT(QThread::currentThread() == thread()); |
105 | socket->write(data: body, len: size); |
106 | } |
107 | |
108 | void QHttpServerStream::responderDestroyed() |
109 | { |
110 | Q_ASSERT(QThread::currentThread() == thread()); |
111 | Q_ASSERT(handlingRequest); |
112 | handlingRequest = false; |
113 | |
114 | if (socket->state() != QAbstractSocket::ConnectedState) { |
115 | deleteLater(); |
116 | } else { |
117 | connect(sender: socket, signal: &QTcpSocket::readyRead, context: this, slot: &QHttpServerStream::handleReadyRead); |
118 | QMetaObject::invokeMethod(object: socket, function: &QAbstractSocket::readyRead, type: Qt::QueuedConnection); |
119 | } |
120 | } |
121 | |
122 | QT_END_NAMESPACE |
123 | |