mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-06-10 05:52:44 +00:00
feat(automation): localhost beast POST /jsonrpc server
This commit is contained in:
@@ -230,6 +230,15 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/HMSPanel.hpp
|
||||
GUI/HttpServer.cpp
|
||||
GUI/HttpServer.hpp
|
||||
GUI/Automation/IUiBackend.hpp
|
||||
GUI/Automation/WidgetSerializer.cpp
|
||||
GUI/Automation/WidgetSerializer.hpp
|
||||
GUI/Automation/Locator.cpp
|
||||
GUI/Automation/Locator.hpp
|
||||
GUI/Automation/JsonRpcDispatcher.cpp
|
||||
GUI/Automation/JsonRpcDispatcher.hpp
|
||||
GUI/Automation/AutomationServer.cpp
|
||||
GUI/Automation/AutomationServer.hpp
|
||||
GUI/I18N.cpp
|
||||
GUI/I18N.hpp
|
||||
GUI/DragDropPanel.cpp
|
||||
|
||||
100
src/slic3r/GUI/Automation/AutomationServer.cpp
Normal file
100
src/slic3r/GUI/Automation/AutomationServer.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#include "AutomationServer.hpp"
|
||||
#include "libslic3r/Thread.hpp" // create_thread / set_current_thread_name
|
||||
|
||||
#include <boost/beast/core.hpp>
|
||||
#include <boost/beast/http.hpp>
|
||||
#include <boost/beast/version.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
namespace beast = boost::beast;
|
||||
namespace http = beast::http;
|
||||
namespace net = boost::asio;
|
||||
using tcp = net::ip::tcp;
|
||||
|
||||
namespace Slic3r { namespace GUI { namespace Automation {
|
||||
|
||||
AutomationServer::AutomationServer(unsigned short port) : m_port(port) {}
|
||||
|
||||
AutomationServer::~AutomationServer() { stop(); }
|
||||
|
||||
void AutomationServer::start() {
|
||||
if (m_started) return;
|
||||
m_ioc = std::make_unique<net::io_context>(1);
|
||||
// Bind to loopback ONLY.
|
||||
tcp::endpoint endpoint(net::ip::make_address("127.0.0.1"), m_port);
|
||||
m_acceptor = std::make_unique<tcp::acceptor>(*m_ioc);
|
||||
m_acceptor->open(endpoint.protocol());
|
||||
m_acceptor->set_option(net::socket_base::reuse_address(true));
|
||||
m_acceptor->bind(endpoint);
|
||||
m_acceptor->listen(net::socket_base::max_listen_connections);
|
||||
m_started = true;
|
||||
|
||||
do_accept();
|
||||
|
||||
net::io_context* ioc = m_ioc.get();
|
||||
m_thread = create_thread([ioc] {
|
||||
set_current_thread_name("orca_automation");
|
||||
ioc->run();
|
||||
});
|
||||
BOOST_LOG_TRIVIAL(info) << "AutomationServer listening on 127.0.0.1:" << m_port;
|
||||
}
|
||||
|
||||
void AutomationServer::stop() {
|
||||
if (!m_started) return;
|
||||
m_started = false;
|
||||
if (m_ioc) m_ioc->stop();
|
||||
if (m_thread.joinable()) m_thread.join();
|
||||
m_acceptor.reset();
|
||||
m_ioc.reset();
|
||||
}
|
||||
|
||||
void AutomationServer::do_accept() {
|
||||
m_acceptor->async_accept([this](beast::error_code ec, tcp::socket socket) {
|
||||
if (!ec) {
|
||||
// v1: single-client, serialized — handle synchronously on the io thread.
|
||||
handle_session(std::move(socket));
|
||||
}
|
||||
if (m_started && m_acceptor && m_acceptor->is_open())
|
||||
do_accept();
|
||||
});
|
||||
}
|
||||
|
||||
void AutomationServer::handle_session(tcp::socket socket) {
|
||||
beast::error_code ec;
|
||||
beast::flat_buffer buffer;
|
||||
http::request<http::string_body> req;
|
||||
http::read(socket, buffer, req, ec);
|
||||
if (ec) { socket.shutdown(tcp::socket::shutdown_send, ec); return; }
|
||||
|
||||
http::response<http::string_body> res;
|
||||
res.version(req.version());
|
||||
res.keep_alive(false);
|
||||
|
||||
if (req.method() == http::verb::post && req.target() == "/jsonrpc") {
|
||||
std::string body_out;
|
||||
try {
|
||||
body_out = m_handler ? m_handler(req.body())
|
||||
: R"({"jsonrpc":"2.0","id":null,"error":{"code":-32603,"message":"no handler"}})";
|
||||
} catch (const std::exception& e) {
|
||||
body_out = std::string(R"({"jsonrpc":"2.0","id":null,"error":{"code":-32603,"message":")")
|
||||
+ e.what() + R"("}})";
|
||||
}
|
||||
res.result(http::status::ok);
|
||||
res.set(http::field::content_type, "application/json");
|
||||
res.body() = std::move(body_out);
|
||||
} else if (req.method() == http::verb::get && req.target() == "/") {
|
||||
res.result(http::status::ok);
|
||||
res.set(http::field::content_type, "text/plain");
|
||||
res.body() = m_health;
|
||||
} else {
|
||||
res.result(http::status::not_found);
|
||||
res.set(http::field::content_type, "text/plain");
|
||||
res.body() = "not found";
|
||||
}
|
||||
res.set(http::field::server, "OrcaSlicer/automation");
|
||||
res.prepare_payload();
|
||||
http::write(socket, res, ec);
|
||||
socket.shutdown(tcp::socket::shutdown_send, ec);
|
||||
}
|
||||
|
||||
}}} // namespace
|
||||
42
src/slic3r/GUI/Automation/AutomationServer.hpp
Normal file
42
src/slic3r/GUI/Automation/AutomationServer.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace Slic3r { namespace GUI { namespace Automation {
|
||||
|
||||
// Localhost-only HTTP/1.1 server. POST /jsonrpc -> handler(body) -> response body.
|
||||
// GET / -> a tiny health/version page. The handler runs on the server's own
|
||||
// io thread; it is responsible for any further thread marshaling.
|
||||
class AutomationServer {
|
||||
public:
|
||||
using RequestHandler = std::function<std::string(const std::string& body)>;
|
||||
|
||||
explicit AutomationServer(unsigned short port);
|
||||
~AutomationServer();
|
||||
|
||||
void set_handler(RequestHandler handler) { m_handler = std::move(handler); }
|
||||
void set_health_text(std::string text) { m_health = std::move(text); }
|
||||
|
||||
void start(); // binds to 127.0.0.1:port, starts the io thread
|
||||
void stop(); // stops the io thread, joins
|
||||
bool is_started() const { return m_started; }
|
||||
unsigned short port() const { return m_port; }
|
||||
|
||||
private:
|
||||
void do_accept();
|
||||
void handle_session(boost::asio::ip::tcp::socket socket);
|
||||
|
||||
unsigned short m_port;
|
||||
std::atomic<bool> m_started{false};
|
||||
RequestHandler m_handler;
|
||||
std::string m_health{"OrcaSlicer automation server"};
|
||||
std::unique_ptr<boost::asio::io_context> m_ioc;
|
||||
std::unique_ptr<boost::asio::ip::tcp::acceptor> m_acceptor;
|
||||
boost::thread m_thread;
|
||||
};
|
||||
|
||||
}}} // namespace
|
||||
Reference in New Issue
Block a user