Julian GroßFix copyright headersae7d2293 years agoHistory
242 lines · 10.1 KB · C++
RawDownload
////  WebSocketClass.cpp//  libraries/script-engine/src/////  Created by Thijs Wenker on 8/4/15.//  Copyright (c) 2015 High Fidelity, Inc. All rights reserved.//  Copyright 2023 Overte e.V.////  This class is an implementation of the WebSocket object for scripting use.  It provides a near-complete implementation//  of the class described in the Mozilla docs: https://developer.mozilla.org/en-US/docs/Web/API/WebSocket////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html//  SPDX-License-Identifier: Apache-2.0// #include "WebSocketClass.h" #include "ScriptContext.h"#include "ScriptEngine.h"#include "ScriptEngineCast.h"#include "ScriptEngineLogging.h"#include "ScriptValue.h"#include "ScriptManager.h" STATIC_SCRIPT_TYPES_INITIALIZER((+[](ScriptManager* manager){    auto scriptEngine = manager->engine().get();     scriptRegisterMetaType<WebSocketClass*, webSocketToScriptValue, webSocketFromScriptValue>(scriptEngine);    scriptRegisterMetaType<QWebSocketProtocol::CloseCode, qWSCloseCodeToScriptValue, qWSCloseCodeFromScriptValue>(scriptEngine);    scriptRegisterMetaType<WebSocketClass::ReadyState, wscReadyStateToScriptValue, wscReadyStateFromScriptValue>(scriptEngine);})); WebSocketClass::WebSocketClass(ScriptEngine* engine, QString url) :    _webSocket(new QWebSocket()),    _engine(engine){    initialize();    _webSocket->open(url);} WebSocketClass::WebSocketClass(ScriptEngine* engine, QWebSocket* qWebSocket) :    _webSocket(qWebSocket),    _engine(engine){    initialize();} void WebSocketClass::initialize() {    connect(_webSocket, &QWebSocket::disconnected, this, &WebSocketClass::handleOnClose);    connect(_webSocket, &QWebSocket::textMessageReceived, this, &WebSocketClass::handleOnMessage);    connect(_webSocket, &QWebSocket::binaryMessageReceived, this, &WebSocketClass::handleOnBinaryMessage);    connect(_webSocket, &QWebSocket::connected, this, &WebSocketClass::handleOnOpen);    connect(_webSocket, static_cast<void(QWebSocket::*)(QAbstractSocket::SocketError)>(&QWebSocket::error), this,        &WebSocketClass::handleOnError);    _binaryType = QStringLiteral("arraybuffer");} ScriptValue WebSocketClass::constructor(ScriptContext* context, ScriptEngine* engine) {    QString url;    if (context->argumentCount() > 0) {        url = context->argument(0).toString();    }    return engine->newQObject(new WebSocketClass(engine, url), ScriptEngine::ScriptOwnership);} WebSocketClass::~WebSocketClass() {    _webSocket->deleteLater();} void WebSocketClass::send(const ScriptValue& message) {    if (message.isObject()) {        QByteArray ba = scriptvalue_cast<QByteArray>(message);        _webSocket->sendBinaryMessage(ba);    } else {        _webSocket->sendTextMessage(message.toString());    }} void WebSocketClass::close() {    this->close(QWebSocketProtocol::CloseCodeNormal);} void WebSocketClass::close(QWebSocketProtocol::CloseCode closeCode) {    this->close(closeCode, QStringLiteral(""));} void WebSocketClass::close(QWebSocketProtocol::CloseCode closeCode, QString reason) {    _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()) {        ScriptValueList args;        ScriptValue arg = _engine->newObject();        arg.setProperty("code", hasError ? QWebSocketProtocol::CloseCodeAbnormalDisconnection : _webSocket->closeCode());        arg.setProperty("reason", _webSocket->closeReason());        arg.setProperty("wasClean", !hasError);        args << arg;        _onCloseEvent.call(ScriptValue(), args);    }} /*@jsdoc * Called when an error occurs. * @callback WebSocket~onErrorCallback * @param {WebSocket.SocketError} error - The error. *//*@jsdoc * <p>The type of socket error.</p> * <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 * Called 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()) {        ScriptValueList args;        ScriptValue arg = _engine->newObject();        arg.setProperty("data", message);        args << arg;        _onMessageEvent.call(ScriptValue(), args);    }} void WebSocketClass::handleOnBinaryMessage(const QByteArray& message) {    if (_onMessageEvent.isFunction()) {        ScriptValueList args;        ScriptValue arg = _engine->newObject();        ScriptValue arrayBuffer = _engine->newArrayBuffer(message);        if (arrayBuffer.isUndefined()) {            qCWarning(scriptengine) << "WebSocketClass::handleOnBinaryMessage !ArrayBuffer";        }        arg.setProperty("data", arrayBuffer);        args << arg;        _onMessageEvent.call(ScriptValue(), args);    }} /*@jsdoc * Called when the connection opens. * @callback WebSocket~onOpenCallback */void WebSocketClass::handleOnOpen() {    if (_onOpenEvent.isFunction()) {        _onOpenEvent.call();    }} ScriptValue qWSCloseCodeToScriptValue(ScriptEngine* engine, const QWebSocketProtocol::CloseCode &closeCode) {    return engine->newValue(closeCode);} bool qWSCloseCodeFromScriptValue(const ScriptValue &object, QWebSocketProtocol::CloseCode &closeCode) {    closeCode = (QWebSocketProtocol::CloseCode)object.toUInt16();    return true;} ScriptValue webSocketToScriptValue(ScriptEngine* engine, WebSocketClass* const &in) {    return engine->newQObject(in, ScriptEngine::ScriptOwnership);} bool webSocketFromScriptValue(const ScriptValue &object, WebSocketClass* &out) {    return (out = qobject_cast<WebSocketClass*>(object.toQObject())) != nullptr;} ScriptValue wscReadyStateToScriptValue(ScriptEngine* engine, const WebSocketClass::ReadyState& readyState) {    return engine->newValue(readyState);} bool wscReadyStateFromScriptValue(const ScriptValue& object, WebSocketClass::ReadyState& readyState) {    readyState = (WebSocketClass::ReadyState)object.toUInt16();    return true;}