From f3924a78b26a9479caf838c2e6b28c79db268a1f Mon Sep 17 00:00:00 2001 From: Dirk Hoffmann Date: Tue, 24 Dec 2024 14:18:28 +0100 Subject: [PATCH] Added HttpServer, SocketServer classes --- Emulator/Base/Defaults.cpp | 6 +- Emulator/Misc/RemoteServers/GdbServer.cpp | 14 +- Emulator/Misc/RemoteServers/GdbServer.h | 2 +- Emulator/Misc/RemoteServers/HttpServer.cpp | 42 ++++ Emulator/Misc/RemoteServers/HttpServer.h | 62 ++++++ Emulator/Misc/RemoteServers/PromServer.cpp | 81 +++---- Emulator/Misc/RemoteServers/PromServer.h | 81 ++----- Emulator/Misc/RemoteServers/RemoteManager.cpp | 1 + Emulator/Misc/RemoteServers/RemoteManager.h | 8 +- .../Misc/RemoteServers/RemoteManagerTypes.h | 4 +- Emulator/Misc/RemoteServers/RemoteServer.cpp | 161 +------------- Emulator/Misc/RemoteServers/RemoteServer.h | 78 ++----- Emulator/Misc/RemoteServers/RshServer.h | 12 +- Emulator/Misc/RemoteServers/SerServer.h | 10 +- Emulator/Misc/RemoteServers/SocketServer.cpp | 204 +++++++++++++++++- Emulator/Misc/RemoteServers/SocketServer.h | 93 ++++++++ Emulator/Misc/RetroShell/CommandConsole.cpp | 38 ++-- Emulator/config.cpp | 2 +- vAmiga.xcodeproj/project.pbxproj | 8 + .../xcdebugger/Breakpoints_v2.xcbkptlist | 16 ++ 20 files changed, 546 insertions(+), 377 deletions(-) create mode 100644 Emulator/Misc/RemoteServers/HttpServer.cpp create mode 100644 Emulator/Misc/RemoteServers/HttpServer.h diff --git a/Emulator/Base/Defaults.cpp b/Emulator/Base/Defaults.cpp index 821c8573a..89aafc53d 100644 --- a/Emulator/Base/Defaults.cpp +++ b/Emulator/Base/Defaults.cpp @@ -161,7 +161,11 @@ Defaults::Defaults() setFallback(OPT_SRV_PROTOCOL, SRVPROT_DEFAULT, { SERVER_RSH }); setFallback(OPT_SRV_AUTORUN, false, { SERVER_RSH }); setFallback(OPT_SRV_VERBOSE, true, { SERVER_RSH }); - setFallback(OPT_SRV_PORT, 8082, { SERVER_GDB }); + setFallback(OPT_SRV_PORT, 8082, { SERVER_PROM }); + setFallback(OPT_SRV_PROTOCOL, SRVPROT_DEFAULT, { SERVER_PROM }); + setFallback(OPT_SRV_AUTORUN, false, { SERVER_PROM }); + setFallback(OPT_SRV_VERBOSE, true, { SERVER_PROM }); + setFallback(OPT_SRV_PORT, 8083, { SERVER_GDB }); setFallback(OPT_SRV_PROTOCOL, SRVPROT_DEFAULT, { SERVER_GDB }); setFallback(OPT_SRV_AUTORUN, false, { SERVER_GDB }); setFallback(OPT_SRV_VERBOSE, true, { SERVER_GDB }); diff --git a/Emulator/Misc/RemoteServers/GdbServer.cpp b/Emulator/Misc/RemoteServers/GdbServer.cpp index 5218a44c1..7ad4f2a47 100644 --- a/Emulator/Misc/RemoteServers/GdbServer.cpp +++ b/Emulator/Misc/RemoteServers/GdbServer.cpp @@ -131,8 +131,14 @@ GdbServer::attach(const string &name) this->processName = name; this->segList = { }; - readSegList(); - + if (readSegList()) { + + retroShell << "Successfully attached to process '" << processName << "'\n\n"; + retroShell << " Data segment: " << util::hexstr <8> (dataSeg()) << "\n"; + retroShell << " Code segment: " << util::hexstr <8> (codeSeg()) << "\n"; + retroShell << " BSS segment: " << util::hexstr <8> (bssSeg()) << "\n\n"; + } + if (segList.empty()) { retroShell << "Waiting for process '" << processName << "' to launch.\n"; @@ -160,11 +166,13 @@ GdbServer::readSegList() // Try to find the segment list in memory osDebugger.read(processName, segList); if (segList.empty()) return false; - + + /* retroShell << "Successfully attached to process '" << processName << "'\n\n"; retroShell << " Data segment: " << util::hexstr <8> (dataSeg()) << "\n"; retroShell << " Code segment: " << util::hexstr <8> (codeSeg()) << "\n"; retroShell << " BSS segment: " << util::hexstr <8> (bssSeg()) << "\n\n"; + */ return true; } diff --git a/Emulator/Misc/RemoteServers/GdbServer.h b/Emulator/Misc/RemoteServers/GdbServer.h index a4d7600db..894f96881 100644 --- a/Emulator/Misc/RemoteServers/GdbServer.h +++ b/Emulator/Misc/RemoteServers/GdbServer.h @@ -58,7 +58,7 @@ class GdbServer final : public SocketServer { GdbServer& operator= (const GdbServer& other) { - RemoteServer::operator = (other); + SocketServer::operator = (other); return *this; } diff --git a/Emulator/Misc/RemoteServers/HttpServer.cpp b/Emulator/Misc/RemoteServers/HttpServer.cpp new file mode 100644 index 000000000..8ceff6ca6 --- /dev/null +++ b/Emulator/Misc/RemoteServers/HttpServer.cpp @@ -0,0 +1,42 @@ +// ----------------------------------------------------------------------------- +// This file is part of vAmiga +// +// Copyright (C) Dirk W. Hoffmann. www.dirkwhoffmann.de +// Licensed under the Mozilla Public License v2 +// +// See https://mozilla.org/MPL/2.0 for license information +// ----------------------------------------------------------------------------- + +#include "config.h" +#include "HttpServer.h" +#include "httplib.h" + +namespace vamiga { + +void +HttpServer::_dump(Category category, std::ostream& os) const +{ + using namespace util; + + if (category == Category::State) { + + RemoteServer::_dump(category, os); + // os << tab("..."); + // os << dec(...) << std::endl; + + } else { + + RemoteServer::_dump(category, os); + } +} + +void +HttpServer::disconnect() +{ + SUSPENDED + + debug(SRV_DEBUG, "Disconnecting...\n"); + if (srv) srv->stop(); +} + +} diff --git a/Emulator/Misc/RemoteServers/HttpServer.h b/Emulator/Misc/RemoteServers/HttpServer.h new file mode 100644 index 000000000..9bff4c7cb --- /dev/null +++ b/Emulator/Misc/RemoteServers/HttpServer.h @@ -0,0 +1,62 @@ +// ----------------------------------------------------------------------------- +// This file is part of vAmiga +// +// Copyright (C) Dirk W. Hoffmann. www.dirkwhoffmann.de +// Licensed under the Mozilla Public License v2 +// +// See https://mozilla.org/MPL/2.0 for license information +// ----------------------------------------------------------------------------- + +#pragma once + +#include "RemoteServer.h" + +namespace httplib { class Server; class Request; } + +namespace vamiga { + +class HttpServer : public RemoteServer { + + using RemoteServer::RemoteServer; + +protected: + + HttpServer& operator= (const HttpServer& other) { + + RemoteServer::operator = (other); + return *this; + } + + // A simple (third-party) HTTP server + httplib::Server *srv = nullptr; + + + // + // Methods from CoreObject + // + +protected: + + void _dump(Category category, std::ostream& os) const override; + + + // + // Methods from RemoteServer + // + +public: + + virtual void disconnect() override; + + + // + // Running the server + // + +private: + + // The main thread function + // void main() override; +}; + +} diff --git a/Emulator/Misc/RemoteServers/PromServer.cpp b/Emulator/Misc/RemoteServers/PromServer.cpp index f176ea2c0..91f677583 100644 --- a/Emulator/Misc/RemoteServers/PromServer.cpp +++ b/Emulator/Misc/RemoteServers/PromServer.cpp @@ -16,64 +16,15 @@ namespace vamiga { void -PromServer::start() +PromServer::_dump(Category category, std::ostream& os) const { - if (state == SRV_STATE_OFF) { + using namespace util; - debug(true, "start()\n"); - - try { - - // Start the server in a separate thread - serverThread = std::thread(&PromServer::startServer, this); - - } catch (std::exception &err) { - - debug(SRV_DEBUG, "Server thread interrupted: %s\n", err.what()); - } - - std::cout << "Started\n"; - state = SRV_STATE_CONNECTED; - } -} - -void -PromServer::startServer() -{ - debug(true, "startServer()\n"); - - // Create an HTTP server - httplib::Server svr; - - // Define the "/metrics" endpoint where Prometheus will scrape metrics - svr.Get("/metrics", [this](const httplib::Request& req, httplib::Response& res) { - - // Generate the metrics as a string - std::string metrics = generate_metrics(); - - // Set the response headers to indicate it's plain text - res.set_content(metrics, "text/plain"); - }); - - // Start the server to listen on localhost, port 8080 - std::cout << "Starting Prometheus data provider on http://localhost:8080/metrics\n"; - svr.listen("localhost", 8080); -} - -void -PromServer::stop() -{ - if (state == SRV_STATE_CONNECTED) { - - debug(true, "stop()"); - - state = SRV_STATE_OFF; - } + HttpServer::_dump(category, os); } -// Function to generate some example metrics in the Prometheus format string -PromServer::generate_metrics() +PromServer::respond(const httplib::Request& request) { auto emuStats = emulator.getStats(); @@ -89,4 +40,28 @@ PromServer::generate_metrics() return metrics; } +void +PromServer::main() +{ + try { + + // Create the HTTP server + if (!srv) srv = new httplib::Server(); + + // Define the "/metrics" endpoint where Prometheus will scrape metrics + srv->Get("/metrics", [this](const httplib::Request& req, httplib::Response& res) { + res.set_content(respond(req), "text/plain"); + }); + + // Start the server to listen on localhost + debug(SRV_DEBUG, "Starting Prometheus data provider\n"); + srv->listen("localhost", (int)config.port); + + } catch (std::exception &err) { + + debug(SRV_DEBUG, "Server thread interrupted\n"); + handleError(err.what()); + } +} + } diff --git a/Emulator/Misc/RemoteServers/PromServer.h b/Emulator/Misc/RemoteServers/PromServer.h index 1f6e34ec9..1f8984549 100644 --- a/Emulator/Misc/RemoteServers/PromServer.h +++ b/Emulator/Misc/RemoteServers/PromServer.h @@ -9,41 +9,21 @@ #pragma once -#include "SubComponent.h" -#include "RemoteServerTypes.h" - -std::string generate_metrics(); -int createPromServer(); +#include "HttpServer.h" namespace vamiga { -class PromServer final : public SubComponent { - - friend class RemoteManager; - - Descriptions descriptions = {{ - - .name = "PromServer", - .description = "Prometheus Server", - .shell = "prom" - }}; - - ConfigOptions options = { - - }; - - // The current server state - SrvState state = SRV_STATE_OFF; +class PromServer final : public HttpServer { - // The server thread - std::thread serverThread; - public: - using SubComponent::SubComponent; + using HttpServer::HttpServer; + +protected: PromServer& operator= (const PromServer& other) { + HttpServer::operator = (other); return *this; } @@ -54,61 +34,24 @@ class PromServer final : public SubComponent { protected: - void _dump(Category category, std::ostream& os) const override { }; - -public: - - const Descriptions &getDescriptions() const override { return descriptions; } + void _dump(Category category, std::ostream& os) const override; // - // Methods from CoreComponent - // - -private: - - template - void serialize(T& worker) - { - - } SERIALIZERS(serialize); - - - // - // Methods from Configurable + // Methods from RemoteServer // public: - // const PromServerConfig &getConfig() const { return config; } - const ConfigOptions &getOptions() const override { return options; } - // i64 getOption(Option option) const override; - // void checkOption(Option opt, i64 value) override; - // void setOption(Option option, i64 value) override; + void main() override; // - // Experimental + // Handling requests // -public: - - // Launch the remote server - void start(); - - // Shuts down the remote server - void stop(); - - // Used by the launch daemon to determine if actions should be taken - bool shouldRun() { return false; } - - // Experimental - string generate_metrics(); - -private: - - void startServer(); - + // Generate a response + string respond(const httplib::Request& request); }; } diff --git a/Emulator/Misc/RemoteServers/RemoteManager.cpp b/Emulator/Misc/RemoteServers/RemoteManager.cpp index 34b6776ec..f59575bc0 100644 --- a/Emulator/Misc/RemoteServers/RemoteManager.cpp +++ b/Emulator/Misc/RemoteServers/RemoteManager.cpp @@ -21,6 +21,7 @@ RemoteManager::RemoteManager(Amiga& ref) : SubComponent(ref) &serServer, &rshServer, + &promServer, &gdbServer }; } diff --git a/Emulator/Misc/RemoteServers/RemoteManager.h b/Emulator/Misc/RemoteServers/RemoteManager.h index e38ed7278..8e009fa47 100644 --- a/Emulator/Misc/RemoteServers/RemoteManager.h +++ b/Emulator/Misc/RemoteServers/RemoteManager.h @@ -36,12 +36,12 @@ class RemoteManager final : public SubComponent, public Inspectable servers = { - &serServer, &rshServer, &gdbServer + &serServer, &rshServer, &gdbServer, &promServer }; diff --git a/Emulator/Misc/RemoteServers/RemoteManagerTypes.h b/Emulator/Misc/RemoteServers/RemoteManagerTypes.h index 9554f6279..9e75653d2 100644 --- a/Emulator/Misc/RemoteServers/RemoteManagerTypes.h +++ b/Emulator/Misc/RemoteServers/RemoteManagerTypes.h @@ -20,6 +20,7 @@ enum_long(SERVER_TYPE) { SERVER_SER, SERVER_RSH, + SERVER_PROM, SERVER_GDB }; typedef SERVER_TYPE ServerType; @@ -29,7 +30,7 @@ struct ServerTypeEnum : vamiga::util::Reflection { static constexpr long minVal = 0; static constexpr long maxVal = SERVER_GDB; - + static const char *prefix() { return "SERVER"; } static const char *_key(long value) { @@ -37,6 +38,7 @@ struct ServerTypeEnum : vamiga::util::Reflection case SERVER_SER: return "SER"; case SERVER_RSH: return "RSH"; + case SERVER_PROM: return "PROM"; case SERVER_GDB: return "GDB"; } return "???"; diff --git a/Emulator/Misc/RemoteServers/RemoteServer.cpp b/Emulator/Misc/RemoteServers/RemoteServer.cpp index 5caafb8cc..e5d25000e 100644 --- a/Emulator/Misc/RemoteServers/RemoteServer.cpp +++ b/Emulator/Misc/RemoteServers/RemoteServer.cpp @@ -40,10 +40,6 @@ RemoteServer::_dump(Category category, std::ostream& os) const os << tab("State"); os << SrvStateEnum::key(state) << std::endl; - os << tab("Received packets"); - os << dec(numReceived) << std::endl; - os << tab("Transmitted packets"); - os << dec(numSent) << std::endl; } } @@ -175,13 +171,7 @@ RemoteServer::stop() void RemoteServer::disconnect() { - SUSPENDED - - debug(SRV_DEBUG, "Disconnecting...\n"); - - // Trigger an exception inside the server thread - connection.close(); - listener.close(); + } void @@ -205,155 +195,6 @@ RemoteServer::switchState(SrvState newState) } } -string -RemoteServer::receive() -{ - string packet; - - if (isConnected()) { - - packet = doReceive(); - msgQueue.put(MSG_SRV_RECEIVE, ++numReceived); - } - - return packet; -} - -void -RemoteServer::send(const string &packet) -{ - if (isConnected()) { - - doSend(packet); - msgQueue.put(MSG_SRV_SEND, ++numSent); - } -} - -void -RemoteServer::send(char payload) -{ - send(string(1, payload)); -} - -void -RemoteServer::send(int payload) -{ - send(std::to_string(payload)); -} - -void -RemoteServer::send(long payload) -{ - send(std::to_string(payload)); -} - - -void -RemoteServer::send(std::stringstream &payload) -{ - string line; - while(std::getline(payload, line)) { - send(line + "\n"); - } -} - -void -RemoteServer::process(const string &payload) -{ - doProcess(payload); -} - -void -RemoteServer::main() -{ - try { - - mainLoop(); - - } catch (std::exception &err) { - - debug(SRV_DEBUG, "Server thread interrupted\n"); - handleError(err.what()); - } -} - -void -RemoteServer::mainLoop() -{ - switchState(SRV_STATE_LISTENING); - - while (isListening()) { - - try { - - try { - - // Try to be a client by connecting to an existing server - connection.connect(config.port); - debug(SRV_DEBUG, "Acting as a client\n"); - - } catch (...) { - - // If there is no existing server, be the server - debug(SRV_DEBUG, "Acting as a server\n"); - - // Create a port listener - listener.bind(config.port); - listener.listen(); - - // Wait for a client to connect - connection = listener.accept(); - } - - // Handle the session - sessionLoop(); - - // Close the port listener - listener.close(); - - } catch (std::exception &err) { - - debug(SRV_DEBUG, "Main loop interrupted\n"); - - // Handle error if we haven't been interrupted purposely - if (!isStopping()) handleError(err.what()); - } - } - - switchState(SRV_STATE_OFF); -} - -void -RemoteServer::sessionLoop() -{ - switchState(SRV_STATE_CONNECTED); - - numReceived = 0; - numSent = 0; - - try { - - // Receive and process packets - while (1) { process(receive()); } - - } catch (std::exception &err) { - - debug(SRV_DEBUG, "Session loop interrupted\n"); - - // Handle error if we haven't been interrupted purposely - if (!isStopping()) { - - handleError(err.what()); - switchState(SRV_STATE_LISTENING); - } - } - - numReceived = 0; - numSent = 0; - - connection.close(); -} - void RemoteServer::handleError(const char *description) { diff --git a/Emulator/Misc/RemoteServers/RemoteServer.h b/Emulator/Misc/RemoteServers/RemoteServer.h index 8d1ca0a6b..ee190c95c 100644 --- a/Emulator/Misc/RemoteServers/RemoteServer.h +++ b/Emulator/Misc/RemoteServers/RemoteServer.h @@ -30,6 +30,10 @@ class RemoteServer : public SubComponent { .name = "RshServer", .description = "Remote Shell Server", .shell = "rshell" + }, { + .name = "PromServer", + .description = "Prometheus Server", + .shell = "prom" }, { .name = "GdbServer", .description = "GDB Remote Server", @@ -49,21 +53,13 @@ class RemoteServer : public SubComponent { // Current configuration ServerConfig config = {}; - // Sockets - Socket listener; - Socket connection; - // The server thread std::thread serverThread; // The current server state SrvState state = SRV_STATE_OFF; - // The number of sent and received packets - isize numSent = 0; - isize numReceived = 0; - // // Initializing // @@ -154,16 +150,16 @@ class RemoteServer : public SubComponent { public: // Launch the remote server - void start() throws; + virtual void start() throws; // Shuts down the remote server - void stop() throws; + virtual void stop() throws; // Disconnects the client - void disconnect() throws; + virtual void disconnect() throws = 0; protected: - + // Switches the internal state void switchState(SrvState newState); @@ -171,65 +167,21 @@ class RemoteServer : public SubComponent { // Used by the launch daemon to determine if actions should be taken virtual bool shouldRun() { return true; } - - + + // // Running the server // - -private: - - // The main thread function - void main(); - // Inner loops (called from main) - void mainLoop() throws; - void sessionLoop(); - - - // - // Transmitting and processing packets - // - -public: - - // Receives or packet - string receive() throws; - - // Sends a packet - void send(const string &payload) throws; - void send(char payload) throws; - void send(int payload) throws; - void send(long payload) throws; - void send(std::stringstream &payload) throws; - - // Operator overloads - RemoteServer &operator<<(char payload) { send(payload); return *this; } - RemoteServer &operator<<(const string &payload) { send(payload); return *this; } - RemoteServer &operator<<(int payload) { send(payload); return *this; } - RemoteServer &operator<<(long payload) { send(payload); return *this; } - RemoteServer &operator<<(std::stringstream &payload) { send(payload); return *this; } - - // Processes a package - void process(const string &payload) throws; - -private: +protected: + + // The main thread function + virtual void main() throws = 0; // Reports an error to the GUI void handleError(const char *description); - - - // - // Subclass specific implementations - // -private: - - virtual string doReceive() throws = 0; - virtual void doSend(const string &payload) throws = 0; - virtual void doProcess(const string &payload) throws = 0; - - + // // Delegation methods // diff --git a/Emulator/Misc/RemoteServers/RshServer.h b/Emulator/Misc/RemoteServers/RshServer.h index 8c9e7ac6f..79e18ecf0 100644 --- a/Emulator/Misc/RemoteServers/RshServer.h +++ b/Emulator/Misc/RemoteServers/RshServer.h @@ -21,7 +21,7 @@ class RshServer final : public SocketServer { RshServer& operator= (const RshServer& other) { - RemoteServer::operator = (other); + SocketServer::operator = (other); return *this; } @@ -38,6 +38,16 @@ class RshServer final : public SocketServer { // // Methods from RemoteServer // + +public: + + // bool shouldRun() override { return true; } + + + // + // Methods from SocketServer + // + string doReceive() throws override; void doProcess(const string &packet) throws override; diff --git a/Emulator/Misc/RemoteServers/SerServer.h b/Emulator/Misc/RemoteServers/SerServer.h index 0d5cfef99..41c5bbd50 100644 --- a/Emulator/Misc/RemoteServers/SerServer.h +++ b/Emulator/Misc/RemoteServers/SerServer.h @@ -45,7 +45,7 @@ class SerServer final : public SocketServer { SerServer& operator= (const SerServer& other) { - RemoteServer::operator = (other); + SocketServer::operator = (other); return *this; } @@ -66,6 +66,14 @@ class SerServer final : public SocketServer { public: bool shouldRun() override; + + + // + // Methods from SocketServer + // + +public: + string doReceive() override; void doSend(const string &packet) override; void doProcess(const string &packet) override; diff --git a/Emulator/Misc/RemoteServers/SocketServer.cpp b/Emulator/Misc/RemoteServers/SocketServer.cpp index 584fc36da..342f01cbb 100644 --- a/Emulator/Misc/RemoteServers/SocketServer.cpp +++ b/Emulator/Misc/RemoteServers/SocketServer.cpp @@ -1,8 +1,202 @@ +// ----------------------------------------------------------------------------- +// This file is part of vAmiga // -// SocketServer.cpp -// vAmiga -// -// Created by Dirk Hoffmann on 24.12.24. -// Copyright © 2024 Dirk Hoffmann. All rights reserved. +// Copyright (C) Dirk W. Hoffmann. www.dirkwhoffmann.de +// Licensed under the Mozilla Public License v2 // +// See https://mozilla.org/MPL/2.0 for license information +// ----------------------------------------------------------------------------- + +#include "config.h" +#include "RemoteServer.h" +#include "Emulator.h" +#include "CPU.h" +#include "IOUtils.h" +#include "Memory.h" +#include "MemUtils.h" +#include "MsgQueue.h" +#include "RetroShell.h" + +namespace vamiga { + +void +SocketServer::_dump(Category category, std::ostream& os) const +{ + using namespace util; + + if (category == Category::State) { + + RemoteServer::_dump(category, os); + os << tab("Received packets"); + os << dec(numReceived) << std::endl; + os << tab("Transmitted packets"); + os << dec(numSent) << std::endl; + + } else { + + RemoteServer::_dump(category, os); + } +} + +void +SocketServer::disconnect() +{ + SUSPENDED + + debug(SRV_DEBUG, "Disconnecting...\n"); + + // Trigger an exception inside the server thread + connection.close(); + listener.close(); +} + +void +SocketServer::main() +{ + try { + + mainLoop(); + + } catch (std::exception &err) { + + debug(SRV_DEBUG, "Server thread interrupted\n"); + handleError(err.what()); + } +} + +void +SocketServer::mainLoop() +{ + switchState(SRV_STATE_LISTENING); + + while (isListening()) { + + try { + + try { + + // Try to be a client by connecting to an existing server + connection.connect(config.port); + debug(SRV_DEBUG, "Acting as a client\n"); + + } catch (...) { + + // If there is no existing server, be the server + debug(SRV_DEBUG, "Acting as a server\n"); + + // Create a port listener + listener.bind(config.port); + listener.listen(); + + // Wait for a client to connect + connection = listener.accept(); + } + + // Handle the session + sessionLoop(); + + // Close the port listener + listener.close(); + + } catch (std::exception &err) { + + debug(SRV_DEBUG, "Main loop interrupted\n"); + + // Handle error if we haven't been interrupted purposely + if (!isStopping()) handleError(err.what()); + } + } + + switchState(SRV_STATE_OFF); +} + +void +SocketServer::sessionLoop() +{ + switchState(SRV_STATE_CONNECTED); + + numReceived = 0; + numSent = 0; + + try { + + // Receive and process packets + while (1) { process(receive()); } + + } catch (std::exception &err) { + + debug(SRV_DEBUG, "Session loop interrupted\n"); + + // Handle error if we haven't been interrupted purposely + if (!isStopping()) { + + handleError(err.what()); + switchState(SRV_STATE_LISTENING); + } + } + + numReceived = 0; + numSent = 0; + + connection.close(); +} + +string +SocketServer::receive() +{ + string packet; + + if (isConnected()) { + + packet = doReceive(); + msgQueue.put(MSG_SRV_RECEIVE, ++numReceived); + } + + return packet; +} + +void +SocketServer::send(const string &packet) +{ + if (isConnected()) { + + doSend(packet); + msgQueue.put(MSG_SRV_SEND, ++numSent); + } +} + +void +SocketServer::send(char payload) +{ + send(string(1, payload)); +} + +void +SocketServer::send(int payload) +{ + send(std::to_string(payload)); +} + +void +SocketServer::send(long payload) +{ + send(std::to_string(payload)); +} + + +void +SocketServer::send(std::stringstream &payload) +{ + string line; + while(std::getline(payload, line)) { + send(line + "\n"); + } +} + +void +SocketServer::process(const string &payload) +{ + doProcess(payload); +} +} diff --git a/Emulator/Misc/RemoteServers/SocketServer.h b/Emulator/Misc/RemoteServers/SocketServer.h index 20351b8a9..c439b198e 100644 --- a/Emulator/Misc/RemoteServers/SocketServer.h +++ b/Emulator/Misc/RemoteServers/SocketServer.h @@ -16,6 +16,99 @@ namespace vamiga { class SocketServer : public RemoteServer { using RemoteServer::RemoteServer; + +protected: + + SocketServer& operator= (const SocketServer& other) { + + RemoteServer::operator = (other); + return *this; + } + + // Sockets + Socket listener; + Socket connection; + + // Number of transmitted packages + isize numSent = 0; + isize numReceived = 0; + + + // + // Methods from CoreObject + // + +protected: + + void _dump(Category category, std::ostream& os) const override; + + + // + // Methods from RemoteServer + // + +public: + + virtual void disconnect() override; + + + // + // Running the server + // + +private: + + // The main thread function + void main() override; + + // Inner loops (called from main) + void mainLoop() throws; + void sessionLoop(); + + + // + // Transmitting and processing packets + // + +public: + + // Receives or packet + string receive() throws; + + // Sends a packet + void send(const string &payload) throws; + void send(char payload) throws; + void send(int payload) throws; + void send(long payload) throws; + void send(std::stringstream &payload) throws; + + // Operator overloads + SocketServer &operator<<(char payload) { send(payload); return *this; } + SocketServer &operator<<(const string &payload) { send(payload); return *this; } + SocketServer &operator<<(int payload) { send(payload); return *this; } + SocketServer &operator<<(long payload) { send(payload); return *this; } + SocketServer &operator<<(std::stringstream &payload) { send(payload); return *this; } + + // Processes a package + void process(const string &payload) throws; + + + // + // Subclass specific implementations + // + +private: + + virtual string doReceive() throws = 0; + virtual void doSend(const string &payload) throws = 0; + virtual void doProcess(const string &payload) throws = 0; + + + // + // Delegation methods + // + + }; } diff --git a/Emulator/Misc/RetroShell/CommandConsole.cpp b/Emulator/Misc/RetroShell/CommandConsole.cpp index d9627c63f..90bf7a9bb 100644 --- a/Emulator/Misc/RetroShell/CommandConsole.cpp +++ b/Emulator/Misc/RetroShell/CommandConsole.cpp @@ -676,8 +676,9 @@ CommandConsole::initCommands(Command &root) }); cmd = registerComponent(remoteManager.serServer, root / "server"); + cmd = registerComponent(remoteManager.rshServer, root / "server"); - + root.add({"server", cmd, "start"}, "Starts the retro shell server", [this](Arguments& argv, long value) { @@ -698,14 +699,30 @@ CommandConsole::initCommands(Command &root) remoteManager.rshServer.disconnect(); }); - - root.add({"server", cmd, ""}, - "Displays the current configuration", + + cmd = registerComponent(remoteManager.promServer, root / "server"); + + root.add({"server", cmd, "start"}, + "Starts the Prometheus server", [this](Arguments& argv, long value) { - - dump(remoteManager.rshServer, Category::Config); + + remoteManager.promServer.start(); }); - + + root.add({"server", cmd, "stop"}, + "Stops the Prometheus server", + [this](Arguments& argv, long value) { + + remoteManager.promServer.stop(); + }); + + root.add({"server", cmd, "disconnect"}, + "Disconnects a client", + [this](Arguments& argv, long value) { + + remoteManager.promServer.disconnect(); + }); + cmd = registerComponent(remoteManager.gdbServer, root / "server"); root.add({"server", cmd, "attach"}, { Arg::process }, @@ -721,13 +738,6 @@ CommandConsole::initCommands(Command &root) remoteManager.gdbServer.detach(); }); - - root.add({"server", cmd, ""}, - "Displays the current configuration", - [this](Arguments& argv, long value) { - - dump(remoteManager.gdbServer, Category::Config); - }); } } diff --git a/Emulator/config.cpp b/Emulator/config.cpp index 49a934a83..efa4e4c30 100644 --- a/Emulator/config.cpp +++ b/Emulator/config.cpp @@ -135,7 +135,7 @@ debugflag KEY_DEBUG = 0; // Misc debugflag REC_DEBUG = 0; debugflag SCK_DEBUG = 0; -debugflag SRV_DEBUG = 0; +debugflag SRV_DEBUG = 1; debugflag GDB_DEBUG = 0; diff --git a/vAmiga.xcodeproj/project.pbxproj b/vAmiga.xcodeproj/project.pbxproj index 38df9c701..996264a7c 100644 --- a/vAmiga.xcodeproj/project.pbxproj +++ b/vAmiga.xcodeproj/project.pbxproj @@ -111,6 +111,8 @@ 5064051C2D19CA0D002FBC57 /* PromServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5064051A2D19CA0C002FBC57 /* PromServer.cpp */; }; 506407CB2D1AC45A002FBC57 /* SocketServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 506407CA2D1AC455002FBC57 /* SocketServer.cpp */; }; 506407CC2D1AC45A002FBC57 /* SocketServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 506407CA2D1AC455002FBC57 /* SocketServer.cpp */; }; + 506407CF2D1AD87E002FBC57 /* HttpServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 506407CE2D1AD87C002FBC57 /* HttpServer.cpp */; }; + 506407D02D1AD87E002FBC57 /* HttpServer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 506407CE2D1AD87C002FBC57 /* HttpServer.cpp */; }; 506436E529C1D92D00768528 /* CMakeLists.txt in Resources */ = {isa = PBXBuildFile; fileRef = 506436E429C1D92D00768528 /* CMakeLists.txt */; }; 5064851121EC7A1700FC4AC3 /* Memory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5064850F21EC7A1700FC4AC3 /* Memory.cpp */; }; 50672CC02946100F004113F5 /* DebugConsole.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 50672CBF2946100F004113F5 /* DebugConsole.cpp */; }; @@ -652,6 +654,8 @@ 506407C82D19D0A7002FBC57 /* httplib.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = httplib.h; sourceTree = ""; }; 506407C92D1AC44D002FBC57 /* SocketServer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SocketServer.h; sourceTree = ""; }; 506407CA2D1AC455002FBC57 /* SocketServer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SocketServer.cpp; sourceTree = ""; }; + 506407CD2D1AD86D002FBC57 /* HttpServer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HttpServer.h; sourceTree = ""; }; + 506407CE2D1AD87C002FBC57 /* HttpServer.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = HttpServer.cpp; sourceTree = ""; }; 506436E229C1D84D00768528 /* CMakeLists.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; 506436E429C1D92D00768528 /* CMakeLists.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = CMakeLists.txt; sourceTree = ""; }; 5064850F21EC7A1700FC4AC3 /* Memory.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Memory.cpp; sourceTree = ""; }; @@ -1926,6 +1930,8 @@ 50E98B53275A127F00AA0CB9 /* RemoteServer.cpp */, 506407C92D1AC44D002FBC57 /* SocketServer.h */, 506407CA2D1AC455002FBC57 /* SocketServer.cpp */, + 506407CD2D1AD86D002FBC57 /* HttpServer.h */, + 506407CE2D1AD87C002FBC57 /* HttpServer.cpp */, 505C83FC2773C9BA001F8159 /* SerServer.h */, 505C83FB2773C9BA001F8159 /* SerServer.cpp */, 50AD90592771B2770011ECCB /* RshServer.h */, @@ -2500,6 +2506,7 @@ 5010A78222B50B690041388B /* PortPanel.swift in Sources */, 509F7F1D21EDEA0200A530E4 /* RomFile.cpp in Sources */, 50F580662431D1A000E7D84D /* Screenshot.swift in Sources */, + 506407CF2D1AD87E002FBC57 /* HttpServer.cpp in Sources */, 50AEBEC724D3D39D0037082D /* BlitterEvents.cpp in Sources */, 501C513F27C0C63A00DF1DD5 /* HardDiskConfigurator.swift in Sources */, 50B0AF93222531C500EE3689 /* CopperTableView.swift in Sources */, @@ -2620,6 +2627,7 @@ 50EFB0D12C18E01E0013B73C /* Wakeable.cpp in Sources */, 50FC047627DA10AD00C3E566 /* Headless.cpp in Sources */, 50FC049227DA196500C3E566 /* DeniseRegs.cpp in Sources */, + 506407D02D1AD87E002FBC57 /* HttpServer.cpp in Sources */, 50FC04F727DA1A8F00C3E566 /* StateMachine.cpp in Sources */, 50FC04F527DA1A8F00C3E566 /* Sampler.cpp in Sources */, 50FC04AE27DA198E00C3E566 /* BlitterRegs.cpp in Sources */, diff --git a/vAmiga.xcodeproj/xcuserdata/hoff.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/vAmiga.xcodeproj/xcuserdata/hoff.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist index e8b26cd4e..c360530e7 100644 --- a/vAmiga.xcodeproj/xcuserdata/hoff.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist +++ b/vAmiga.xcodeproj/xcuserdata/hoff.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -229,5 +229,21 @@ landmarkType = "7"> + + + +