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
17QT_BEGIN_NAMESPACE
18
19Q_LOGGING_CATEGORY(lcHttpServerStream, "qt.httpserver.stream")
20
21void 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
76void QHttpServerStream::socketDisconnected()
77{
78 if (!handlingRequest)
79 deleteLater();
80}
81
82QHttpServerStream::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
96void QHttpServerStream::write(const QByteArray &ba)
97{
98 Q_ASSERT(QThread::currentThread() == thread());
99 socket->write(data: ba);
100}
101
102void QHttpServerStream::write(const char *body, qint64 size)
103{
104 Q_ASSERT(QThread::currentThread() == thread());
105 socket->write(data: body, len: size);
106}
107
108void 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
122QT_END_NAMESPACE
123

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