diff options
Diffstat (limited to 'indra/newview/llscripteditorws.h')
| -rw-r--r-- | indra/newview/llscripteditorws.h | 230 |
1 files changed, 230 insertions, 0 deletions
diff --git a/indra/newview/llscripteditorws.h b/indra/newview/llscripteditorws.h new file mode 100644 index 0000000000..c8898c66a7 --- /dev/null +++ b/indra/newview/llscripteditorws.h @@ -0,0 +1,230 @@ +/** + * @file llscripteditorws.h + * @brief WebSocket server and connection classes for external script editor integration + * + * $LicenseInfo:firstyear=2025&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2025, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#pragma once + +#include "lljsonrpcws.h" +#include "llsd.h" +#include "lluuid.h" +#include "llhandle.h" +#include "lltimer.h" + +#include <memory> +#include <string> +#include <map> +#include <set> + +// Forward declarations +class LLLiveLSLEditor; +class LLScriptEdContainer; +class LLScriptEditorWSServer; +class LLChat; +class LLPanel; + +class LLScriptEditorWSConnection : public LLJSONRPCConnection, public std::enable_shared_from_this<LLScriptEditorWSConnection> +{ +public: + using ptr_t = std::shared_ptr<LLScriptEditorWSConnection>; + using wptr_t = std::weak_ptr<LLScriptEditorWSConnection>; + + enum DisconnectReason + { + REASON_NORMAL = 0, + REASON_EDITOR_CLOSED = 1, + REASON_PROTOCOL_ERROR = 2, + REASON_TIMEOUT = 3, + REASON_INTERNAL_ERROR = 4 + }; + + LLScriptEditorWSConnection(const LLWebsocketMgr::WSServer::ptr_t server, const LLWebsocketMgr::connection_h& handle) : + LLJSONRPCConnection(server, handle) + { + mConnectionID = sNextConnectionID++; + } + + ~LLScriptEditorWSConnection() override = default; + + U32 getConnectionID() const { return mConnectionID; } + + // Connection lifecycle overrides + void onOpen() override; + void onClose() override; + + void sendDisconnect(S32 reason = 0, const std::string& message = "Goodbye"); + +private: + using string_set_t = std::set<std::string>; + /** + * @brief Handle the handshake response from the client + * @param result The response data from the client containing client information + */ + void handleHandshakeResponse(const LLSD& result); + std::string generateChallenge(); + + LLScriptEdContainer* getEditor() const; + std::shared_ptr<LLScriptEditorWSServer> getServer() const; + + U32 mConnectionID{ 0 }; ///< Unique identifier for this connection + + // Client handshake response data + std::string mClientName; ///< Name of the external editor client + std::string mClientVersion; ///< Version of the external editor client + std::string mProtocolVersion; ///< JSON-RPC protocol version supported by client + std::string mScriptName; ///< Name of the script being edited + std::string mScriptLanguage; ///< Programming language of the script (lsl, luau, etc.) + string_set_t mLanguages; ///< Set of supported scripting languages + string_set_t mFeatures; ///< Active client features (live_sync, compilation, etc.) + LLUUID mChallenge; + std::string mChallengeFile; ///< Temporary file used for challenge-response verification + + static U32 sNextConnectionID; +}; + +/** + * @class LLScriptEditorWSServer + * @brief JSON-RPC 2.0 WebSocket server for external script editor integration + * + * This server extends the JSON-RPC server to provide specialized functionality + * for external script editor integration. It manages WebSocket connections from + * external script editors and provides a structured JSON-RPC 2.0 interface + * between the Second Life viewer's script editing functionality and external + * development tools. + * + * ## Architecture + * + * The server acts as a JSON-RPC communication hub between: + * - LLLiveLSLEditor instances (in-world script editing) + * - External script editors (VS Code, Atom, Sublime Text, etc.) + * - Script compilation and save services + * + * ## Usage + * + * @code + * // Create and start the JSON-RPC server + * auto server = std::make_shared<LLScriptEditorWSServer>("script_editor_server", 9020); + * LLWebsocketMgr::getInstance()->addServer(server); + * LLWebsocketMgr::getInstance()->startServer("script_editor_server"); + * + * // Associate with an LSL editor + * server->associateEditor(editor_handle, script_id); + * @endcode + * + * ## Security Considerations + * + * - Server binds to localhost only by default for security + * - JSON-RPC 2.0 structured protocol with validation + * - Rate limiting handled by base JSON-RPC server + * - Error handling with standardized JSON-RPC error codes + */ +class LLScriptEditorWSServer : public LLJSONRPCServer +{ +public: + enum SubscriptionError_t + { + SUBSCRIPTION_SUCCESS = 0, + SUBSCRIPTION_INVALID_EDITOR, + SUBSCRIPTION_INVALID_SUBSCRIPTION, + SUBSCRIPTION_ALREADY_SUBSCRIBED, + SUBSCRIPTION_INTERNAL_ERROR + }; + + static constexpr char const* DEFAULT_SERVER_NAME = "script_editor_server"; + static constexpr U16 DEFAULT_SERVER_PORT = 9020; + + using ptr_t = std::shared_ptr<LLScriptEditorWSServer>; + using wptr_t = std::weak_ptr<LLScriptEditorWSServer>; + + LLScriptEditorWSServer(const std::string& name, U16 port, bool local_only = true); + + virtual ~LLScriptEditorWSServer() = default; + + static LLScriptEditorWSServer::ptr_t getServer(); + + void onStarted() override; + void onStopped() override; + void onConnectionOpened(const LLWebsocketMgr::WSConnection::ptr_t& connection) override; + void onConnectionClosed(const LLWebsocketMgr::WSConnection::ptr_t& connection) override; + + bool subscribeScriptEditor(const LLUUID& object_id, const LLUUID& item_id, std::string_view script_name, + const LLHandle<LLPanel>& editor_handle, const std::string &script_id); + void unsubscribeEditor(const std::string &script_id); + + void notifyScript(const std::string& script_id, const std::string& method, const LLSD& message) const; + void sendUnsubscribeScriptEditor(const std::string& script_id); + void sendCompileResults(const std::string& script_id, const LLSD& results) const; + + LLHandle<LLPanel> findEditorForScript(const std::string& script_id) const; + + void forwardChatToIDE(const LLChat& chat_msg) const; + + std::set<std::string> getActiveScripts() const; + +protected: + LLWebsocketMgr::WSConnection::ptr_t connectionFactory(LLWebsocketMgr::WSServer::ptr_t server, + LLWebsocketMgr::connection_h handle) override; + + void setupConnectionMethods(LLJSONRPCConnection::ptr_t connection) override; + + void broadcastLanguageChange(); + + LLSD handleLanguageIdRequest() const; + LLSD handleSyntaxRequest(const LLSD ¶ms) const; + LLSD handleScriptSubscribe(U32 connection_id, const LLSD& params); + LLSD handleScriptUnsubscribe(U32 connection_id, const LLSD& params); + LLSD handleFileWatcherFileListRequest() const; + +private: + struct EditorSubscription + { + EditorSubscription(const LLUUID &object_id, const LLUUID &item_id, std::string_view script_name, LLHandle<LLPanel> editor_handle): + mObjectID(object_id), + mItemID(item_id), + mScriptName(script_name), + mEditorHandle(editor_handle) + {} + U32 mConnectionID{ 0 }; + LLUUID mObjectID; + LLUUID mItemID; + std::string mScriptName; + LLScriptEditorWSConnection::wptr_t mConnection; + LLHandle<LLPanel> mEditorHandle; + }; + using subscriptions_t = std::unordered_map<std::string, EditorSubscription>; + + SubscriptionError_t updateScriptSubscription(const std::string &script_id, U32 connection_id); + void unsubscribeConnection(U32 connection_id); + + subscriptions_t mSubscriptions; + std::map<U32, LLScriptEditorWSConnection::wptr_t> mActiveConnections; + + boost::signals2::connection mLanguageChangeSignal; + LLUUID mLastSyntaxId; + + + LLTimer mCleanupTimer; + static constexpr F32 CLEANUP_INTERVAL = 60.0f; // seconds + static constexpr F32 CONNECTION_TIMEOUT = 300.0f; // 5 minutes +}; |
