Merge pull request #16574 from ctrlaltdavid/DOC-242

DOC-242: WebSocket and WebSocketServer JSDoc
This commit is contained in:
Brad Hefta-Gaub 2019-12-15 09:25:47 -08:00 committed by GitHub
commit bec28e7e05
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 256 additions and 0 deletions

View file

@ -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 - <code>true</code> if the connection closed cleanly, <code>false</code> if it didn't.
*/
void WebSocketClass::handleOnClose() {
bool hasError = (_webSocket->error() != QAbstractSocket::UnknownSocketError);
if (_onCloseEvent.isFunction()) {
@ -81,12 +93,73 @@ void WebSocketClass::handleOnClose() {
}
}
/**jsdoc
* Called when an error occurs.
* @callback WebSocket~onErrorCallback
* @param {WebSocket.SocketError} error - The error.
*/
/**jsdoc
* Information on a socket error.
* <table>
* <thead>
* <tr><th>Value</th><th>Name</th><th>Description</th></tr>
* </thead>
* <tbody>
* <tr><td><code>0</code></td><td>ConnectionRefusedError</td><td>The connection was refused or timed out.</td></tr>
* <tr><td><code>1</code></td><td>RemoteHostClosedError</td><td>The remote host closed the connection.</td></tr>
* <tr><td><code>2</code></td><td>HostNotFoundError</td><td>The host address was not found.</td></tr>
* <tr><td><code>3</code></td><td>SocketAccessError</td><td>The socket operation failed because the application doesn't have
* the necessary privileges.</td></tr>
* <tr><td><code>4</code></td><td>SocketResourceError</td><td>The local system ran out of resources (e.g., too many
* sockets).</td></tr>
* <tr><td><code>5</code></td><td>SocketTimeoutError</td><td>The socket operation timed out.</td></tr>
* <tr><td><code>6</code></td><td>DatagramTooLargeError</td><td>The datagram was larger than the OS's limit.</td></tr>
* <tr><td><code>7</code></td><td>NetworkError</td><td>An error occurred with the network.</td></tr>
* <tr><td><code>8</code></td><td>AddressInUseError</td><td>The is already in use and cannot be reused.</td></tr>
* <tr><td><code>9</code></td><td>SocketAddressNotAvailableError</td><td>The address specified does not belong to the
* host.</td></tr>
* <tr><td><code>10</code></td><td>UnsupportedSocketOperationError</td><td>The requested socket operation is not supported
* by the local OS.</td></tr>
* <tr><td><code>11</code></td><td>ProxyAuthenticationRequiredError</td><td>The socket is using a proxy and requires
* authentication.</td></tr>
* <tr><td><code>12</code></td><td>SslHandshakeFailedError</td><td>The SSL/TLS handshake failed.</td></tr>
* <tr><td><code>13</code></td><td>UnfinishedSocketOperationError</td><td>The last operation has not finished yet.</td></tr>
* <tr><td><code>14</code></td><td>ProxyConnectionRefusedError</td><td>Could not contact the proxy server because connection
* was denied.</td></tr>
* <tr><td><code>15</code></td><td>ProxyConnectionClosedError</td><td>The connection to the proxy server was unexpectedly
* closed.</td></tr>
* <tr><td><code>16</code></td><td>ProxyConnectionTimeoutError</td><td>The connection to the proxy server timed
* out.</td></tr>
* <tr><td><code>17</code></td><td>ProxyNotFoundError</td><td>The proxy address was not found.</td></tr>
* <tr><td><code>18</code></td><td>ProxyProtocolError</td><td>Connection to the proxy server failed because the server
* response could not be understood.</td></tr>
* <tr><td><code>19</code></td><td>OperationError</td><td>An operation failed because the socket state did not permit
* it.</td></tr>
* <tr><td><code>20</code></td><td>SslInternalError</td><td>Internal error in the SSL library being used.</td></tr>
* <tr><td><code>21</code></td><td>SslInvalidUserDataError</td><td>Error in the SSL library because of invalid
* data.</td></tr>
* <tr><td><code>22</code></td><td>TemporaryError</td><td>A temporary error occurred.</td></tr>
* <tr><td><code>-1</code></td><td>UnknownSocketError</td><td>An unknown error occurred.</td></tr>
* </tbody>
* </table>
* @typedef {number} WebSocket.SocketError
*/
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 +170,10 @@ void WebSocketClass::handleOnMessage(const QString& message) {
}
}
/**jsdoc
* Called when the connection opens.
* @callback WebSocket~onOpenCallback
*/
void WebSocketClass::handleOnOpen() {
if (_onOpenEvent.isFunction()) {
_onOpenEvent.call();

View file

@ -16,6 +16,68 @@
#include <QScriptEngine>
#include <QWebSocket>
/**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:
* <a href="https://developer.mozilla.org/en-US/docs/Web/API/WebSocket">https://developer.mozilla.org/en-US/docs/Web/API/WebSocket</a>.</p>
*
* <p>Constructed by <code>new WebSocket(...)</code> in Interface, client entity, avatar, and server entity scripts, or the
* {@link WebSocketServer} class in server entity and assignment client scripts.
*
* <p><strong>Note:</strong> Does not support secure, <code>wss:</code> protocol.</p>
*
* @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" - <em>Not used.</em>
* @property {number} bufferedAmount=0 - <em>Not implemented.</em> <em>Read-only.</em>
* @property {string} extensions="" - <em>Not implemented.</em> <em>Read-only.</em>
*
* @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="" - <em>Not implemented.</em> <em>Read-only.</em>
* @property {WebSocket.ReadyState} readyState - The state of the connection. <em>Read-only.</em>
* @property {string} url - The URL to connect to. <em>Read-only.</em>
*
* @property {WebSocket.ReadyState} CONNECTING - The connection is opening. <em>Read-only.</em>
* @property {WebSocket.ReadyState} OPEN - The connection is open. <em>Read-only.</em>
* @property {WebSocket.ReadyState} CLOSING - The connection is closing. <em>Read-only.</em>
* @property {WebSocket.ReadyState} CLOSED - The connection is closed. <em>Read-only.</em>
*
* @example <caption>Echo a message off websocket.org.</caption>
* 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.
* <table>
* <thead>
* <tr><th>Value</th><th>Name</th><th>Description</th></tr>
* </thead>
* <tbody>
* <tr><td><code>0</code></td><td>CONNECTING</td><td>The connection is opening.</td></tr>
* <tr><td><code>1</code></td><td>OPEN</td><td>The connection is open.</td></tr>
* <tr><td><code>2</code></td><td>CLOSING</td><td>The connection is closing.</td></tr>
* <tr><td><code>3</code></td><td>CLOSED</td><td>The connection is closed.</td></tr>
* </tbody>
* </table>
* @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.
* <table>
* <thead>
* <tr><th>Value</th><th>Name</th><th>Description</th></tr>
* </thead>
* <tbody>
* <tr><td><code>1000</code></td><td>Normal</td><td>Normal closure.</td></tr>
* <tr><td><code>1001</code></td><td>GoingAway</td><td>Going away.</td></tr>
* <tr><td><code>1002</code></td><td>ProtocolError</td><td>Protocol error.</td></tr>
* <tr><td><code>1003</code></td><td>DatatypeNotSupported</td><td>Unsupported data.</td></tr>
* <tr><td><code>1004</code></td><td>Reserved1004</td><td>Reserved.</td></tr>
* <tr><td><code>1005</code></td><td>MissingStatusCode</td><td>No status received.</td></tr>
* <tr><td><code>1006</code></td><td>AbnormalDisconnection</td><td>abnormal closure.</td></tr>
* <tr><td><code>1007</code></td><td>WrongDatatype</td><td>Invalid frame payload data.</td></tr>
* <tr><td><code>1008</code></td><td>PolicyViolated</td><td>Policy violation.</td></tr>
* <tr><td><code>1009</code></td><td>TooMuchData</td><td>Message too big.</td></tr>
* <tr><td><code>1010</code></td><td>MissingExtension</td><td>Mandatory extension missing.</td></tr>
* <tr><td><code>1011</code></td><td>BadOperation</td><td>Internal server error.</td></tr>
* <tr><td><code>1015</code></td><td>TlsHandshakeFailed</td><td>TLS handshake failed.</td></tr>
* </tbody>
* <table>
* @typedef {number} WebSocket.CloseCode
*/
void close();
void close(QWebSocketProtocol::CloseCode closeCode);
void close(QWebSocketProtocol::CloseCode closeCode, QString reason);

View file

@ -17,6 +17,60 @@
#include <QWebSocketServer>
#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. <em>Read-only.</em>
* @property {number} port - The port that the server is listening on. <em>Read-only.</em>
* @property {boolean} listening - <code>true</code> if the server is listening for incoming connections, <code>false</code> if
* it isn't. <em>Read-only.</em>
*
* @example <caption>Echo a message back to sender.</caption>
* // 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);
};