Overte C++ Documentation
WebSocketClass.h
1 //
2 // WebSocketClass.h
3 // libraries/script-engine/src/
4 //
5 // Created by Thijs Wenker on 8/4/15.
6 // Copyright (c) 2015 High Fidelity, Inc. All rights reserved.
7 // Copyright 2023 Overte e.V.
8 //
9 // Distributed under the Apache License, Version 2.0.
10 // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
11 // SPDX-License-Identifier: Apache-2.0
12 //
13 
16 
17 #ifndef hifi_WebSocketClass_h
18 #define hifi_WebSocketClass_h
19 
20 #include <QObject>
21 #include <QWebSocket>
22 
23 #include "ScriptValue.h"
24 
25 class ScriptContext;
26 class ScriptEngine;
27 
28 /*@jsdoc
29  * Provides a bi-directional, event-driven communication session between the script and another WebSocket connection. It is a
30  * near-complete implementation of the WebSocket API described in the Mozilla docs:
31  * <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket">https://developer.mozilla.org/en-US/docs/Web/API/WebSocket</a>.
32  *
33  * <p>Create using <code>new WebSocket(...)</code> in Interface, client entity, avatar, and server entity scripts, or the
34  * {@link WebSocketServer} class in server entity and assignment client scripts.
35  *
36  * <p><strong>Note:</strong> Does not support secure, <code>wss:</code> protocol.</p>
37  *
38  * @class WebSocket
39  * @param {string|WebSocket} urlOrWebSocket - The URL to connect to or an existing {@link WebSocket} to reuse the connection of.
40  *
41  * @hifi-interface
42  * @hifi-client-entity
43  * @hifi-avatar
44  * @hifi-server-entity
45  * @hifi-assignment-client
46  *
47  * @property {string} binaryType="blob" - <em>Not used.</em>
48  * @property {number} bufferedAmount=0 - <em>Not implemented.</em> <em>Read-only.</em>
49  * @property {string} extensions="" - <em>Not implemented.</em> <em>Read-only.</em>
50  *
51  * @property {WebSocket~onOpenCallback} onopen - Function called when the connection opens.
52  * @property {WebSocket~onMessageCallback} onmessage - Function called when a message is received.
53  * @property {WebSocket~onErrorCallback} onerror - Function called when an error occurs.
54  * @property {WebSocket~onCloseCallback} onclose - Function called when the connection closes.
55  *
56  * @property {string} protocol="" - <em>Not implemented.</em> <em>Read-only.</em>
57  * @property {WebSocket.ReadyState} readyState - The state of the connection. <em>Read-only.</em>
58  * @property {string} url - The URL to connect to. <em>Read-only.</em>
59  *
60  * @property {WebSocket.ReadyState} CONNECTING - The connection is opening. <em>Read-only.</em>
61  * @property {WebSocket.ReadyState} OPEN - The connection is open. <em>Read-only.</em>
62  * @property {WebSocket.ReadyState} CLOSING - The connection is closing. <em>Read-only.</em>
63  * @property {WebSocket.ReadyState} CLOSED - The connection is closed. <em>Read-only.</em>
64  *
65  * @example <caption>Echo a message off websocket.org.</caption>
66  * print("Create WebSocket");
67  * var WEBSOCKET_PING_URL = "ws://echo.websocket.org";
68  * var webSocket = new WebSocket(WEBSOCKET_PING_URL);
69  *
70  * webSocket.onclose = function (data) {
71  * print("WebSocket closed");
72  * print("Ready state =", webSocket.readyState); // 3
73  * };
74  *
75  * webSocket.onmessage = function (data) {
76  * print("Message received:", data.data);
77  *
78  * print("Close WebSocket");
79  * webSocket.close();
80  * };
81  *
82  * webSocket.onopen = function () {
83  * print("WebSocket opened");
84  * print("Ready state =", webSocket.readyState); // 1
85  *
86  * print("Send a test message");
87  * webSocket.send("Test message");
88  * };
89  */
91 class WebSocketClass : public QObject {
92  Q_OBJECT
93  Q_PROPERTY(QString binaryType READ getBinaryType WRITE setBinaryType)
94  Q_PROPERTY(ulong bufferedAmount READ getBufferedAmount)
95  Q_PROPERTY(QString extensions READ getExtensions)
96 
97  Q_PROPERTY(ScriptValue onclose READ getOnClose WRITE setOnClose)
98  Q_PROPERTY(ScriptValue onerror READ getOnError WRITE setOnError)
99  Q_PROPERTY(ScriptValue onmessage READ getOnMessage WRITE setOnMessage)
100  Q_PROPERTY(ScriptValue onopen READ getOnOpen WRITE setOnOpen)
101 
102  Q_PROPERTY(QString protocol READ getProtocol)
103  Q_PROPERTY(WebSocketClass::ReadyState readyState READ getReadyState)
104  Q_PROPERTY(QString url READ getURL)
105 
106  Q_PROPERTY(WebSocketClass::ReadyState CONNECTING READ getConnecting CONSTANT)
107  Q_PROPERTY(WebSocketClass::ReadyState OPEN READ getOpen CONSTANT)
108  Q_PROPERTY(WebSocketClass::ReadyState CLOSING READ getClosing CONSTANT)
109  Q_PROPERTY(WebSocketClass::ReadyState CLOSED READ getClosed CONSTANT)
110 
111 public:
112  WebSocketClass(ScriptEngine* engine, QString url);
113  WebSocketClass(ScriptEngine* engine, QWebSocket* qWebSocket);
114  ~WebSocketClass();
115 
116  static ScriptValue constructor(ScriptContext* context, ScriptEngine* engine);
117 
118  /*@jsdoc
119  * The state of a WebSocket connection.
120  * <table>
121  * <thead>
122  * <tr><th>Value</th><th>Name</th><th>Description</th></tr>
123  * </thead>
124  * <tbody>
125  * <tr><td><code>0</code></td><td>CONNECTING</td><td>The connection is opening.</td></tr>
126  * <tr><td><code>1</code></td><td>OPEN</td><td>The connection is open.</td></tr>
127  * <tr><td><code>2</code></td><td>CLOSING</td><td>The connection is closing.</td></tr>
128  * <tr><td><code>3</code></td><td>CLOSED</td><td>The connection is closed.</td></tr>
129  * </tbody>
130  * </table>
131  * @typedef {number} WebSocket.ReadyState
132  */
133  enum ReadyState {
134  CONNECTING = 0,
135  OPEN,
136  CLOSING,
137  CLOSED
138  };
139 
140  QWebSocket* getWebSocket() { return _webSocket; }
141 
142  ReadyState getConnecting() const { return CONNECTING; };
143  ReadyState getOpen() const { return OPEN; };
144  ReadyState getClosing() const { return CLOSING; };
145  ReadyState getClosed() const { return CLOSED; };
146 
147  void setBinaryType(QString binaryType) { _binaryType = binaryType; }
148  QString getBinaryType() { return _binaryType; }
149 
150  // extensions is a empty string until supported in QT WebSockets
151  QString getExtensions() { return QString(); }
152 
153  // protocol is a empty string until supported in QT WebSockets
154  QString getProtocol() { return QString(); }
155 
156  ulong getBufferedAmount() { return 0; }
157 
158  QString getURL() { return _webSocket->requestUrl().toDisplayString(); }
159 
160  ReadyState getReadyState() {
161  switch (_webSocket->state()) {
162  case QAbstractSocket::SocketState::HostLookupState:
163  case QAbstractSocket::SocketState::ConnectingState:
164  return CONNECTING;
165  case QAbstractSocket::SocketState::ConnectedState:
166  case QAbstractSocket::SocketState::BoundState:
167  case QAbstractSocket::SocketState::ListeningState:
168  return OPEN;
169  case QAbstractSocket::SocketState::ClosingState:
170  return CLOSING;
171  case QAbstractSocket::SocketState::UnconnectedState:
172  default:
173  return CLOSED;
174  }
175  }
176 
177  void setOnClose(const ScriptValue& eventFunction) { _onCloseEvent = eventFunction; }
178  ScriptValue getOnClose() { return _onCloseEvent; }
179 
180  void setOnError(const ScriptValue& eventFunction) { _onErrorEvent = eventFunction; }
181  ScriptValue getOnError() { return _onErrorEvent; }
182 
183  void setOnMessage(const ScriptValue& eventFunction) { _onMessageEvent = eventFunction; }
184  ScriptValue getOnMessage() { return _onMessageEvent; }
185 
186  void setOnOpen(const ScriptValue& eventFunction) { _onOpenEvent = eventFunction; }
187  ScriptValue getOnOpen() { return _onOpenEvent; }
188 
189 public slots:
190 
191  /*@jsdoc
192  * Sends a message on the connection.
193  * @function WebSocket.send
194  * @param {string|object} message - The message to send. If an object, it is converted to a string.
195  */
196  void send(const ScriptValue& message);
197 
198  /*@jsdoc
199  * Closes the connection.
200  * @function WebSocket.close
201  * @param {WebSocket.CloseCode} [closeCode=1000] - The reason for closing.
202  * @param {string} [reason=""] - A description of the reason for closing.
203  */
204  /*@jsdoc
205  * The reason why the connection was closed.
206  * <table>
207  * <thead>
208  * <tr><th>Value</th><th>Name</th><th>Description</th></tr>
209  * </thead>
210  * <tbody>
211  * <tr><td><code>1000</code></td><td>Normal</td><td>Normal closure.</td></tr>
212  * <tr><td><code>1001</code></td><td>GoingAway</td><td>Going away.</td></tr>
213  * <tr><td><code>1002</code></td><td>ProtocolError</td><td>Protocol error.</td></tr>
214  * <tr><td><code>1003</code></td><td>DatatypeNotSupported</td><td>Unsupported data.</td></tr>
215  * <tr><td><code>1004</code></td><td>Reserved1004</td><td>Reserved.</td></tr>
216  * <tr><td><code>1005</code></td><td>MissingStatusCode</td><td>No status received.</td></tr>
217  * <tr><td><code>1006</code></td><td>AbnormalDisconnection</td><td>abnormal closure.</td></tr>
218  * <tr><td><code>1007</code></td><td>WrongDatatype</td><td>Invalid frame payload data.</td></tr>
219  * <tr><td><code>1008</code></td><td>PolicyViolated</td><td>Policy violation.</td></tr>
220  * <tr><td><code>1009</code></td><td>TooMuchData</td><td>Message too big.</td></tr>
221  * <tr><td><code>1010</code></td><td>MissingExtension</td><td>Mandatory extension missing.</td></tr>
222  * <tr><td><code>1011</code></td><td>BadOperation</td><td>Internal server error.</td></tr>
223  * <tr><td><code>1015</code></td><td>TlsHandshakeFailed</td><td>TLS handshake failed.</td></tr>
224  * </tbody>
225  * <table>
226  * @typedef {number} WebSocket.CloseCode
227  */
228  void close();
229  void close(QWebSocketProtocol::CloseCode closeCode);
230  void close(QWebSocketProtocol::CloseCode closeCode, QString reason);
231 
232 private:
233  QWebSocket* _webSocket;
234  ScriptEngine* _engine;
235 
236  ScriptValue _onCloseEvent;
237  ScriptValue _onErrorEvent;
238  ScriptValue _onMessageEvent;
239  ScriptValue _onOpenEvent;
240 
241  QString _binaryType;
242 
243  void initialize();
244 
245 private slots:
246  void handleOnClose();
247  void handleOnError(QAbstractSocket::SocketError error);
248  void handleOnMessage(const QString& message);
249  void handleOnBinaryMessage(const QByteArray& message);
250  void handleOnOpen();
251 
252 };
253 
254 Q_DECLARE_METATYPE(QWebSocketProtocol::CloseCode);
255 Q_DECLARE_METATYPE(WebSocketClass::ReadyState);
256 
257 ScriptValue qWSCloseCodeToScriptValue(ScriptEngine* engine, const QWebSocketProtocol::CloseCode& closeCode);
258 bool qWSCloseCodeFromScriptValue(const ScriptValue& object, QWebSocketProtocol::CloseCode& closeCode);
259 
260 ScriptValue webSocketToScriptValue(ScriptEngine* engine, WebSocketClass* const &in);
261 bool webSocketFromScriptValue(const ScriptValue& object, WebSocketClass*& out);
262 
263 ScriptValue wscReadyStateToScriptValue(ScriptEngine* engine, const WebSocketClass::ReadyState& readyState);
264 bool wscReadyStateFromScriptValue(const ScriptValue& object, WebSocketClass::ReadyState& readyState);
265 
266 #endif // hifi_WebSocketClass_h
267 
[ScriptInterface] Provides an engine-independent interface for QScriptContext
Definition: ScriptContext.h:55
Provides an engine-independent interface for a scripting engine.
Definition: ScriptEngine.h:93
[ScriptInterface] Provides an engine-independent interface for QScriptValue
Definition: ScriptValue.h:40
Provides the WebSocket scripting interface.
Definition: WebSocketClass.h:91