From 13abf7f0167a3816bf5f571dee06d8bbd93c3723 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 14 Dec 2019 11:59:18 +1300 Subject: [PATCH 1/3] WebSocket and WebSocketServer JSDoc --- .../script-engine/src/WebSocketClass.cpp | 30 +++++ libraries/script-engine/src/WebSocketClass.h | 113 ++++++++++++++++++ .../script-engine/src/WebSocketServerClass.h | 66 ++++++++++ 3 files changed, 209 insertions(+) diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 56753f07d1..c02efd8a30 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -68,6 +68,18 @@ void WebSocketClass::close(QWebSocketProtocol::CloseCode closeCode, QString reas _webSocket->close(closeCode, reason); } +/**jsdoc + * Called when the connection closes. + * @callback WebSocket~onCloseCallback + * @param {WebSocket.CloseData} data - Information on the connection closure. + */ +/**jsdoc + * Information on a connection being closed. + * @typedef {object} WebSocket.CloseData + * @property {WebSocket.CloseCode} code - The reason why the connection was closed. + * @property {string} reason - Description of the reason why the connection was closed. + * @property {boolean} wasClean - true if the connection closed cleanly, false if it didn't. + */ void WebSocketClass::handleOnClose() { bool hasError = (_webSocket->error() != QAbstractSocket::UnknownSocketError); if (_onCloseEvent.isFunction()) { @@ -81,12 +93,26 @@ void WebSocketClass::handleOnClose() { } } +/**jsdoc + * Called when an error occurs. + * @callback WebSocket~onErrorCallback + */ void WebSocketClass::handleOnError(QAbstractSocket::SocketError error) { if (_onErrorEvent.isFunction()) { _onErrorEvent.call(); } } +/**jsdoc + * Triggered when a message is received. + * @callback WebSocket~onMessageCallback + * @param {WebSocket.MessageData} message - The message received. + */ +/**jsdoc + * A message received on a WebSocket connection. + * @typedef {object} WebSocket.MessageData + * @property {string} data - The message content. + */ void WebSocketClass::handleOnMessage(const QString& message) { if (_onMessageEvent.isFunction()) { QScriptValueList args; @@ -97,6 +123,10 @@ void WebSocketClass::handleOnMessage(const QString& message) { } } +/**jsdoc + * Called when the connection opens. + * @callback WebSocket~onOpenCallback + */ void WebSocketClass::handleOnOpen() { if (_onOpenEvent.isFunction()) { _onOpenEvent.call(); diff --git a/libraries/script-engine/src/WebSocketClass.h b/libraries/script-engine/src/WebSocketClass.h index dbc9729c61..56fb334152 100644 --- a/libraries/script-engine/src/WebSocketClass.h +++ b/libraries/script-engine/src/WebSocketClass.h @@ -16,6 +16,68 @@ #include #include +/**jsdoc + * Provides a bi-direcctional, event-driven communication session between the script and another WebSocket connection. It is a + * near-complete implementation of the WebSocket API described in the Mozilla docs: + * https://developer.mozilla.org/en-US/docs/Web/API/WebSocket.

+ * + *

Constructed by new WebSocket(...) in Interface, client entity, avatar, and server entity scripts, or the + * {@link WebSocketServer} class in server entity and assignment client scripts. + * + *

Note: Does not support secure, wss: protocol.

+ * + * @class WebSocket + * @param {string|WebSocket} urlOrWebSocket - The URL to connect to or an existing {@link WebSocket} to reuse the connection of. + * + * @hifi-interface + * @hifi-client-entity + * @hifi-avatar + * @hifi-server-entity + * @hifi-assignment-client + * + * @property {string} binaryType="blob" - Not used. + * @property {number} bufferedAmount=0 - Not implemented. Read-only. + * @property {string} extensions="" - Not implemented. Read-only. + * + * @property {WebSocket~onOpenCallback} onopen - Function called when the connection opens. + * @property {WebSocket~onMessageCallback} onmessage - Function called when a message is received. + * @property {WebSocket~onErrorCallback} onerror - Function called when an error occurs. + * @property {WebSocket~onCloseCallback} onclose - Function called when the connection closes. + * + * @property {string} protocol="" - Not implemented. Read-only. + * @property {WebSocket.ReadyState} readyState - The state of the connection. Read-only. + * @property {string} url - The URL to connect to. Read-only. + * + * @property {WebSocket.ReadyState} CONNECTING - The connection is opening. Read-only. + * @property {WebSocket.ReadyState} OPEN - The connection is open. Read-only. + * @property {WebSocket.ReadyState} CLOSING - The connection is closing. Read-only. + * @property {WebSocket.ReadyState} CLOSED - The connection is closed. Read-only. + * + * @example Echo a message off websocket.org. + * print("Create WebSocket"); + * var WEBSOCKET_PING_URL = "ws://echo.websocket.org"; + * var webSocket = new WebSocket(WEBSOCKET_PING_URL); + * + * webSocket.onclose = function (data) { + * print("WebSocket closed"); + * print("Ready state =", webSocket.readyState); // 3 + * }; + * + * webSocket.onmessage = function (data) { + * print("Message received:", data.data); + * + * print("Close WebSocket"); + * webSocket.close(); + * }; + * + * webSocket.onopen = function () { + * print("WebSocket opened"); + * print("Ready state =", webSocket.readyState); // 1 + * + * print("Send a test message"); + * webSocket.send("Test message"); + * }; + */ class WebSocketClass : public QObject { Q_OBJECT Q_PROPERTY(QString binaryType READ getBinaryType WRITE setBinaryType) @@ -43,6 +105,21 @@ public: static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); + /**jsdoc + * The state of a WebSocket connection. + * + * + * + * + * + * + * + * + * + * + *
ValueNameDescription
0CONNECTINGThe connection is opening.
1OPENThe connection is open.
2CLOSINGThe connection is closing.
3CLOSEDThe connection is closed.
+ * @typedef {number} WebSocket.ReadyState + */ enum ReadyState { CONNECTING = 0, OPEN, @@ -100,8 +177,44 @@ public: QScriptValue getOnOpen() { return _onOpenEvent; } public slots: + + /**jsdoc + * Sends a message on the connection. + * @function WebSocket.send + * @param {string|object} message - The message to send. If an object, it is converted to a string. + */ void send(QScriptValue message); + /**jsdoc + * Closes the connection. + * @function WebSocket.close + * @param {WebSocket.CloseCode} [closeCode=1000] - The reason for closing. + * @param {string} [reason=""] - A description of the reason for closing. + */ + /**jsdoc + * The reason why the connection was closed. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ValueNameDescription
1000NormalNormal closure.
1001GoingAwayGoing away.
1002ProtocolErrorProtocol error.
1003DatatypeNotSupportedUnsupported data.
1004Reserved1004Reserved.
1005MissingStatusCodeNo status received.
1006AbnormalDisconnectionabnormal closure.
1007WrongDatatypeInvalid frame payload data.
1008PolicyViolatedPolicy violation.
1009TooMuchDataMessage too big.
1010MissingExtensionMandatory extension missing.
1011BadOperationInternal server error.
1015TlsHandshakeFailedTLS handshake failed.
+ * @typedef {number} WebSocket.CloseCode + */ void close(); void close(QWebSocketProtocol::CloseCode closeCode); void close(QWebSocketProtocol::CloseCode closeCode, QString reason); diff --git a/libraries/script-engine/src/WebSocketServerClass.h b/libraries/script-engine/src/WebSocketServerClass.h index 972bf9c032..dfd277fec1 100644 --- a/libraries/script-engine/src/WebSocketServerClass.h +++ b/libraries/script-engine/src/WebSocketServerClass.h @@ -17,6 +17,60 @@ #include #include "WebSocketClass.h" +/**jsdoc + * Manages {@link WebSocket}s in server entity and assignment client scripts. + * + * @class WebSocketServer + * + * @hifi-server-entity + * @hifi-assignment-client + * + * @property {string} url - The URL that the server is listening on. Read-only. + * @property {number} port - The port that the server is listening on. Read-only. + * @property {boolean} listening - true if the server is listening for incoming connections, false if + * it isn't. Read-only. + * + * @example + * // Server entity script. Echoes received message back to sender. + * (function () { + * print("Create WebSocketServer"); + * var webSocketServer = new WebSocketServer(); + * print("Server url:", webSocketServer.url); + * + * function onNewConnection(webSocket) { + * print("New connection"); + * + * webSocket.onmessage = function (message) { + * print("Message received:", message.data); + * + * var returnMessage = message.data + " back!"; + * print("Echo a message back:", returnMessage); + * webSocket.send(message.data + " back!"); + * }; + * } + * + * webSocketServer.newConnection.connect(onNewConnection); + * }) + * + * @example + * // Interface script. Bounces message off server entity script. + * // Use the server URL reported by the server entity script. + * var WEBSOCKET_PING_URL = "ws://127.0.0.1:nnnnn"; + * var TEST_MESSAGE = "Hello"; + * + * print("Create WebSocket"); + * var webSocket = new WebSocket(WEBSOCKET_PING_URL); + * + * webSocket.onmessage = function(data) { + * print("Message received:", data.data); + * }; + * + * webSocket.onopen = function() { + * print("WebSocket opened"); + * print("Send test message:", TEST_MESSAGE); + * webSocket.send(TEST_MESSAGE); + * }; + */ class WebSocketServerClass : public QObject { Q_OBJECT Q_PROPERTY(QString url READ getURL) @@ -34,6 +88,11 @@ public: static QScriptValue constructor(QScriptContext* context, QScriptEngine* engine); public slots: + + /**jsdoc + * Closes all connections and closes the WebSocketServer. + * @function WebSocketServer.close + */ void close(); private: @@ -45,6 +104,13 @@ private slots: void onNewConnection(); signals: + + /**jsdoc + * Triggered when there is a new connection. + * @function WebSocketServer.newConnection + * @param {WebSocket} webSocket - The {@link WebSocket} for the new connection. + * @returns {Signal} + */ void newConnection(WebSocketClass* client); }; From 5359efead84c186fd86a7db28f7674c4bf822aab Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 14 Dec 2019 12:33:51 +1300 Subject: [PATCH 2/3] Add JSDoc missing for callback parameter --- .../script-engine/src/WebSocketClass.cpp | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index c02efd8a30..313096a470 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -96,6 +96,53 @@ void WebSocketClass::handleOnClose() { /**jsdoc * Called when an error occurs. * @callback WebSocket~onErrorCallback + * @param {WebSocket.SocketError} error - The error. + */ +/**jsdoc + * Information on a socket error. + *
Echo a message back to sender.
+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
ValueNameDescription
0ConnectionRefusedErrorThe connection was refused or timed out.
1RemoteHostClosedErrorThe remote host closed the connection.
2HostNotFoundErrorThe host address was not found.
3SocketAccessErrorThe socket operation failed because the application doesn't have + * the necessary privileges.
4SocketResourceErrorThe local system ran out of resources (e.g., too many + * sockets).
5SocketTimeoutErrorThe socket operation timed out.
6DatagramTooLargeErrorThe datagram was larger than the OS's limit.
7NetworkErrorAn error occurred with the network.
8AddressInUseErrorThe is already in use and cannot be reused.
9SocketAddressNotAvailableErrorThe address specified does not belong to the + * host.
10UnsupportedSocketOperationErrorThe requested socket operation is not supported +* by the local OS.
11ProxyAuthenticationRequiredErrorThe socket is using a proxy and requires + * authentication.
12SslHandshakeFailedErrorThe SSL/TLS handshake failed.
13UnfinishedSocketOperationErrorThe last operation has not finished yet.
14ProxyConnectionRefusedErrorCould not contact the proxy server because connection + * was denied.
15ProxyConnectionClosedErrorThe connection to the proxy server was unexpectedly + * closed.
16ProxyConnectionTimeoutErrorThe connection to the proxy server timed +* out.
17ProxyNotFoundErrorThe proxy address was not found.
18ProxyProtocolErrorConnection to the proxy server failed because the server + * response could not be understood.
19OperationErrorAn operation failed because the socket state did not permit + * it.
20SslInternalErrorInternal error in the SSL library being used.
21SslInvalidUserDataErrorError in the SSL library because of invalid + * data.
22TemporaryErrorA temporary error occurred.
-1UnknownSocketErrorAn unknown error occurred.
+ * @typedef {number} WebSocket.SocketError */ void WebSocketClass::handleOnError(QAbstractSocket::SocketError error) { if (_onErrorEvent.isFunction()) { From ea7203675739cfb758f7ae451b2108e29820aee4 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Sat, 14 Dec 2019 12:36:36 +1300 Subject: [PATCH 3/3] Typos --- libraries/script-engine/src/WebSocketClass.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/script-engine/src/WebSocketClass.cpp b/libraries/script-engine/src/WebSocketClass.cpp index 313096a470..73774912a9 100644 --- a/libraries/script-engine/src/WebSocketClass.cpp +++ b/libraries/script-engine/src/WebSocketClass.cpp @@ -119,7 +119,7 @@ void WebSocketClass::handleOnClose() { * 9SocketAddressNotAvailableErrorThe address specified does not belong to the * host. * 10UnsupportedSocketOperationErrorThe requested socket operation is not supported -* by the local OS. + * by the local OS. * 11ProxyAuthenticationRequiredErrorThe socket is using a proxy and requires * authentication. * 12SslHandshakeFailedErrorThe SSL/TLS handshake failed. @@ -129,7 +129,7 @@ void WebSocketClass::handleOnClose() { * 15ProxyConnectionClosedErrorThe connection to the proxy server was unexpectedly * closed. * 16ProxyConnectionTimeoutErrorThe connection to the proxy server timed -* out. + * out. * 17ProxyNotFoundErrorThe proxy address was not found. * 18ProxyProtocolErrorConnection to the proxy server failed because the server * response could not be understood.