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