From 701a7feee3ca4e7faf1c9596b6c99dff5672bbf2 Mon Sep 17 00:00:00 2001 From: Lion Kortlepel Date: Sun, 7 Nov 2021 23:54:33 +0100 Subject: [PATCH] remove boost, add httplib, temporarily remove http* lua --- .github/workflows/cmake-linux.yml | 2 - .github/workflows/cmake-windows.yml | 2 +- .gitmodules | 3 + CMakeLists.txt | 11 +-- README.md | 9 +- deps/cpp-httplib | 1 + include/Http.h | 2 +- src/Http.cpp | 139 +++++----------------------- src/THeartbeatThread.cpp | 6 +- src/TLuaEngine.cpp | 13 +-- src/TNetwork.cpp | 2 +- 11 files changed, 43 insertions(+), 147 deletions(-) create mode 160000 deps/cpp-httplib diff --git a/.github/workflows/cmake-linux.yml b/.github/workflows/cmake-linux.yml index 098deaa..3ddf653 100644 --- a/.github/workflows/cmake-linux.yml +++ b/.github/workflows/cmake-linux.yml @@ -21,8 +21,6 @@ jobs: echo ${#beammp_sentry_url} sudo apt-get update sudo apt-get install -y libz-dev rapidjson-dev liblua5.3 libssl-dev libwebsocketpp-dev libcurl4-openssl-dev - sudo add-apt-repository ppa:mhier/libboost-latest - sudo apt-get install -y libboost1.70-dev libboost1.70 - name: Create Build Environment run: cmake -E make_directory ${{github.workspace}}/build-linux diff --git a/.github/workflows/cmake-windows.yml b/.github/workflows/cmake-windows.yml index e4c5fac..9ea76a7 100644 --- a/.github/workflows/cmake-windows.yml +++ b/.github/workflows/cmake-windows.yml @@ -18,7 +18,7 @@ jobs: uses: lukka/run-vcpkg@v7 id: runvcpkg with: - vcpkgArguments: 'lua zlib rapidjson boost-beast boost-asio openssl websocketpp curl' + vcpkgArguments: 'lua zlib rapidjson openssl websocketpp curl' vcpkgDirectory: '${{ runner.workspace }}/b/vcpkg' vcpkgGitCommitId: '8dddc6c899ce6fdbeab38b525a31e7f23cb2d5bb' vcpkgTriplet: 'x64-windows-static' diff --git a/.gitmodules b/.gitmodules index 1822884..d81806c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -22,3 +22,6 @@ [submodule "deps/libzip"] path = deps/libzip url = https://github.com/nih-at/libzip +[submodule "deps/cpp-httplib"] + path = deps/cpp-httplib + url = https://github.com/yhirose/cpp-httplib diff --git a/CMakeLists.txt b/CMakeLists.txt index acac6e2..b21de0e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,8 +13,11 @@ include_directories("${PROJECT_SOURCE_DIR}/deps/rapidjson/include") include_directories("${PROJECT_SOURCE_DIR}/deps/websocketpp") include_directories("${PROJECT_SOURCE_DIR}/deps/commandline") include_directories("${PROJECT_SOURCE_DIR}/deps/sol2/include") +include_directories("${PROJECT_SOURCE_DIR}/deps/cpp-httplib") include_directories("${PROJECT_SOURCE_DIR}/deps") +add_compile_definitions(CPPHTTPLIB_OPENSSL_SUPPORT) + if (WIN32) # this has to happen before sentry, so that crashpad on windows links with these settings. message(STATUS "MSVC -> forcing use of statically-linked runtime.") @@ -67,9 +70,6 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") -message(STATUS "Looking for Boost") -find_package(Boost REQUIRED COMPONENTS system thread) - add_executable(BeamMP-Server src/main.cpp include/TConsole.h src/TConsole.cpp @@ -92,7 +92,7 @@ add_executable(BeamMP-Server include/SignalHandling.h src/SignalHandling.cpp) target_compile_definitions(BeamMP-Server PRIVATE SECRET_SENTRY_URL="${BEAMMP_SECRET_SENTRY_URL}") -include_directories(BeamMP-Server PUBLIC ${Boost_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/include) +include_directories(BeamMP-Server PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) target_include_directories(BeamMP-Server PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/include" @@ -101,7 +101,6 @@ target_include_directories(BeamMP-Server PUBLIC message(STATUS "Looking for Lua") find_package(Lua REQUIRED) target_include_directories(BeamMP-Server PUBLIC - ${Boost_INCLUDE_DIRS} ${LUA_INCLUDE_DIR} ${CURL_INCLUDE_DIRS} "include/tomlplusplus" @@ -131,7 +130,7 @@ elseif (WIN32) find_package(ZLIB REQUIRED) message(STATUS "Looking for RapidJSON") find_package(RapidJSON CONFIG REQUIRED) - target_include_directories(BeamMP-Server PRIVATE ${RAPIDJSON_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS}) + target_include_directories(BeamMP-Server PRIVATE ${RAPIDJSON_INCLUDE_DIRS}) target_link_libraries(BeamMP-Server ws2_32 ZLIB::ZLIB diff --git a/README.md b/README.md index 7ad35c9..fe909d5 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ Currently only linux and windows are supported (generally). See [Releases](https Please use the prepackaged binaries in [Releases](https://github.com/BeamMP/BeamMP-Server/releases/). -Dependencies for windows can be installed with `vcpkg`, in which case the current dependencies are the `x64-windows-static` versions of `lua`, `zlib`, `rapidjson`, `boost-beast`, `boost-asio` and `openssl`. +Dependencies for windows can be installed with `vcpkg`, in which case the current dependencies are the `x64-windows-static` versions of `lua`, `zlib`, `rapidjson`, and `openssl`. #### Linux / \*nix @@ -72,19 +72,16 @@ These package names are in the debian / ubuntu style. Feel free to PR your own g - `rapidjson-dev` - `libopenssl-dev` or `libssl-dev` -**If** you're building it from source, you'll need `libboost1.70-all-dev` or `libboost1.71-all-dev` or higher as well. -If you can't find this version of boost (only 1.6x, for example), you can either update to a newer version of your distro, build boost yourself, or use an unstable rolling release (like Debian `sid` aka `unstable`). - In the end you should end up with a command something like this: ```sh -sudo apt install git make cmake g++-10 liblua5.3 libz-dev rapidjson-dev libopenssl-dev libboost1.71-all-dev +sudo apt install git make cmake g++-10 liblua5.3 libz-dev rapidjson-dev libopenssl-dev ``` In the end you should end up with a command something like this: ```sh -sudo apt install git make cmake g++-10 liblua5.3 libz-dev rapidjson-dev libopenssl-dev libboost1.71-all-dev +sudo apt install git make cmake g++-10 liblua5.3 libz-dev rapidjson-dev libopenssl-dev ``` ### How to build diff --git a/deps/cpp-httplib b/deps/cpp-httplib new file mode 160000 index 0000000..301faa0 --- /dev/null +++ b/deps/cpp-httplib @@ -0,0 +1 @@ +Subproject commit 301faa074c4a0fa1dbe470dfb4f77912caa1c57f diff --git a/include/Http.h b/include/Http.h index daf38da..568054b 100644 --- a/include/Http.h +++ b/include/Http.h @@ -5,7 +5,7 @@ namespace Http { std::string GET(const std::string& host, int port, const std::string& target, unsigned int* status = nullptr); -std::string POST(const std::string& host, int port, const std::string& target, const std::unordered_map& fields, const std::string& body, const std::string& ContentType, unsigned int* status = nullptr); +std::string POST(const std::string& host, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status = nullptr); namespace Status { std::string ToString(int code); } diff --git a/src/Http.cpp b/src/Http.cpp index 9d7203e..2ad735c 100644 --- a/src/Http.cpp +++ b/src/Http.cpp @@ -1,134 +1,39 @@ #include "Http.h" #include "Common.h" -#undef error -#include -#include -#include -#include #include -namespace beast = boost::beast; // from -namespace http = beast::http; // from -namespace net = boost::asio; // from -namespace ssl = net::ssl; // from -using tcp = net::ip::tcp; // from +#include -std::string GenericRequest(http::verb verb, const std::string& host, int port, const std::string& target, const std::unordered_map& fields, const std::string& body, const std::string& ContentType, unsigned int* status) { - try { - net::io_context io; +// TODO: Add sentry error handling back - // The SSL context is required, and holds certificates - ssl::context ctx(ssl::context::tlsv13); - - ctx.set_verify_mode(ssl::verify_none); - - tcp::resolver resolver(io); - beast::ssl_stream stream(io, ctx); - decltype(resolver)::results_type results; - auto try_connect_with_protocol = [&](tcp protocol) { - try { - results = resolver.resolve(protocol, host, std::to_string(port)); - if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) { - boost::system::error_code ec { static_cast(::ERR_get_error()), boost::asio::error::get_ssl_category() }; - return false; - } - beast::get_lowest_layer(stream).connect(results); - } catch (const boost::system::system_error&) { - return false; - } - return true; - }; - bool ok = try_connect_with_protocol(tcp::v4()); - if (!ok) { - Application::Console().Write("[ERROR] failed to resolve or connect in POST " + host + target); - Sentry.AddErrorBreadcrumb("failed to resolve or connect to " + host + target, __FILE__, std::to_string(__LINE__)); // FIXME: this is ugly. - return "-1"; - } - stream.handshake(ssl::stream_base::client); - http::request req { verb, target, 11 /* http 1.1 */ }; - - req.set(http::field::host, host); - if (!body.empty()) { - if (!ContentType.empty()) { - req.set(http::field::content_type, ContentType); // "application/json" - // "application/x-www-form-urlencoded" - } - - req.set(http::field::content_length, std::to_string(body.size())); - req.body() = body; - } - for (const auto& pair : fields) { - req.set(pair.first, pair.second); - } - - std::unordered_map request_data; - for (const auto& header : req.base()) { - // need to do explicit casts to convert string_view to string - // since string_view may not be null-terminated (and in fact isn't, here) - std::string KeyString(header.name_string()); - std::string ValueString(header.value()); - request_data[KeyString] = ValueString; - } - Sentry.SetContext("https-request-data", request_data); - - std::stringstream oss; - oss << req; - - beast::get_lowest_layer(stream).expires_after(std::chrono::seconds(5)); - - http::write(stream, req); - - // used for reading - beast::flat_buffer buffer; - http::response response; - - http::read(stream, buffer, response); - - std::unordered_map response_data; - response_data["reponse-code"] = std::to_string(response.result_int()); +std::string Http::GET(const std::string& host, int port, const std::string& target, unsigned int* status) { + httplib::SSLClient client(host, port); + client.enable_server_certificate_verification(false); + auto res = client.Get(target.c_str()); + if (res) { if (status) { - *status = response.result_int(); + *status = res->status; } - for (const auto& header : response.base()) { - // need to do explicit casts to convert string_view to string - // since string_view may not be null-terminated (and in fact isn't, here) - std::string KeyString(header.name_string()); - std::string ValueString(header.value()); - response_data[KeyString] = ValueString; - } - Sentry.SetContext("https-response-data", response_data); - - if (status) { - *status = response.base().result_int(); - } - - std::stringstream result; - result << response; - - beast::error_code ec; - stream.shutdown(ec); - // IGNORING ec - - std::string debug_response_str; - std::getline(result, debug_response_str); - - return std::string(response.body()); - - } catch (const std::exception& e) { - Application::Console().Write(__func__ + std::string(": ") + e.what()); - Sentry.AddErrorBreadcrumb(e.what(), __FILE__, std::to_string(__LINE__)); // FIXME: this is ugly. + return res->body; + } else { return Http::ErrorString; } } -std::string Http::GET(const std::string& host, int port, const std::string& target, unsigned int* status) { - return GenericRequest(http::verb::get, host, port, target, {}, {}, "", status); -} - -std::string Http::POST(const std::string& host, int port, const std::string& target, const std::unordered_map& fields, const std::string& body, const std::string& ContentType, unsigned int* status) { - return GenericRequest(http::verb::post, host, port, target, fields, body, ContentType, status); +std::string Http::POST(const std::string& host, int port, const std::string& target, const std::string& body, const std::string& ContentType, unsigned int* status) { + httplib::SSLClient client(host, port); + client.enable_server_certificate_verification(false); + auto res = client.Post(target.c_str(), body.c_str(), body.size(), ContentType.c_str()); + if (res) { + if (status) { + *status = res->status; + } + return res->body; + } else { + return Http::ErrorString; + } } // RFC 2616, RFC 7231 diff --git a/src/THeartbeatThread.cpp b/src/THeartbeatThread.cpp index cd5aa83..92e8585 100644 --- a/src/THeartbeatThread.cpp +++ b/src/THeartbeatThread.cpp @@ -48,17 +48,17 @@ void THeartbeatThread::operator()() { auto Target = "/heartbeat"; unsigned int ResponseCode = 0; - T = Http::POST(Application::GetBackendHostname(), 443, Target, {}, Body, "application/x-www-form-urlencoded", &ResponseCode); + T = Http::POST(Application::GetBackendHostname(), 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode); if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) { beammp_trace("got " + T + " from backend"); SentryReportError(Application::GetBackendHostname() + Target, ResponseCode); std::this_thread::sleep_for(std::chrono::milliseconds(500)); - T = Http::POST(Application::GetBackup1Hostname(), 443, Target, {}, Body, "application/x-www-form-urlencoded", &ResponseCode); + T = Http::POST(Application::GetBackup1Hostname(), 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode); if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) { SentryReportError(Application::GetBackup1Hostname() + Target, ResponseCode); std::this_thread::sleep_for(std::chrono::milliseconds(500)); - T = Http::POST(Application::GetBackup2Hostname(), 443, Target, {}, Body, "application/x-www-form-urlencoded", &ResponseCode); + T = Http::POST(Application::GetBackup2Hostname(), 443, Target, Body, "application/x-www-form-urlencoded", &ResponseCode); if ((T.substr(0, 2) != "20" && ResponseCode != 200) || ResponseCode != 200) { beammp_warn("Backend system refused server! Server will not show in the public server list."); diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 905f338..c9bdd71 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -446,16 +446,9 @@ TLuaEngine::StateThreadData::StateThreadData(const std::string& Name, std::atomi mEngine->CancelEventTimers(EventName, mStateId); }); MPTable.set_function("Set", &LuaAPI::MP::Set); - MPTable.set_function("HttpsGET", [&](const std::string& Host, int Port, const std::string& Target) -> std::tuple { - unsigned Status; - auto Body = Http::GET(Host, Port, Target, &Status); - return { Status, Body }; - }); - MPTable.set_function("HttpsPOST", [&](const std::string& Host, int Port, const std::string& Target, const std::string& Body, const std::string& ContentType) -> std::tuple { - unsigned Status; - auto ResponseBody = Http::POST(Host, Port, Target, {}, Body, ContentType, &Status); - return { Status, ResponseBody }; - }); + auto HttpTable = StateView.create_named_table("Http"); + //HttpTable.set_function("CreateConnection", &LuaAPI::Http::CreateConnection); + MPTable.create_named("Settings", "Debug", 0, "Private", 1, diff --git a/src/TNetwork.cpp b/src/TNetwork.cpp index d71e23d..859579f 100644 --- a/src/TNetwork.cpp +++ b/src/TNetwork.cpp @@ -241,7 +241,7 @@ void TNetwork::Authentication(const TConnection& ClientConnection) { auto Target = "/pkToUser"; unsigned int ResponseCode = 0; if (!Rc.empty()) { - Rc = Http::POST(Application::GetBackendUrlForAuth(), 443, Target, {}, RequestString, "application/json", &ResponseCode); + Rc = Http::POST(Application::GetBackendUrlForAuth(), 443, Target, RequestString, "application/json", &ResponseCode); } json::Document AuthResponse;