From cb2334559bed56c554ee50062f381398b308a4ec Mon Sep 17 00:00:00 2001 From: Stone Li Date: Sat, 28 Jan 2023 11:18:12 +0800 Subject: [PATCH] NEW: add http server in BambuStudio Change-Id: I72d99277187ea0d6b600e741dcc4306bc5f44036 Signed-off-by: Stone Li (cherry picked from commit 9ee36044158856e433b19e407094120bd15f4aa0) --- src/imgui/imgui_internal.h | 2 +- src/slic3r/CMakeLists.txt | 2 + src/slic3r/GUI/GUI_App.cpp | 41 ++++--- src/slic3r/GUI/GUI_App.hpp | 10 +- src/slic3r/GUI/HttpServer.cpp | 151 ++++++++++++++++++++++++ src/slic3r/GUI/HttpServer.hpp | 162 ++++++++++++++++++++++++++ src/slic3r/GUI/WebUserLoginDialog.cpp | 31 ++++- src/slic3r/GUI/WebUserLoginDialog.hpp | 4 +- src/slic3r/Utils/NetworkAgent.cpp | 14 +++ src/slic3r/Utils/NetworkAgent.hpp | 3 + 10 files changed, 397 insertions(+), 23 deletions(-) create mode 100644 src/slic3r/GUI/HttpServer.cpp create mode 100644 src/slic3r/GUI/HttpServer.hpp diff --git a/src/imgui/imgui_internal.h b/src/imgui/imgui_internal.h index 018654d8a0..a10b0ac0f0 100644 --- a/src/imgui/imgui_internal.h +++ b/src/imgui/imgui_internal.h @@ -391,7 +391,7 @@ IM_MSVC_RUNTIME_CHECKS_OFF static inline float ImPow(float x, float y) { return powf(x, y); } // DragBehaviorT/SliderBehaviorT uses ImPow with either float/double and need the precision static inline double ImPow(double x, double y) { return pow(x, y); } static inline float ImLog(float x) { return logf(x); } // DragBehaviorT/SliderBehaviorT uses ImLog with either float/double and need the precision -static inline double ImLog(double x) { return log(x); } +static inline double ImLog(double x) { return logf(x); } static inline int ImAbs(int x) { return x < 0 ? -x : x; } static inline float ImAbs(float x) { return fabsf(x); } static inline double ImAbs(double x) { return fabs(x); } diff --git a/src/slic3r/CMakeLists.txt b/src/slic3r/CMakeLists.txt index feca4025cb..ca40730f27 100644 --- a/src/slic3r/CMakeLists.txt +++ b/src/slic3r/CMakeLists.txt @@ -273,6 +273,8 @@ set(SLIC3R_GUI_SOURCES GUI/ImGuiWrapper.cpp GUI/DeviceManager.hpp GUI/DeviceManager.cpp + GUI/HttpServer.hpp + GUI/HttpServer.cpp Config/Snapshot.cpp Config/Snapshot.hpp Config/Version.cpp diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index ac914190d4..f6402df2fa 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -87,7 +87,6 @@ //BBS: DailyTip and UserGuide Dialog #include "WebDownPluginDlg.hpp" #include "WebGuideDialog.hpp" -#include "WebUserLoginDialog.hpp" #include "ReleaseNote.hpp" #include "ModelMall.hpp" @@ -2369,9 +2368,6 @@ bool GUI_App::on_init_inner() } //} - - - if (app_config->get("sync_user_preset") == "true") { //BBS loading user preset // Always async, not such startup step @@ -3103,14 +3099,24 @@ void GUI_App::ShowDownNetPluginDlg() { } } -void GUI_App::ShowUserLogin() +void GUI_App::ShowUserLogin(bool show) { // BBS: User Login Dialog - try { - ZUserLogin LoginDlg; - LoginDlg.ShowModal(); - } catch (std::exception &e) { - // wxMessageBox(e.what(), "", MB_OK); + if (show) { + try { + if (!login_dlg) + login_dlg = new ZUserLogin(); + else { + delete login_dlg; + login_dlg = new ZUserLogin(); + } + login_dlg->ShowModal(); + } catch (std::exception &e) { + ; + } + } else { + if (login_dlg) + login_dlg->EndModal(wxID_OK); } } @@ -3393,11 +3399,6 @@ std::string GUI_App::handle_web_request(std::string cmd) std::string web_cmd = j["command"].get(); if (web_cmd == "request_model_download") { - /* json j_data = j["data"]; - json import_j;*/ - /* import_j["model_id"] = j["data"]["model_id"].get(); - import_j["profile_id"] = j["data"]["profile_id"].get();*/ - std::string download_url = ""; if (j["data"].contains("download_url")) download_url = j["data"]["download_url"].get(); @@ -4161,6 +4162,16 @@ void GUI_App::stop_sync_user_preset() m_sync_update_thread.join(); } +void GUI_App::start_http_server() +{ + if (!m_http_server.is_started()) + m_http_server.start(); +} +void GUI_App::stop_http_server() +{ + m_http_server.stop(); +} + bool GUI_App::switch_language() { if (select_language()) { diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index d8738592b6..6d73a18890 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -11,8 +11,10 @@ #include "slic3r/GUI/DeviceManager.hpp" #include "slic3r/Utils/NetworkAgent.hpp" #include "slic3r/GUI/WebViewDialog.hpp" +#include "slic3r/GUI/WebUserLoginDialog.hpp" #include "slic3r/GUI/HMS.hpp" #include "slic3r/GUI/Jobs/UpgradeNetworkJob.hpp" +#include "slic3r/GUI/HttpServer.hpp" #include "../Utils/PrintHost.hpp" #include @@ -274,6 +276,9 @@ private: bool m_networking_cancel_update { false }; std::shared_ptr m_upgrade_network_job; + // login widget + ZUserLogin* login_dlg { nullptr }; + VersionInfo version_info; static std::string version_display; HMSQuery *hms_query { nullptr }; @@ -283,6 +288,7 @@ private: bool m_is_dark_mode{ false }; bool m_adding_script_handler { false }; bool m_side_popup_status{false}; + HttpServer m_http_server; public: void check_filaments_in_blacklist(std::string tag_supplier, std::string tag_material, bool& in_blacklist, std::string& action, std::string& info); std::string get_local_models_path(); @@ -381,7 +387,7 @@ public: wxString transition_tridid(int trid_id); void ShowUserGuide(); void ShowDownNetPluginDlg(); - void ShowUserLogin(); + void ShowUserLogin(bool show = true); void ShowOnlyFilament(); //BBS void request_login(bool show_user_info = false); @@ -424,6 +430,8 @@ public: void sync_preset(Preset* preset); void start_sync_user_preset(bool load_immediately = false, bool with_progress_dlg = false); void stop_sync_user_preset(); + void start_http_server(); + void stop_http_server(); static bool catch_error(std::function cb, const std::string& err); diff --git a/src/slic3r/GUI/HttpServer.cpp b/src/slic3r/GUI/HttpServer.cpp new file mode 100644 index 0000000000..06d1c54760 --- /dev/null +++ b/src/slic3r/GUI/HttpServer.cpp @@ -0,0 +1,151 @@ +#include "HttpServer.hpp" +#include +#include "GUI_App.hpp" +#include "slic3r/Utils/Http.hpp" +#include "slic3r/Utils/NetworkAgent.hpp" + +namespace Slic3r { +namespace GUI { + +static std::string parse_params(std::string url, std::string key) +{ + size_t start = url.find(key); + if (start < 0) return ""; + size_t eq = url.find('=', start); + if (eq < 0) return ""; + std::string key_str = url.substr(start, eq - start); + if (key_str != key) + return ""; + start += key.size() + 1; + size_t end = url.find('&', start); + if (end < 0) + return ""; + std::string result = url.substr(start, end - start); + return result; +} + +std::string http_headers::get_response() +{ + BOOST_LOG_TRIVIAL(info) << "thirdparty_login: get_response"; + std::stringstream ssOut; + std::string url_str = Http::url_decode(url); + if (boost::contains(url_str, "access_token")) { + std::string sHTML = "

redirect to url

"; + std::string redirect_url = parse_params(url_str, "redirect_url"); + std::string access_token = parse_params(url_str, "access_token"); + std::string refresh_token = parse_params(url_str, "refresh_token"); + std::string expires_in_str = parse_params(url_str, "expires_in"); + std::string refresh_expires_in_str = parse_params(url_str, "refresh_expires_in"); + NetworkAgent* agent = wxGetApp().getAgent(); + + unsigned int http_code; + std::string http_body; + int result = agent->get_my_profile(access_token, &http_code, &http_body); + if (result == 0) { + std::string user_id; + std::string user_name; + std::string user_account; + std::string user_avatar; + try { + json user_j = json::parse(http_body); + if (user_j.contains("uid")) + user_id = std::to_string(user_j["uid"].get()); + if (user_j.contains("name")) + user_name = user_j["name"].get(); + if (user_j.contains("avatar")) + user_avatar = user_j["avatar"].get(); + if (user_j.contains("account")) + user_account = user_j["account"].get(); + } catch (...) { + ; + } + json j; + j["data"]["refresh_token"] = refresh_token; + j["data"]["token"] = access_token; + j["data"]["expires_in"] = expires_in_str; + j["data"]["refresh_expires_in"] = refresh_expires_in_str; + j["data"]["user"]["uid"] = user_id; + j["data"]["user"]["name"] = user_name; + j["data"]["user"]["account"] = user_account; + j["data"]["user"]["avatar"] = user_avatar; + agent->change_user(j.dump()); + if (agent->is_user_login()) { + wxGetApp().request_user_login(1); + } + GUI::wxGetApp().CallAfter([this] { + wxGetApp().ShowUserLogin(false); + }); + std::string location_str = (boost::format("Location: %1%?result=success") % redirect_url).str(); + ssOut << "HTTP/1.1 302 Found" << std::endl; + ssOut << location_str << std::endl; + ssOut << "content-type: text/html" << std::endl; + ssOut << "content-length: " << sHTML.length() << std::endl; + ssOut << std::endl; + ssOut << sHTML; + } else { + std::string error_str = "get_user_profile_error_" + std::to_string(result); + std::string location_str = (boost::format("Location: %1%?result=fail&error=%2%") % redirect_url % error_str).str(); + ssOut << "HTTP/1.1 302 Found" << std::endl; + ssOut << location_str << std::endl; + ssOut << "content-type: text/html" << std::endl; + ssOut << "content-length: " << sHTML.length() << std::endl; + ssOut << std::endl; + ssOut << sHTML; + } + } else { + std::string sHTML = "

404 Not Found

There's nothing here.

"; + ssOut << "HTTP/1.1 404 Not Found" << std::endl; + ssOut << "content-type: text/html" << std::endl; + ssOut << "content-length: " << sHTML.length() << std::endl; + ssOut << std::endl; + ssOut << sHTML; + } + return ssOut.str(); +} + + +void accept_and_run(boost::asio::ip::tcp::acceptor& acceptor, boost::asio::io_service& io_service) +{ + std::shared_ptr sesh = std::make_shared(io_service); + acceptor.async_accept(sesh->socket, + [sesh, &acceptor, &io_service](const boost::beast::error_code& accept_error) + { + accept_and_run(acceptor, io_service); + if (!accept_error) + { + session::interact(sesh); + } + }); +} + +HttpServer::HttpServer() +{ + ; +} + +void HttpServer::start() +{ + BOOST_LOG_TRIVIAL(info) << "start_http_service..."; + start_http_server = true; + m_http_server_thread = Slic3r::create_thread( + [this] { + boost::asio::io_service io_service; + boost::asio::ip::tcp::endpoint endpoint{ boost::asio::ip::tcp::v4(), 9090}; + boost::asio::ip::tcp::acceptor acceptor { io_service, endpoint}; + acceptor.listen(); + accept_and_run(acceptor, io_service); + while (start_http_server) { + io_service.run(); + } + }); +} + +void HttpServer::stop() +{ + start_http_server = false; + if (m_http_server_thread.joinable()) + m_http_server_thread.join(); +} + +} // GUI +} //Slic3r diff --git a/src/slic3r/GUI/HttpServer.hpp b/src/slic3r/GUI/HttpServer.hpp new file mode 100644 index 0000000000..550c5b3c94 --- /dev/null +++ b/src/slic3r/GUI/HttpServer.hpp @@ -0,0 +1,162 @@ +#ifndef slic3r_Http_App_hpp_ +#define slic3r_Http_App_hpp_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +using namespace boost; +using namespace boost::system; +using namespace boost::asio; + +#define LOCALHOST_URL "http://localhost:9090" + +namespace Slic3r { +namespace GUI { + +class http_headers +{ + std::string method; + std::string url; + std::string version; + + std::map headers; + +public: + + std::string get_response(); + + int content_length() + { + auto request = headers.find("content-length"); + if (request != headers.end()) + { + std::stringstream ssLength(request->second); + int content_length; + ssLength >> content_length; + return content_length; + } + return 0; + } + + void on_read_header(std::string line) + { + //std::cout << "header: " << line << std::endl; + + std::stringstream ssHeader(line); + std::string headerName; + std::getline(ssHeader, headerName, ':'); + + std::string value; + std::getline(ssHeader, value); + headers[headerName] = value; + } + + void on_read_request_line(std::string line) + { + std::stringstream ssRequestLine(line); + ssRequestLine >> method; + ssRequestLine >> url; + ssRequestLine >> version; + + std::cout << "request for resource: " << url << std::endl; + } +}; + +class session +{ + asio::streambuf buff; + http_headers headers; + + static void read_body(std::shared_ptr pThis) + { + int nbuffer = 1000; + std::shared_ptr> bufptr = std::make_shared>(nbuffer); + asio::async_read(pThis->socket, boost::asio::buffer(*bufptr, nbuffer), [pThis](const boost::beast::error_code& e, std::size_t s) + { + }); + } + + static void read_next_line(std::shared_ptr pThis) + { + asio::async_read_until(pThis->socket, pThis->buff, '\r', [pThis](const boost::beast::error_code& e, std::size_t s) + { + std::string line, ignore; + std::istream stream{ &pThis->buff }; + std::getline(stream, line, '\r'); + std::getline(stream, ignore, '\n'); + pThis->headers.on_read_header(line); + + if (line.length() == 0) + { + if (pThis->headers.content_length() == 0) + { + std::shared_ptr str = std::make_shared(pThis->headers.get_response()); + asio::async_write(pThis->socket, boost::asio::buffer(str->c_str(), str->length()), [pThis, str](const boost::beast::error_code& e, std::size_t s) + { + std::cout << "done" << std::endl; + }); + } + else + { + pThis->read_body(pThis); + } + } + else + { + pThis->read_next_line(pThis); + } + }); + } + + static void read_first_line(std::shared_ptr pThis) + { + asio::async_read_until(pThis->socket, pThis->buff, '\r', [pThis](const boost::beast::error_code& e, std::size_t s) + { + std::string line, ignore; + std::istream stream{ &pThis->buff }; + std::getline(stream, line, '\r'); + std::getline(stream, ignore, '\n'); + pThis->headers.on_read_request_line(line); + pThis->read_next_line(pThis); + }); + } + +public: + + ip::tcp::socket socket; + + session(io_service& io_service) + :socket(io_service) + { + } + + static void interact(std::shared_ptr pThis) + { + read_first_line(pThis); + } +}; + +class HttpServer { +public: + HttpServer(); + + boost::thread m_http_server_thread; + bool start_http_server = false; + + bool is_started() { return start_http_server; } + void start(); + void stop(); +}; + +} +}; + +#endif diff --git a/src/slic3r/GUI/WebUserLoginDialog.cpp b/src/slic3r/GUI/WebUserLoginDialog.cpp index c94d9f4320..f482ad51ad 100644 --- a/src/slic3r/GUI/WebUserLoginDialog.cpp +++ b/src/slic3r/GUI/WebUserLoginDialog.cpp @@ -49,6 +49,8 @@ string &replace_str(string &str, const string &to_replaced, const string &newcha return str; } +int ZUserLogin::web_sequence_id = 20000; + ZUserLogin::ZUserLogin() : wxDialog((wxWindow *) (wxGetApp().mainframe), wxID_ANY, "BambuStudio") { SetBackgroundColour(*wxWHITE); @@ -249,7 +251,7 @@ void ZUserLogin::OnScriptMessage(wxWebViewEvent &evt) { wxString str_input = evt.GetString(); try { - json j = json::parse(str_input); + json j = json::parse(str_input); wxString strCmd = j["command"]; @@ -262,6 +264,31 @@ void ZUserLogin::OnScriptMessage(wxWebViewEvent &evt) wxGetApp().handle_script_message(j.dump()); Close(); } + else if (strCmd == "get_localhost_url") { + BOOST_LOG_TRIVIAL(info) << "thirdparty_login: get_localhost_url"; + wxGetApp().start_http_server(); + std::string sequence_id = j["sequence_id"].get(); + CallAfter([this, sequence_id] { + json ack_j; + ack_j["command"] = "get_localhost_url"; + ack_j["response"]["base_url"] = LOCALHOST_URL; + ack_j["response"]["result"] = "success"; + ack_j["sequence_id"] = sequence_id; + wxString str_js = wxString::Format("window.postMessage(%s)", ack_j.dump()); + this->RunScript(str_js); + }); + } + else if (strCmd == "thirdparty_login") { + BOOST_LOG_TRIVIAL(info) << "thirdparty_login: thirdparty_login"; + if (j["data"].contains("url")) { + std::string jump_url = j["data"]["url"].get(); + CallAfter([this, jump_url] { + wxString url = wxString::FromUTF8(jump_url); + wxLaunchDefaultBrowser(url); + }); + } + return; + } } catch (std::exception &e) { wxMessageBox(e.what(), "parse json failed", wxICON_WARNING); Close(); @@ -274,8 +301,6 @@ void ZUserLogin::RunScript(const wxString &javascript) // the "Run Script" dialog box, it is shown there for convenient updating. m_javascript = javascript; - // wxLogMessage("Running JavaScript:\n%s\n", javascript); - if (!m_browser) return; WebView::RunScript(m_browser, javascript); diff --git a/src/slic3r/GUI/WebUserLoginDialog.hpp b/src/slic3r/GUI/WebUserLoginDialog.hpp index ba46614669..6a820bef9a 100644 --- a/src/slic3r/GUI/WebUserLoginDialog.hpp +++ b/src/slic3r/GUI/WebUserLoginDialog.hpp @@ -27,8 +27,6 @@ #include #include "wx/textctrl.h" -#include "GUI_App.hpp" - namespace Slic3r { namespace GUI { class ZUserLogin : public wxDialog @@ -62,7 +60,7 @@ public: bool run(); - + static int web_sequence_id; private: wxTimer *m_timer { nullptr }; void OnTimer(wxTimerEvent &event); diff --git a/src/slic3r/Utils/NetworkAgent.cpp b/src/slic3r/Utils/NetworkAgent.cpp index bf5c4b4a1a..cd63fb4156 100644 --- a/src/slic3r/Utils/NetworkAgent.cpp +++ b/src/slic3r/Utils/NetworkAgent.cpp @@ -95,6 +95,7 @@ func_start_pubilsh NetworkAgent::start_publish_ptr = nullptr; func_get_profile_3mf NetworkAgent::get_profile_3mf_ptr = nullptr; func_get_model_publish_url NetworkAgent::get_model_publish_url_ptr = nullptr; func_get_model_mall_home_url NetworkAgent::get_model_mall_home_url_ptr = nullptr; +func_get_my_profile NetworkAgent::get_my_profile_ptr = nullptr; NetworkAgent::NetworkAgent() @@ -236,6 +237,7 @@ int NetworkAgent::initialize_network_module(bool using_backup) get_profile_3mf_ptr = reinterpret_cast(get_network_function("bambu_network_get_profile_3mf")); get_model_publish_url_ptr = reinterpret_cast(get_network_function("bambu_network_get_model_publish_url")); get_model_mall_home_url_ptr = reinterpret_cast(get_network_function("bambu_network_get_model_mall_home_url")); + get_my_profile_ptr = reinterpret_cast(get_network_function("bambu_network_get_my_profile")); return 0; } @@ -331,6 +333,7 @@ int NetworkAgent::unload_network_module() get_profile_3mf_ptr = nullptr; get_model_publish_url_ptr = nullptr; get_model_mall_home_url_ptr = nullptr; + get_my_profile_ptr = nullptr; return 0; } @@ -1103,4 +1106,15 @@ int NetworkAgent::get_model_mall_home_url(std::string* url) return ret; } +int NetworkAgent::get_my_profile(std::string token, unsigned int *http_code, std::string *http_body) +{ + int ret = 0; + if (network_agent && get_my_profile_ptr) { + ret = get_my_profile_ptr(network_agent, token, http_code, http_body); + if (ret) + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format("error network_agnet=%1%, ret = %2%") % network_agent % ret; + } + return ret; +} + } //namespace diff --git a/src/slic3r/Utils/NetworkAgent.hpp b/src/slic3r/Utils/NetworkAgent.hpp index 83bd1ffd7d..7d8b593214 100644 --- a/src/slic3r/Utils/NetworkAgent.hpp +++ b/src/slic3r/Utils/NetworkAgent.hpp @@ -75,6 +75,7 @@ typedef int (*func_start_pubilsh)(void *agent, PublishParams params, OnUpdateSta typedef int (*func_get_profile_3mf)(void *agent, BBLProfile* profile); typedef int (*func_get_model_publish_url)(void *agent, std::string* url); typedef int (*func_get_model_mall_home_url)(void *agent, std::string* url); +typedef int (*func_get_my_profile)(void *agent, std::string token, unsigned int *http_code, std::string *http_body); //the NetworkAgent class @@ -158,6 +159,7 @@ public: int get_profile_3mf(BBLProfile* profile); int get_model_publish_url(std::string* url); int get_model_mall_home_url(std::string* url); + int get_my_profile(std::string token, unsigned int* http_code, std::string* http_body); private: @@ -231,6 +233,7 @@ private: static func_get_profile_3mf get_profile_3mf_ptr; static func_get_model_publish_url get_model_publish_url_ptr; static func_get_model_mall_home_url get_model_mall_home_url_ptr; + static func_get_my_profile get_my_profile_ptr; }; }