mirror of
https://github.com/SantaSpeen/BeamMP-Server.git
synced 2025-08-18 12:45:36 +00:00
Merge pull request #10 from BeamMP/segfault-fix-1
PostHTTP rebuild, SegFault fix (fixes #8), good work.
This commit is contained in:
commit
dc9c3255de
@ -31,7 +31,7 @@ find_package(Lua REQUIRED)
|
||||
target_include_directories(BeamMP-Server PUBLIC ${LUA_INCLUDE_DIR})
|
||||
|
||||
if (UNIX)
|
||||
target_link_libraries(BeamMP-Server z pthread stdc++fs ${Boost_LINK_DIRS} ${LUA_LIBRARIES} curl dl)
|
||||
target_link_libraries(BeamMP-Server z pthread stdc++fs ${Boost_LINK_DIRS} ${LUA_LIBRARIES} curl dl crypto ssl)
|
||||
elseif (WIN32)
|
||||
include(FindLua)
|
||||
find_package(ZLIB REQUIRED)
|
||||
|
@ -7,5 +7,6 @@
|
||||
///
|
||||
#pragma once
|
||||
#include <string>
|
||||
std::string HttpRequest(const std::string& IP, int port);
|
||||
std::string PostHTTP(const std::string& IP, const std::string& Fields, bool json);
|
||||
#include <unordered_map>
|
||||
std::string HttpRequest(const std::string& host, int port, const std::string& target);
|
||||
std::string PostHTTP(const std::string& host, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, bool json);
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <future>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
void WebsocketInit();
|
||||
std::string GetPlayers() {
|
||||
@ -36,8 +37,8 @@ std::string GenerateCall() {
|
||||
<< "&desc=" << ServerDesc;
|
||||
return Ret.str();
|
||||
}
|
||||
std::string RunPromise(const std::string& IP, const std::string& R) {
|
||||
std::packaged_task<std::string()> task([&] { return PostHTTP(IP, R, false); });
|
||||
std::string RunPromise(const std::string& host, const std::string& target, const std::unordered_map<std::string, std::string>& R, const std::string& body) {
|
||||
std::packaged_task<std::string()> task([&] { DebugPrintTIDInternal("Heartbeat_POST"); return PostHTTP(host, target, R, body, false); });
|
||||
std::future<std::string> f1 = task.get_future();
|
||||
std::thread t(std::move(task));
|
||||
t.detach();
|
||||
@ -50,20 +51,32 @@ std::string RunPromise(const std::string& IP, const std::string& R) {
|
||||
|
||||
[[noreturn]] void Heartbeat() {
|
||||
DebugPrintTID();
|
||||
std::string R, T;
|
||||
std::string R;
|
||||
std::string T;
|
||||
|
||||
// these are "hot-change" related variables
|
||||
static std::string LastR = "";
|
||||
|
||||
static std::chrono::high_resolution_clock::time_point LastNormalUpdateTime = std::chrono::high_resolution_clock::now();
|
||||
bool isAuth = false;
|
||||
while (true) {
|
||||
R = GenerateCall();
|
||||
// a hot-change occurs when a setting has changed, to update the backend of that change.
|
||||
auto Now = std::chrono::high_resolution_clock::now();
|
||||
if (LastR == R && (Now - LastNormalUpdateTime) < std::chrono::seconds(30)) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
continue;
|
||||
}
|
||||
LastR = R;
|
||||
LastNormalUpdateTime = Now;
|
||||
if (!CustomIP.empty())
|
||||
R += "&ip=" + CustomIP;
|
||||
std::string link = "https://beammp.com/heartbeatv2";
|
||||
T = RunPromise(link, R);
|
||||
T = RunPromise("beammp.com", "/heartbeatv2", {}, R);
|
||||
|
||||
if (T.substr(0, 2) != "20") {
|
||||
//Backend system refused server startup!
|
||||
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||
std::string Backup = "https://backup1.beammp.com/heartbeatv2";
|
||||
T = RunPromise(Backup, R);
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
T = RunPromise("backup1.beammp.com", "/heartbeatv2", {}, R);
|
||||
if (T.substr(0, 2) != "20") {
|
||||
warn("Backend system refused server! Server might not show in the public list");
|
||||
}
|
||||
@ -71,11 +84,12 @@ std::string RunPromise(const std::string& IP, const std::string& R) {
|
||||
//Server Authenticated
|
||||
if (!isAuth) {
|
||||
WebsocketInit();
|
||||
if (T.length() == 4)info(("Authenticated!"));
|
||||
else info(("Resumed authenticated session!"));
|
||||
if (T.length() == 4)
|
||||
info(("Authenticated!"));
|
||||
else
|
||||
info(("Resumed authenticated session!"));
|
||||
isAuth = true;
|
||||
}
|
||||
//std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
}
|
||||
}
|
||||
void HBInit() {
|
||||
|
@ -20,8 +20,7 @@
|
||||
|
||||
std::string GetClientInfo(const std::string& PK) {
|
||||
if (!PK.empty()) {
|
||||
return PostHTTP("https://auth.beammp.com/pkToUser", R"({"key":")" + PK + "\"}", true);
|
||||
;
|
||||
return PostHTTP("auth.beammp.com", "/pkToUser", {}, R"({"key":")" + PK + "\"}", true);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
@ -7,69 +7,155 @@
|
||||
///
|
||||
|
||||
#include "CustomAssert.h"
|
||||
#include <curl/curl.h>
|
||||
#include <iostream>
|
||||
|
||||
class CurlManager{
|
||||
public:
|
||||
CurlManager(){
|
||||
curl = curl_easy_init();
|
||||
#include <boost/asio/connect.hpp>
|
||||
#include <boost/asio/ip/tcp.hpp>
|
||||
#include <boost/asio/ssl/error.hpp>
|
||||
#include <boost/asio/ssl/stream.hpp>
|
||||
#include <boost/beast/core.hpp>
|
||||
#include <boost/beast/http.hpp>
|
||||
#include <boost/beast/ssl.hpp>
|
||||
#include <boost/beast/version.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace beast = boost::beast; // from <boost/beast.hpp>
|
||||
namespace http = beast::http; // from <boost/beast/http.hpp>
|
||||
namespace net = boost::asio; // from <boost/asio.hpp>
|
||||
namespace ssl = net::ssl; // from <boost/asio/ssl.hpp>
|
||||
using tcp = net::ip::tcp; // from <boost/asio/ip/tcp.hpp>
|
||||
|
||||
// UNUSED?!
|
||||
std::string HttpRequest(const std::string& host, int port, const std::string& target) {
|
||||
// FIXME: this is likely not very well written.
|
||||
// if it causes issues, yell at me and I'll fix it asap. - Lion
|
||||
try {
|
||||
net::io_context io;
|
||||
tcp::resolver resolver(io);
|
||||
beast::tcp_stream stream(io);
|
||||
auto const results = resolver.resolve(host, std::to_string(port));
|
||||
stream.connect(results);
|
||||
|
||||
http::request<http::string_body> req { http::verb::get, target, 11 /* http 1.1 */ };
|
||||
|
||||
req.set(http::field::host, host);
|
||||
// tell the server what we are (boost beast)
|
||||
req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
|
||||
|
||||
http::write(stream, req);
|
||||
|
||||
// used for reading
|
||||
beast::flat_buffer buffer;
|
||||
http::response<http::string_body> response;
|
||||
|
||||
http::read(stream, buffer, response);
|
||||
|
||||
std::string result(response.body());
|
||||
|
||||
beast::error_code ec;
|
||||
stream.socket().shutdown(tcp::socket::shutdown_both, ec);
|
||||
if (ec && ec != beast::errc::not_connected) {
|
||||
throw beast::system_error { ec }; // goes down to `return "-1"` anyways
|
||||
}
|
||||
~CurlManager(){
|
||||
curl_easy_cleanup(curl);
|
||||
|
||||
return result;
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
error(e.what());
|
||||
return "-1";
|
||||
}
|
||||
inline CURL* Get(){
|
||||
return curl;
|
||||
}
|
||||
private:
|
||||
CURL *curl;
|
||||
|
||||
std::string PostHTTP(const std::string& host, const std::string& target, const std::unordered_map<std::string, std::string>& fields, const std::string& body, bool json) {
|
||||
try {
|
||||
net::io_context io;
|
||||
|
||||
// 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<beast::tcp_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(443));
|
||||
if (!SSL_set_tlsext_host_name(stream.native_handle(), host.c_str())) {
|
||||
boost::system::error_code ec { static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category() };
|
||||
// FIXME: we could throw and crash, if we like
|
||||
// throw boost::system::system_error { ec };
|
||||
debug("POST " + host + target + " failed.");
|
||||
return false;
|
||||
}
|
||||
beast::get_lowest_layer(stream).connect(results);
|
||||
} catch (const boost::system::system_error&) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
static size_t WriteCallback(void* contents, size_t size, size_t nmemb, void* userp) {
|
||||
((std::string*)userp)->append((char*)contents, size * nmemb);
|
||||
return size * nmemb;
|
||||
}
|
||||
std::string HttpRequest(const std::string& IP, int port) {
|
||||
static thread_local CurlManager M;
|
||||
std::string readBuffer;
|
||||
CURL* curl = M.Get();
|
||||
CURLcode res;
|
||||
Assert(curl);
|
||||
if (curl) {
|
||||
curl_easy_setopt(curl, CURLOPT_URL, IP.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_PORT, port);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
|
||||
res = curl_easy_perform(curl);
|
||||
if (res != CURLE_OK)
|
||||
bool ok = try_connect_with_protocol(tcp::v6());
|
||||
if (!ok) {
|
||||
debug("IPv6 connect failed, trying IPv4");
|
||||
ok = try_connect_with_protocol(tcp::v4());
|
||||
if (!ok) {
|
||||
error("failed to resolve or connect in POST " + host + target);
|
||||
return "-1";
|
||||
}
|
||||
return readBuffer;
|
||||
}
|
||||
stream.handshake(ssl::stream_base::client);
|
||||
http::request<http::string_body> req { http::verb::post, target, 11 /* http 1.1 */ };
|
||||
|
||||
req.set(http::field::host, host);
|
||||
if (!body.empty()) {
|
||||
if (json) {
|
||||
// FIXME: json is untested.
|
||||
req.set(http::field::content_type, "application/json");
|
||||
} else {
|
||||
req.set(http::field::content_type, "application/x-www-form-urlencoded");
|
||||
}
|
||||
req.set(http::field::content_length, boost::lexical_cast<std::string>(body.size()));
|
||||
req.body() = body;
|
||||
// info("body is " + body + " (" + req.body() + ")");
|
||||
// info("content size is " + std::to_string(body.size()) + " (" + boost::lexical_cast<std::string>(body.size()) + ")");
|
||||
}
|
||||
for (const auto& pair : fields) {
|
||||
// info("setting " + pair.first + " to " + pair.second);
|
||||
req.set(pair.first, pair.second);
|
||||
}
|
||||
|
||||
std::string PostHTTP(const std::string& IP, const std::string& Fields, bool json) {
|
||||
auto header = curl_slist { (char*)"Content-Type: application/json" };
|
||||
static thread_local CurlManager M;
|
||||
static std::mutex Lock;
|
||||
std::scoped_lock Guard(Lock);
|
||||
CURL* curl = M.Get();
|
||||
CURLcode res;
|
||||
std::string readBuffer;
|
||||
std::stringstream oss;
|
||||
oss << req;
|
||||
|
||||
Assert(curl);
|
||||
if (curl) {
|
||||
curl_easy_setopt(curl, CURLOPT_URL, IP.c_str());
|
||||
if (json)
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header);
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, Fields.size());
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, Fields.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer);
|
||||
curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
|
||||
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5);
|
||||
res = curl_easy_perform(curl);
|
||||
if (res != CURLE_OK)
|
||||
beast::get_lowest_layer(stream).expires_after(std::chrono::seconds(5));
|
||||
|
||||
http::write(stream, req);
|
||||
|
||||
// used for reading
|
||||
beast::flat_buffer buffer;
|
||||
http::response<http::string_body> response;
|
||||
|
||||
http::read(stream, buffer, response);
|
||||
|
||||
std::stringstream result;
|
||||
result << response;
|
||||
|
||||
beast::error_code ec;
|
||||
stream.shutdown(ec);
|
||||
// IGNORING ec
|
||||
|
||||
// info(result.str());
|
||||
std::string debug_response_str;
|
||||
std::getline(result, debug_response_str);
|
||||
debug("POST " + host + target + ": " + debug_response_str);
|
||||
return std::string(response.body());
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
error(e.what());
|
||||
return "-1";
|
||||
}
|
||||
return readBuffer;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user