mirror of
https://github.com/SWG-Source/src.git
synced 2026-07-13 21:01:08 -04:00
make the webAPI more generic so that we can use it in a more standardized fashion
This commit is contained in:
@@ -123,9 +123,10 @@
|
||||
#include <stdio.h>
|
||||
|
||||
|
||||
// Trying todo something here ...
|
||||
#include "webAPI.h"
|
||||
|
||||
using namespace StellaBellum;
|
||||
|
||||
namespace CentralServerNamespace
|
||||
{
|
||||
bool gs_connectionServersPublic=false;
|
||||
@@ -2782,15 +2783,14 @@ void CentralServer::update()
|
||||
|
||||
// update the webAPI if specified
|
||||
int webUpdateIntervalSeconds = ConfigCentralServer::getWebUpdateIntervalSeconds();
|
||||
std::string updateURL = std::string(ConfigCentralServer::getMetricsDataURL());
|
||||
|
||||
// assuming that every 5th frame is ~1 second, we can multiply and then check
|
||||
if ( !(updateURL.empty()) && webUpdateIntervalSeconds && (++apiLoopCount > (webUpdateIntervalSeconds*1000)) )
|
||||
if (webUpdateIntervalSeconds && (++apiLoopCount > (webUpdateIntervalSeconds*1000)) )
|
||||
{
|
||||
apiLoopCount = 0;
|
||||
|
||||
// update the web api
|
||||
sendMetricsToWebAPI(updateURL);
|
||||
sendMetricsToWebAPI();
|
||||
}
|
||||
|
||||
if ( ConfigCentralServer::getAuctionEnabled() ) // allow auctions?
|
||||
@@ -2860,15 +2860,48 @@ void CentralServer::sendPopulationUpdateToLoginServer()
|
||||
sendToAllLoginServers(upm);
|
||||
}
|
||||
|
||||
// TODO: for sending metrics and such, would it make more sense to pass an object instead of a huge assed string as below?
|
||||
void CentralServer::sendMetricsToWebAPI(std::string updateURL)
|
||||
void CentralServer::sendMetricsToWebAPI()
|
||||
{
|
||||
std::ostringstream postBuf;
|
||||
// create the object
|
||||
webAPI api = webAPI::webAPI(std::string(ConfigCentralServer::getMetricsDataURL()));
|
||||
|
||||
postBuf << "totalPlayerCount=" << m_totalPlayerCount << "&totalGameServers=" << m_gameServers.size() - 1 << "&totalPlanetServers=" << m_planetServers.size() << "&isPublic=" << getIsClusterPublic() << "&isLocked=" << getIsClusterLocked() << "&isSecret=" << getIsClusterSecret() << "&preloadFinished=" << getClusterStartupTime() << "&databasebacklogged=" << isDatabaseBacklogged() << "&totalTutorialSceneCount=" << m_totalTutorialSceneCount << "&totalFalconSceneCount=" << m_totalFalconSceneCount;
|
||||
|
||||
webAPI::statusMessage response = webAPI::simplePost(updateURL, std::string(postBuf.str()));
|
||||
WARNING(response.status, ("Error sending stats: %s", response.retVal.c_str()));
|
||||
// add our data
|
||||
api.addJsonData<int>("totalPlayerCount", m_totalPlayerCount);
|
||||
api.addJsonData<int>("totalGameServers", (m_gameServers.size() - 1));
|
||||
api.addJsonData<int>("totalPlanetServers", m_planetServers.size());
|
||||
api.addJsonData<bool>("isPublic", getIsClusterPublic());
|
||||
api.addJsonData<bool>("isLocked", getIsClusterLocked());
|
||||
api.addJsonData<bool>("isSecret", getIsClusterSecret());
|
||||
api.addJsonData<bool>("preloadFinished", getClusterStartupTime());
|
||||
api.addJsonData<bool>("databasebacklogged", isDatabaseBacklogged());
|
||||
api.addJsonData<int>("totalTutorialSceneCount", getClusterStartupTime());
|
||||
api.addJsonData<int>("totalFalconSceneCount", isDatabaseBacklogged());
|
||||
|
||||
#ifdef _DEBUG
|
||||
if (api.submit()) {
|
||||
std::string status = api.getRespValue<std::string>("status");
|
||||
|
||||
if (status.empty() || status != "success")
|
||||
{
|
||||
std::string message = api.getRespValue<std::string>("message");
|
||||
|
||||
if (message.empty())
|
||||
{
|
||||
message = "No message returned.";
|
||||
}
|
||||
|
||||
WARNING(true, ("Error sending stats: %s", message.c_str()));
|
||||
}
|
||||
|
||||
WARNING(true, ("Success sending server stats to API."));
|
||||
}
|
||||
else
|
||||
{
|
||||
WARNING(true, ("Error sending stats."));
|
||||
}
|
||||
#else
|
||||
api.submit();
|
||||
#endif
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
@@ -182,7 +182,7 @@ private:
|
||||
size_t getGameServerCount (void) const;
|
||||
void update();
|
||||
void sendPopulationUpdateToLoginServer();
|
||||
void sendMetricsToWebAPI(std::string updateURL);
|
||||
void sendMetricsToWebAPI();
|
||||
ConnectionServerConnection * getConnectionServerForAccount(StationId suid);
|
||||
void addToAccountConnectionMap(StationId suid, ConnectionServerConnection * cconn, uint32 subscriptionBits);
|
||||
void removeFromAccountConnectionMap(int connectionServerConnectionId);
|
||||
|
||||
@@ -23,9 +23,12 @@
|
||||
#include "sharedNetworkMessages/GenericValueTypeMessage.h"
|
||||
#include "sharedNetworkMessages/LoginEnumCluster.h"
|
||||
|
||||
#include "webAPI.h"
|
||||
#include <algorithm>
|
||||
|
||||
#include "webAPI.h"
|
||||
|
||||
using namespace StellaBellum;
|
||||
|
||||
//-----------------------------------------------------------------------
|
||||
|
||||
ClientConnection::ClientConnection(UdpConnectionMT * u, TcpClient * t) :
|
||||
@@ -179,22 +182,38 @@ void ClientConnection::validateClient(const std::string & id, const std::string
|
||||
|
||||
if (!authURL.empty())
|
||||
{
|
||||
std::ostringstream postBuf;
|
||||
postBuf << "user_name=" << id << "&user_password=" << key << "&ip=" << getRemoteAddress();
|
||||
// create the object
|
||||
webAPI api = webAPI::webAPI(authURL);
|
||||
|
||||
const webAPI::statusMessage response = webAPI::simplePost(authURL, std::string(postBuf.str()), "username");
|
||||
// add our data
|
||||
api.addJsonData<std::string>("user_name", id);
|
||||
api.addJsonData<std::string>("user_password", key);
|
||||
api.addJsonData<std::string>("ip", getRemoteAddress());
|
||||
|
||||
// true indicates that we logged in successfully via the api
|
||||
// since both cases rely on response.retVal which will never be empty
|
||||
// ...we needn't check if it's empty/not
|
||||
if (response.status)
|
||||
if (api.submit())
|
||||
{
|
||||
authOK = true;
|
||||
uname = response.retVal;
|
||||
std::string status = api.getRespValue<std::string>("status");
|
||||
uname = api.getRespValue<std::string>("username");
|
||||
|
||||
if (!status.empty() && status == "success" && !uname.empty())
|
||||
{
|
||||
authOK = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string msg = api.getRespValue<std::string>("message");
|
||||
if (msg.empty())
|
||||
{
|
||||
msg = "Invalid username or password.";
|
||||
}
|
||||
|
||||
ErrorMessage err("Login Failed", msg);
|
||||
this->send(err, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorMessage err("Login Failed", response.retVal);
|
||||
ErrorMessage err("Login Failed", "Could not connect to remote.");
|
||||
this->send(err, true);
|
||||
}
|
||||
}
|
||||
|
||||
+138
-83
@@ -1,4 +1,6 @@
|
||||
/*
|
||||
Version: 1.3
|
||||
|
||||
This code is just a simple wrapper around nlohmann's wonderful json lib
|
||||
(https://github.com/nlohmann/json) and libcurl. While originally included directly,
|
||||
we have come to realize that we may require web API functionality elsewhere in the future.
|
||||
@@ -15,64 +17,102 @@ License: what's a license? we're a bunch of dirty pirates!
|
||||
|
||||
#include "webAPI.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace StellaBellum;
|
||||
|
||||
/*
|
||||
endpoint: URI
|
||||
data: get/post
|
||||
messageSlot: key for fetching a verbose user friendly message
|
||||
statusSlot: slot containing the success/fail value
|
||||
statusVal: the expected value in the case of success
|
||||
*/
|
||||
webAPI::statusMessage webAPI::simplePost(const string &endpoint, const string &data, const string &slotName, const string &messageSlot, const string &statusSlot, const string &statusVal)
|
||||
webAPI::webAPI(std::string endpoint, std::string userAgent) : uri(endpoint), userAgent(userAgent) {}
|
||||
webAPI::~webAPI()
|
||||
{
|
||||
// declare our output and go ahead and attempt to get data from remote
|
||||
nlohmann::json response = request(endpoint, data);
|
||||
|
||||
// if we got data back...
|
||||
if (response.count(statusSlot) && response[statusSlot].get<std::string>() == statusVal && response.count(slotName))
|
||||
{
|
||||
return {true, response[slotName].get<std::string>()};
|
||||
|
||||
}
|
||||
else //default message is an error, the other end always assumes "success" or the specified slot
|
||||
{
|
||||
if (response.count(messageSlot))
|
||||
{
|
||||
return {false, response[messageSlot].get<std::string>()};
|
||||
}
|
||||
}
|
||||
|
||||
return {false, "Message not provided by remote."};
|
||||
requestData.clear();
|
||||
responseData.clear();
|
||||
}
|
||||
|
||||
// this can be broken out to separate the json bits later if we need raw or other http type requests
|
||||
// all it does is fetch via get or post, and if the status is 200 returns the json, else error json
|
||||
nlohmann::json webAPI::request(const string &endpoint, const string &data, const int &reqType)
|
||||
bool webAPI::setEndpoint(std::string endpoint)
|
||||
{
|
||||
nlohmann::json response;
|
||||
uri = endpoint;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!endpoint.empty()) //data is allowed to be an empty string if we're doing a normal GET
|
||||
std::string webAPI::getRaw()
|
||||
{
|
||||
return sResponse;
|
||||
}
|
||||
|
||||
bool webAPI::setData(std::string &data)
|
||||
{
|
||||
if (!data.empty())
|
||||
{
|
||||
sRequest = data;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool webAPI::submit(const int &reqType, const int &getPost, const int &respType)
|
||||
{
|
||||
|
||||
if (reqType == 0) // json request
|
||||
{
|
||||
if (!requestData.is_null())
|
||||
{
|
||||
// serialize our data into sRequest
|
||||
sRequest = requestData.dump();
|
||||
|
||||
// clear our the object for next time
|
||||
requestData.clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (fetch(getPost, respType) && !(sResponse.empty()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
sResponse.clear();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool webAPI::fetch(const int &getPost, const int &mimeType) // 0 for json 1 for string
|
||||
{
|
||||
if (!uri.empty()) //data is allowed to be an empty string if we're doing a normal GET
|
||||
{
|
||||
CURL *curl = curl_easy_init(); // start up curl
|
||||
|
||||
if (curl)
|
||||
{
|
||||
string readBuffer; // container for the remote response
|
||||
long http_code = 0; // we get this after performing the get or post
|
||||
|
||||
std::string readBuffer; // container for the remote response
|
||||
struct curl_slist *slist = nullptr;
|
||||
|
||||
// set the content type
|
||||
if (mimeType == 0)
|
||||
{
|
||||
slist = curl_slist_append(slist, "Content-Type: application/json");
|
||||
}
|
||||
else
|
||||
{
|
||||
slist = curl_slist_append(slist, "Content-Type: application/x-www-form-urlencoded");
|
||||
}
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, userAgent.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback); // place the data into readBuffer using writeCallback
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &readBuffer); // specify readBuffer as the container for data
|
||||
|
||||
switch (reqType)
|
||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
|
||||
|
||||
switch (getPost)
|
||||
{
|
||||
case 1:
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_URL, endpoint.c_str());
|
||||
case 0:
|
||||
curl_easy_setopt(curl, CURLOPT_URL, std::string(uri + "?" + sRequest).c_str());
|
||||
break;
|
||||
case 2:
|
||||
std::string getURI = endpoint + "?" + data;
|
||||
curl_easy_setopt(curl, CURLOPT_URL, getURI.c_str());
|
||||
case 1:
|
||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, sRequest.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_URL, uri.c_str());
|
||||
break;
|
||||
// want to do a put, or whatever other type? feel free to add here
|
||||
}
|
||||
@@ -80,53 +120,68 @@ nlohmann::json webAPI::request(const string &endpoint, const string &data, const
|
||||
CURLcode res = curl_easy_perform(curl); // make the request!
|
||||
char *contentType;
|
||||
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); //get status code
|
||||
curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &contentType);
|
||||
|
||||
if (res == CURLE_OK && http_code == 200 && !(readBuffer.empty())) // check it all out and parse
|
||||
{
|
||||
// if we ever use anything other than json we can break the stuff below output
|
||||
// into a separate function...
|
||||
try
|
||||
{
|
||||
response = nlohmann::json::parse(readBuffer);
|
||||
}
|
||||
catch (string &e)
|
||||
{
|
||||
response["message"] = e;
|
||||
response["status"] = "failure";
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
response["message"] = "JSON parse error for endpoint.";
|
||||
response["status"] = "failure";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
response["message"] = "Error fetching data from remote.";
|
||||
response["status"] = "failure";
|
||||
}
|
||||
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &statusCode); //get status code
|
||||
curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &contentType); // get response mime type
|
||||
|
||||
curl_slist_free_all(slist);
|
||||
curl_easy_cleanup(curl); // always wipe our butt
|
||||
}
|
||||
else //default err messages below
|
||||
{
|
||||
response["message"] = "Failed to initialize cURL.";
|
||||
response["status"] = "failure";
|
||||
|
||||
if (res == CURLE_OK && statusCode == 200 && !(readBuffer.empty())) // check it all out and parse
|
||||
{
|
||||
sResponse = readBuffer;
|
||||
|
||||
if (std::string(contentType) == "application/json")
|
||||
{
|
||||
processJSON();
|
||||
}
|
||||
else
|
||||
{
|
||||
responseData.clear();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
response["message"] = "Invalid endpoint URL.";
|
||||
response["status"] = "failure";
|
||||
}
|
||||
|
||||
return response;
|
||||
|
||||
sResponse.clear();
|
||||
responseData.clear();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// This is used by curl to grab the response and put it into a var
|
||||
size_t webAPI::writeCallback(void *contents, size_t size, size_t nmemb, void *userp)
|
||||
{
|
||||
((string*)userp)->append((char*)contents, size * nmemb);
|
||||
((std::string*)userp)->append((char*)contents, size * nmemb);
|
||||
return size * nmemb;
|
||||
}
|
||||
|
||||
bool webAPI::processJSON()
|
||||
{
|
||||
if (!(sResponse.empty())) // check it all out and parse
|
||||
{
|
||||
try
|
||||
{
|
||||
responseData = nlohmann::json::parse(sResponse);
|
||||
return true;
|
||||
}
|
||||
catch (std::string &e)
|
||||
{
|
||||
responseData["message"] = e;
|
||||
responseData["status"] = "failure";
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
responseData["message"] = "JSON parse error for endpoint.";
|
||||
responseData["status"] = "failure";
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
responseData["message"] = "Error fetching data from remote.";
|
||||
responseData["status"] = "failure";
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Vendored
+89
-26
@@ -1,17 +1,19 @@
|
||||
/*
|
||||
This code is just a simple wrapper around nlohmann's wonderful json lib
|
||||
(https://github.com/nlohmann/json) and libcurl. While originally included directly,
|
||||
we have come to realize that we may require web API functionality elsewhere in the future.
|
||||
|
||||
As such, and in an effort to keep the code clean, we've broken it out into this simple little
|
||||
namespace/lib that is easy to include. Just make sure to link against curl when including, and
|
||||
make all the cmake modifications required to properly use it.
|
||||
|
||||
(c) stellabellum/swgilluminati (combined crews), written by DA with help from DC
|
||||
based on the original prototype by parz1val
|
||||
|
||||
License: what's a license? we're a bunch of dirty pirates!
|
||||
*/
|
||||
* Version: 1.3
|
||||
*
|
||||
* This code is just a simple wrapper around nlohmann's wonderful json lib
|
||||
* (https://github.com/nlohmann/json) and libcurl. While originally included directly,
|
||||
* we have come to realize that we may require web API functionality elsewhere in the future.
|
||||
*
|
||||
* As such, and in an effort to keep the code clean, we've broken it out into this simple little
|
||||
* namespace/lib that is easy to include. Just make sure to link against curl when including, and
|
||||
* make all the cmake modifications required to properly use it.
|
||||
*
|
||||
* (c) stellabellum/swgilluminati (combined crews), written by DA with help from DC
|
||||
* based on the original prototype by parz1val
|
||||
*
|
||||
* License: what's a license? we're a bunch of dirty pirates!
|
||||
*/
|
||||
|
||||
#ifndef webAPI_H
|
||||
#define webAPI_H
|
||||
@@ -24,20 +26,81 @@ License: what's a license? we're a bunch of dirty pirates!
|
||||
#include <curl/curl.h>
|
||||
#endif
|
||||
|
||||
namespace webAPI
|
||||
namespace StellaBellum
|
||||
{
|
||||
|
||||
struct statusMessage
|
||||
class webAPI
|
||||
{
|
||||
bool status;
|
||||
std::string retVal;
|
||||
public:
|
||||
// useragent
|
||||
std::string userAgent;
|
||||
|
||||
// constructor - can setup with the endpoint from the start
|
||||
webAPI(std::string endpoint, std::string userAgent = "StellaBellum webAPI");
|
||||
~webAPI();
|
||||
|
||||
// submits the request - respType 0 is json, else raw, get = 0 post = 1
|
||||
bool submit(const int &reqType = 0, const int &getPost = 1, const int &respType = 0);
|
||||
|
||||
// set the endpoint after object creation...or change the target if needed
|
||||
bool setEndpoint(std::string endpoint);
|
||||
|
||||
// get raw response
|
||||
std::string getRaw();
|
||||
|
||||
// set a standard request string
|
||||
bool setData(std::string &data); // all or nothing
|
||||
|
||||
// set json key and value for request
|
||||
template<typename T> bool addJsonData(std::string key, T value)
|
||||
{
|
||||
if (!key.empty())
|
||||
{
|
||||
requestData[key] = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// get json response slot
|
||||
template<typename T> T getRespValue(std::string slot)
|
||||
{
|
||||
if (!responseData.is_null() && !slot.empty() && responseData.count(slot))
|
||||
{
|
||||
return responseData[slot].get<T>();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
// json request data - object is serialized before sending, used with above setter template
|
||||
nlohmann::json requestData;
|
||||
|
||||
// json response, stored so we can use the getter template above
|
||||
nlohmann::json responseData;
|
||||
|
||||
// raw response
|
||||
std::string sResponse;
|
||||
|
||||
// raw request string
|
||||
std::string sRequest;
|
||||
|
||||
// API endpoint
|
||||
std::string uri;
|
||||
|
||||
// fetcher - returns raw response direct from remote - 0 for get, 1 for post, 0 for json mime, 1 for standard
|
||||
bool fetch(const int &getPost = 1, const int &mimeType = 0);
|
||||
|
||||
// cURL writeback callback
|
||||
static size_t writeCallback(void *contents, size_t size, size_t nmemb, void *userp);
|
||||
|
||||
// json processor - string to json
|
||||
bool processJSON();
|
||||
|
||||
protected:
|
||||
// http response code (200, 404, etc)
|
||||
long statusCode;
|
||||
};
|
||||
|
||||
using namespace std;
|
||||
|
||||
statusMessage simplePost(const string &endpoint, const string &data, const string &slotName = "success", const string &messageSlot ="message", const string &statusSlot = "status", const string &statusVal = "success");
|
||||
nlohmann::json request(const string &endpoint, const string &data, const int &reqType = 1); // 1 for post, 0 for get
|
||||
size_t writeCallback(void *contents, size_t size, size_t nmemb, void *userp);
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user