1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include <private/qqmldebugserverconnection_p.h> |
5 | #include <private/qqmldebugserver_p.h> |
6 | |
7 | #include <QtCore/qplugin.h> |
8 | #include <QtNetwork/qtcpserver.h> |
9 | #include <QtNetwork/qtcpsocket.h> |
10 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | class QTcpServerConnection : public QQmlDebugServerConnection |
14 | { |
15 | Q_OBJECT |
16 | Q_DISABLE_COPY(QTcpServerConnection) |
17 | |
18 | public: |
19 | QTcpServerConnection(); |
20 | ~QTcpServerConnection() override; |
21 | |
22 | void setServer(QQmlDebugServer *server) override; |
23 | bool setPortRange(int portFrom, int portTo, bool block, const QString &hostaddress) override; |
24 | bool setFileName(const QString &fileName, bool block) override; |
25 | |
26 | bool isConnected() const override; |
27 | void disconnect() override; |
28 | |
29 | void waitForConnection() override; |
30 | void flush() override; |
31 | |
32 | private: |
33 | void newConnection(); |
34 | bool listen(); |
35 | |
36 | int m_portFrom = 0; |
37 | int m_portTo = 0; |
38 | bool m_block = false; |
39 | QString m_hostaddress; |
40 | QTcpSocket *m_socket = nullptr; |
41 | QTcpServer *m_tcpServer = nullptr; |
42 | QQmlDebugServer *m_debugServer = nullptr; |
43 | }; |
44 | |
45 | QTcpServerConnection::QTcpServerConnection() {} |
46 | |
47 | QTcpServerConnection::~QTcpServerConnection() |
48 | { |
49 | if (isConnected()) |
50 | disconnect(); |
51 | } |
52 | |
53 | void QTcpServerConnection::setServer(QQmlDebugServer *server) |
54 | { |
55 | m_debugServer = server; |
56 | } |
57 | |
58 | bool QTcpServerConnection::isConnected() const |
59 | { |
60 | return m_socket && m_socket->state() == QTcpSocket::ConnectedState; |
61 | } |
62 | |
63 | void QTcpServerConnection::disconnect() |
64 | { |
65 | while (m_socket && m_socket->bytesToWrite() > 0) { |
66 | if (!m_socket->waitForBytesWritten()) { |
67 | qWarning(msg: "QML Debugger: Failed to send remaining %lld bytes on disconnect." , |
68 | m_socket->bytesToWrite()); |
69 | break; |
70 | } |
71 | } |
72 | |
73 | m_socket->deleteLater(); |
74 | m_socket = nullptr; |
75 | } |
76 | |
77 | bool QTcpServerConnection::setPortRange(int portFrom, int portTo, bool block, |
78 | const QString &hostaddress) |
79 | { |
80 | m_portFrom = portFrom; |
81 | m_portTo = portTo; |
82 | m_block = block; |
83 | m_hostaddress = hostaddress; |
84 | |
85 | return listen(); |
86 | } |
87 | |
88 | bool QTcpServerConnection::setFileName(const QString &fileName, bool block) |
89 | { |
90 | Q_UNUSED(fileName); |
91 | Q_UNUSED(block); |
92 | return false; |
93 | } |
94 | |
95 | void QTcpServerConnection::waitForConnection() |
96 | { |
97 | m_tcpServer->waitForNewConnection(msec: -1); |
98 | } |
99 | |
100 | void QTcpServerConnection::flush() |
101 | { |
102 | if (m_socket) |
103 | m_socket->flush(); |
104 | } |
105 | |
106 | bool QTcpServerConnection::listen() |
107 | { |
108 | m_tcpServer = new QTcpServer(this); |
109 | QObject::connect(sender: m_tcpServer, signal: &QTcpServer::newConnection, |
110 | context: this, slot: &QTcpServerConnection::newConnection); |
111 | QHostAddress hostaddress; |
112 | if (!m_hostaddress.isEmpty()) { |
113 | if (!hostaddress.setAddress(m_hostaddress)) { |
114 | hostaddress = QHostAddress::Any; |
115 | qDebug(msg: "QML Debugger: Incorrect host address provided. So accepting connections " |
116 | "from any host." ); |
117 | } |
118 | } else { |
119 | hostaddress = QHostAddress::Any; |
120 | } |
121 | int port = m_portFrom; |
122 | do { |
123 | if (m_tcpServer->listen(address: hostaddress, port)) { |
124 | qDebug(msg: "QML Debugger: Waiting for connection on port %d..." , port); |
125 | break; |
126 | } |
127 | ++port; |
128 | } while (port <= m_portTo); |
129 | if (port > m_portTo) { |
130 | if (m_portFrom == m_portTo) |
131 | qWarning(msg: "QML Debugger: Unable to listen to port %d." , m_portFrom); |
132 | else |
133 | qWarning(msg: "QML Debugger: Unable to listen to ports %d - %d." , m_portFrom, m_portTo); |
134 | return false; |
135 | } else { |
136 | return true; |
137 | } |
138 | } |
139 | |
140 | void QTcpServerConnection::newConnection() |
141 | { |
142 | if (m_socket && m_socket->peerPort()) { |
143 | qWarning(msg: "QML Debugger: Another client is already connected." ); |
144 | QTcpSocket *faultyConnection = m_tcpServer->nextPendingConnection(); |
145 | delete faultyConnection; |
146 | return; |
147 | } |
148 | |
149 | delete m_socket; |
150 | m_socket = m_tcpServer->nextPendingConnection(); |
151 | m_socket->setParent(this); |
152 | m_debugServer->setDevice(m_socket); |
153 | } |
154 | |
155 | |
156 | class QTcpServerConnectionFactory : public QQmlDebugServerConnectionFactory |
157 | { |
158 | Q_OBJECT |
159 | Q_PLUGIN_METADATA(IID QQmlDebugServerConnectionFactory_iid FILE "qtcpserverconnection.json" ) |
160 | Q_INTERFACES(QQmlDebugServerConnectionFactory) |
161 | public: |
162 | QQmlDebugServerConnection *create(const QString &key) override; |
163 | }; |
164 | |
165 | QQmlDebugServerConnection *QTcpServerConnectionFactory::create(const QString &key) |
166 | { |
167 | return (key == QLatin1String("QTcpServerConnection" ) ? new QTcpServerConnection : nullptr); |
168 | } |
169 | |
170 | QT_END_NAMESPACE |
171 | |
172 | #include "qtcpserverconnection.moc" |
173 | |