// // WebSocketClass.h // libraries/script-engine/src/ // // Created by Thijs Wenker on 8/4/15. // Copyright (c) 2015 High Fidelity, Inc. All rights reserved. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // /// @addtogroup ScriptEngine /// @{ #ifndef hifi_WebSocketClass_h #define hifi_WebSocketClass_h #include #include #include "ScriptValue.h" class ScriptContext; class ScriptEngine; /*@jsdoc * Provides a bi-directional, 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. * *

Create using 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"); * }; */ /// Provides the WebSocket scripting interface class WebSocketClass : public QObject { Q_OBJECT Q_PROPERTY(QString binaryType READ getBinaryType WRITE setBinaryType) Q_PROPERTY(ulong bufferedAmount READ getBufferedAmount) Q_PROPERTY(QString extensions READ getExtensions) Q_PROPERTY(ScriptValue onclose READ getOnClose WRITE setOnClose) Q_PROPERTY(ScriptValue onerror READ getOnError WRITE setOnError) Q_PROPERTY(ScriptValue onmessage READ getOnMessage WRITE setOnMessage) Q_PROPERTY(ScriptValue onopen READ getOnOpen WRITE setOnOpen) Q_PROPERTY(QString protocol READ getProtocol) Q_PROPERTY(WebSocketClass::ReadyState readyState READ getReadyState) Q_PROPERTY(QString url READ getURL) Q_PROPERTY(WebSocketClass::ReadyState CONNECTING READ getConnecting CONSTANT) Q_PROPERTY(WebSocketClass::ReadyState OPEN READ getOpen CONSTANT) Q_PROPERTY(WebSocketClass::ReadyState CLOSING READ getClosing CONSTANT) Q_PROPERTY(WebSocketClass::ReadyState CLOSED READ getClosed CONSTANT) public: WebSocketClass(ScriptEngine* engine, QString url); WebSocketClass(ScriptEngine* engine, QWebSocket* qWebSocket); ~WebSocketClass(); static ScriptValue constructor(ScriptContext* context, ScriptEngine* 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, CLOSING, CLOSED }; QWebSocket* getWebSocket() { return _webSocket; } ReadyState getConnecting() const { return CONNECTING; }; ReadyState getOpen() const { return OPEN; }; ReadyState getClosing() const { return CLOSING; }; ReadyState getClosed() const { return CLOSED; }; void setBinaryType(QString binaryType) { _binaryType = binaryType; } QString getBinaryType() { return _binaryType; } // extensions is a empty string until supported in QT WebSockets QString getExtensions() { return QString(); } // protocol is a empty string until supported in QT WebSockets QString getProtocol() { return QString(); } ulong getBufferedAmount() { return 0; } QString getURL() { return _webSocket->requestUrl().toDisplayString(); } ReadyState getReadyState() { switch (_webSocket->state()) { case QAbstractSocket::SocketState::HostLookupState: case QAbstractSocket::SocketState::ConnectingState: return CONNECTING; case QAbstractSocket::SocketState::ConnectedState: case QAbstractSocket::SocketState::BoundState: case QAbstractSocket::SocketState::ListeningState: return OPEN; case QAbstractSocket::SocketState::ClosingState: return CLOSING; case QAbstractSocket::SocketState::UnconnectedState: default: return CLOSED; } } void setOnClose(const ScriptValue& eventFunction) { _onCloseEvent = eventFunction; } ScriptValue getOnClose() { return _onCloseEvent; } void setOnError(const ScriptValue& eventFunction) { _onErrorEvent = eventFunction; } ScriptValue getOnError() { return _onErrorEvent; } void setOnMessage(const ScriptValue& eventFunction) { _onMessageEvent = eventFunction; } ScriptValue getOnMessage() { return _onMessageEvent; } void setOnOpen(const ScriptValue& eventFunction) { _onOpenEvent = eventFunction; } ScriptValue 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(const ScriptValue& 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); private: QWebSocket* _webSocket; ScriptEngine* _engine; ScriptValue _onCloseEvent; ScriptValue _onErrorEvent; ScriptValue _onMessageEvent; ScriptValue _onOpenEvent; QString _binaryType; void initialize(); private slots: void handleOnClose(); void handleOnError(QAbstractSocket::SocketError error); void handleOnMessage(const QString& message); void handleOnBinaryMessage(const QByteArray& message); void handleOnOpen(); }; Q_DECLARE_METATYPE(QWebSocketProtocol::CloseCode); Q_DECLARE_METATYPE(WebSocketClass::ReadyState); ScriptValue qWSCloseCodeToScriptValue(ScriptEngine* engine, const QWebSocketProtocol::CloseCode& closeCode); bool qWSCloseCodeFromScriptValue(const ScriptValue& object, QWebSocketProtocol::CloseCode& closeCode); ScriptValue webSocketToScriptValue(ScriptEngine* engine, WebSocketClass* const &in); bool webSocketFromScriptValue(const ScriptValue& object, WebSocketClass*& out); ScriptValue wscReadyStateToScriptValue(ScriptEngine* engine, const WebSocketClass::ReadyState& readyState); bool wscReadyStateFromScriptValue(const ScriptValue& object, WebSocketClass::ReadyState& readyState); #endif // hifi_WebSocketClass_h /// @}