1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 Kurt Pattyn <pattyn.kurt@gmail.com>. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtWebSockets module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | /*! |
41 | \qmltype WebSocket |
42 | \instantiates QQmlWebSocket |
43 | \since 5.3 |
44 | |
45 | \inqmlmodule QtWebSockets |
46 | \ingroup websockets-qml |
47 | \brief QML interface to QWebSocket. |
48 | |
49 | WebSockets is a web technology providing full-duplex communications channels over a |
50 | single TCP connection. |
51 | The WebSocket protocol was standardized by the IETF as \l {RFC 6455} in 2011. |
52 | */ |
53 | |
54 | /*! |
55 | \qmlproperty QUrl WebSocket::url |
56 | Server url to connect to. The url must have one of 2 schemes: \e ws:// or \e wss://. |
57 | When not supplied, then \e ws:// is used. |
58 | */ |
59 | |
60 | /*! |
61 | \qmlproperty Status WebSocket::status |
62 | Status of the WebSocket. |
63 | |
64 | The status can have the following values: |
65 | \list |
66 | \li WebSocket.Connecting |
67 | \li WebSocket.Open |
68 | \li WebSocket.Closing |
69 | \li WebSocket.Closed |
70 | \li WebSocket.Error |
71 | \endlist |
72 | */ |
73 | |
74 | /*! |
75 | \qmlproperty QString WebSocket::errorString |
76 | Contains a description of the last error that occurred. When no error occurrred, |
77 | this string is empty. |
78 | */ |
79 | |
80 | /*! |
81 | \qmlproperty bool WebSocket::active |
82 | When set to true, a connection is made to the server with the given url. |
83 | When set to false, the connection is closed. |
84 | The default value is false. |
85 | */ |
86 | |
87 | /*! |
88 | \qmlsignal WebSocket::textMessageReceived(QString message) |
89 | This signal is emitted when a text message is received. |
90 | \a message contains the bytes received. |
91 | */ |
92 | |
93 | /*! |
94 | \qmlsignal WebSocket::binaryMessageReceived(QString message) |
95 | \since 5.8 |
96 | This signal is emitted when a binary message is received. |
97 | \a message contains the bytes received. |
98 | */ |
99 | |
100 | /*! |
101 | \qmlsignal WebSocket::statusChanged(Status status) |
102 | This signal is emitted when the status of the WebSocket changes. |
103 | The \a status argument provides the current status. |
104 | |
105 | \sa {QtWebSockets::}{WebSocket::status} |
106 | */ |
107 | |
108 | /*! |
109 | \qmlmethod void WebSocket::sendTextMessage(string message) |
110 | Sends \a message to the server. |
111 | */ |
112 | |
113 | /*! |
114 | \qmlmethod void WebSocket::sendBinaryMessage(ArrayBuffer message) |
115 | \since 5.8 |
116 | Sends the parameter \a message to the server. |
117 | */ |
118 | |
119 | #include "qqmlwebsocket.h" |
120 | #include <QtWebSockets/QWebSocket> |
121 | |
122 | QT_BEGIN_NAMESPACE |
123 | |
124 | QQmlWebSocket::QQmlWebSocket(QObject *parent) : |
125 | QObject(parent), |
126 | m_webSocket(), |
127 | m_status(Closed), |
128 | m_url(), |
129 | m_isActive(false), |
130 | m_componentCompleted(true), |
131 | m_errorString() |
132 | { |
133 | } |
134 | |
135 | QQmlWebSocket::QQmlWebSocket(QWebSocket *socket, QObject *parent) : |
136 | QObject(parent), |
137 | m_status(Closed), |
138 | m_url(socket->requestUrl()), |
139 | m_isActive(true), |
140 | m_componentCompleted(true), |
141 | m_errorString(socket->errorString()) |
142 | { |
143 | setSocket(socket); |
144 | onStateChanged(state: socket->state()); |
145 | } |
146 | |
147 | QQmlWebSocket::~QQmlWebSocket() |
148 | { |
149 | } |
150 | |
151 | qint64 QQmlWebSocket::sendTextMessage(const QString &message) |
152 | { |
153 | if (m_status != Open) { |
154 | setErrorString(tr(s: "Messages can only be sent when the socket is open." )); |
155 | setStatus(Error); |
156 | return 0; |
157 | } |
158 | return m_webSocket->sendTextMessage(message); |
159 | } |
160 | |
161 | qint64 QQmlWebSocket::sendBinaryMessage(const QByteArray &message) |
162 | { |
163 | if (m_status != Open) { |
164 | setErrorString(tr(s: "Messages can only be sent when the socket is open." )); |
165 | setStatus(Error); |
166 | return 0; |
167 | } |
168 | return m_webSocket->sendBinaryMessage(data: message); |
169 | } |
170 | |
171 | QUrl QQmlWebSocket::url() const |
172 | { |
173 | return m_url; |
174 | } |
175 | |
176 | void QQmlWebSocket::setUrl(const QUrl &url) |
177 | { |
178 | if (m_url == url) { |
179 | return; |
180 | } |
181 | if (m_webSocket && (m_status == Open)) { |
182 | m_webSocket->close(); |
183 | } |
184 | m_url = url; |
185 | Q_EMIT urlChanged(); |
186 | open(); |
187 | } |
188 | |
189 | QQmlWebSocket::Status QQmlWebSocket::status() const |
190 | { |
191 | return m_status; |
192 | } |
193 | |
194 | QString QQmlWebSocket::errorString() const |
195 | { |
196 | return m_errorString; |
197 | } |
198 | |
199 | void QQmlWebSocket::classBegin() |
200 | { |
201 | m_componentCompleted = false; |
202 | m_errorString = tr(s: "QQmlWebSocket is not ready." ); |
203 | m_status = Closed; |
204 | } |
205 | |
206 | void QQmlWebSocket::componentComplete() |
207 | { |
208 | setSocket(new QWebSocket); |
209 | |
210 | m_componentCompleted = true; |
211 | |
212 | open(); |
213 | } |
214 | |
215 | void QQmlWebSocket::setSocket(QWebSocket *socket) |
216 | { |
217 | m_webSocket.reset(other: socket); |
218 | if (m_webSocket) { |
219 | // explicit ownership via QScopedPointer |
220 | m_webSocket->setParent(Q_NULLPTR); |
221 | connect(sender: m_webSocket.data(), signal: &QWebSocket::textMessageReceived, |
222 | receiver: this, slot: &QQmlWebSocket::textMessageReceived); |
223 | connect(sender: m_webSocket.data(), signal: &QWebSocket::binaryMessageReceived, |
224 | receiver: this, slot: &QQmlWebSocket::binaryMessageReceived); |
225 | typedef void (QWebSocket::* ErrorSignal)(QAbstractSocket::SocketError); |
226 | connect(sender: m_webSocket.data(), signal: static_cast<ErrorSignal>(&QWebSocket::error), |
227 | receiver: this, slot: &QQmlWebSocket::onError); |
228 | connect(sender: m_webSocket.data(), signal: &QWebSocket::stateChanged, |
229 | receiver: this, slot: &QQmlWebSocket::onStateChanged); |
230 | } |
231 | } |
232 | |
233 | void QQmlWebSocket::onError(QAbstractSocket::SocketError error) |
234 | { |
235 | Q_UNUSED(error) |
236 | setErrorString(m_webSocket->errorString()); |
237 | setStatus(Error); |
238 | } |
239 | |
240 | void QQmlWebSocket::onStateChanged(QAbstractSocket::SocketState state) |
241 | { |
242 | switch (state) |
243 | { |
244 | case QAbstractSocket::ConnectingState: |
245 | case QAbstractSocket::BoundState: |
246 | case QAbstractSocket::HostLookupState: |
247 | { |
248 | setStatus(Connecting); |
249 | break; |
250 | } |
251 | case QAbstractSocket::UnconnectedState: |
252 | { |
253 | setStatus(Closed); |
254 | break; |
255 | } |
256 | case QAbstractSocket::ConnectedState: |
257 | { |
258 | setStatus(Open); |
259 | break; |
260 | } |
261 | case QAbstractSocket::ClosingState: |
262 | { |
263 | setStatus(Closing); |
264 | break; |
265 | } |
266 | default: |
267 | { |
268 | setStatus(Connecting); |
269 | break; |
270 | } |
271 | } |
272 | } |
273 | |
274 | void QQmlWebSocket::setStatus(QQmlWebSocket::Status status) |
275 | { |
276 | if (m_status == status) { |
277 | return; |
278 | } |
279 | m_status = status; |
280 | if (status != Error) { |
281 | setErrorString(); |
282 | } |
283 | Q_EMIT statusChanged(status: m_status); |
284 | } |
285 | |
286 | void QQmlWebSocket::setActive(bool active) |
287 | { |
288 | if (m_isActive == active) { |
289 | return; |
290 | } |
291 | m_isActive = active; |
292 | Q_EMIT activeChanged(isActive: m_isActive); |
293 | if (!m_componentCompleted) { |
294 | return; |
295 | } |
296 | if (m_isActive) { |
297 | open(); |
298 | } else { |
299 | close(); |
300 | } |
301 | } |
302 | |
303 | bool QQmlWebSocket::isActive() const |
304 | { |
305 | return m_isActive; |
306 | } |
307 | |
308 | void QQmlWebSocket::open() |
309 | { |
310 | if (m_componentCompleted && m_isActive && m_url.isValid() && Q_LIKELY(m_webSocket)) { |
311 | m_webSocket->open(url: m_url); |
312 | } |
313 | } |
314 | |
315 | void QQmlWebSocket::close() |
316 | { |
317 | if (m_componentCompleted && Q_LIKELY(m_webSocket)) { |
318 | m_webSocket->close(); |
319 | } |
320 | } |
321 | |
322 | void QQmlWebSocket::setErrorString(QString errorString) |
323 | { |
324 | if (m_errorString == errorString) { |
325 | return; |
326 | } |
327 | m_errorString = errorString; |
328 | Q_EMIT errorStringChanged(errorString: m_errorString); |
329 | } |
330 | |
331 | QT_END_NAMESPACE |
332 | |