remove cpr as a dependency (#21)

abstract away library usage
This commit is contained in:
ouwou
2021-01-23 21:07:03 +00:00
committed by GitHub
parent 547124c94f
commit b28bfd6f20
14 changed files with 451 additions and 251 deletions

View File

@@ -101,7 +101,7 @@ std::set<Snowflake> DiscordClient::GetMessagesForChannel(Snowflake id) const {
void DiscordClient::FetchInvite(std::string code, sigc::slot<void(std::optional<InviteData>)> callback) {
sigc::signal<void, std::optional<InviteData>> signal;
signal.connect(callback);
m_http.MakeGET("/invites/" + code + "?with_counts=true", [this, callback](cpr::Response r) {
m_http.MakeGET("/invites/" + code + "?with_counts=true", [this, callback](http::response_type r) {
if (!CheckCode(r)) {
if (r.status_code == 404)
callback(std::nullopt);
@@ -114,7 +114,7 @@ void DiscordClient::FetchInvite(std::string code, sigc::slot<void(std::optional<
void DiscordClient::FetchMessagesInChannel(Snowflake id, std::function<void(const std::vector<Snowflake> &)> cb) {
std::string path = "/channels/" + std::to_string(id) + "/messages?limit=50";
m_http.MakeGET(path, [this, id, cb](cpr::Response r) {
m_http.MakeGET(path, [this, id, cb](const http::response_type &r) {
if (!CheckCode(r)) return;
std::vector<Message> msgs;
@@ -137,7 +137,7 @@ void DiscordClient::FetchMessagesInChannel(Snowflake id, std::function<void(cons
void DiscordClient::FetchMessagesInChannelBefore(Snowflake channel_id, Snowflake before_id, std::function<void(const std::vector<Snowflake> &)> cb) {
std::string path = "/channels/" + std::to_string(channel_id) + "/messages?limit=50&before=" + std::to_string(before_id);
m_http.MakeGET(path, [this, channel_id, cb](cpr::Response r) {
m_http.MakeGET(path, [this, channel_id, cb](http::response_type r) {
if (!CheckCode(r)) return;
std::vector<Message> msgs;
@@ -453,7 +453,7 @@ void DiscordClient::SetGuildName(Snowflake id, const Glib::ustring &name, sigc::
obj.Name = name;
sigc::signal<void, bool> signal;
signal.connect(callback);
m_http.MakePATCH("/guilds/" + std::to_string(id), nlohmann::json(obj).dump(), [this, signal](const cpr::Response &r) {
m_http.MakePATCH("/guilds/" + std::to_string(id), nlohmann::json(obj).dump(), [this, signal](const http::response_type &r) {
const auto success = r.status_code == 200;
signal.emit(success);
});
@@ -468,7 +468,7 @@ void DiscordClient::SetGuildIcon(Snowflake id, const std::string &data, sigc::sl
obj.IconData = data;
sigc::signal<void, bool> signal;
signal.connect(callback);
m_http.MakePATCH("/guilds/" + std::to_string(id), nlohmann::json(obj).dump(), [this, signal](const cpr::Response &r) {
m_http.MakePATCH("/guilds/" + std::to_string(id), nlohmann::json(obj).dump(), [this, signal](const http::response_type &r) {
const auto success = r.status_code == 200;
signal.emit(success);
});
@@ -481,7 +481,7 @@ void DiscordClient::UnbanUser(Snowflake guild_id, Snowflake user_id) {
void DiscordClient::UnbanUser(Snowflake guild_id, Snowflake user_id, sigc::slot<void(bool success)> callback) {
sigc::signal<void, bool> signal;
signal.connect(callback);
m_http.MakeDELETE("/guilds/" + std::to_string(guild_id) + "/bans/" + std::to_string(user_id), [this, callback](const cpr::Response &response) {
m_http.MakeDELETE("/guilds/" + std::to_string(guild_id) + "/bans/" + std::to_string(user_id), [this, callback](const http::response_type &response) {
callback(response.status_code == 204);
});
}
@@ -493,7 +493,7 @@ void DiscordClient::DeleteInvite(const std::string &code) {
void DiscordClient::DeleteInvite(const std::string &code, sigc::slot<void(bool success)> callback) {
sigc::signal<void, bool> signal;
signal.connect(callback);
m_http.MakeDELETE("/invites/" + code, [this, callback](const cpr::Response &response) {
m_http.MakeDELETE("/invites/" + code, [this, callback](const http::response_type &response) {
callback(CheckCode(response));
});
}
@@ -505,7 +505,7 @@ std::vector<BanData> DiscordClient::GetBansInGuild(Snowflake guild_id) {
void DiscordClient::FetchGuildBan(Snowflake guild_id, Snowflake user_id, sigc::slot<void(BanData)> callback) {
sigc::signal<void, BanData> signal;
signal.connect(callback);
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/bans/" + std::to_string(user_id), [this, callback, guild_id](const cpr::Response &response) {
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/bans/" + std::to_string(user_id), [this, callback, guild_id](const http::response_type &response) {
if (!CheckCode(response)) return;
auto ban = nlohmann::json::parse(response.text).get<BanData>();
m_store.SetBan(guild_id, ban.User.ID, ban);
@@ -517,7 +517,7 @@ void DiscordClient::FetchGuildBan(Snowflake guild_id, Snowflake user_id, sigc::s
void DiscordClient::FetchGuildBans(Snowflake guild_id, sigc::slot<void(std::vector<BanData>)> callback) {
sigc::signal<void, std::vector<BanData>> signal;
signal.connect(callback);
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/bans", [this, callback, guild_id](const cpr::Response &response) {
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/bans", [this, callback, guild_id](const http::response_type &response) {
if (!CheckCode(response)) return;
auto bans = nlohmann::json::parse(response.text).get<std::vector<BanData>>();
m_store.BeginTransaction();
@@ -533,7 +533,7 @@ void DiscordClient::FetchGuildBans(Snowflake guild_id, sigc::slot<void(std::vect
void DiscordClient::FetchGuildInvites(Snowflake guild_id, sigc::slot<void(std::vector<InviteData>)> callback) {
sigc::signal<void, std::vector<InviteData>> signal;
signal.connect(callback);
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/invites", [this, callback, guild_id](const cpr::Response &response) {
m_http.MakeGET("/guilds/" + std::to_string(guild_id) + "/invites", [this, callback, guild_id](const http::response_type &response) {
// store?
if (!CheckCode(response)) return;
auto invites = nlohmann::json::parse(response.text).get<std::vector<InviteData>>();
@@ -1232,9 +1232,9 @@ void DiscordClient::HandleSocketOpen() {
void DiscordClient::HandleSocketClose(uint16_t code) {
}
bool DiscordClient::CheckCode(const cpr::Response &r) {
bool DiscordClient::CheckCode(const http::response_type &r) {
if (r.status_code >= 300 || r.error) {
fprintf(stderr, "api request to %s failed with status code %d\n", r.url.c_str(), r.status_code);
fprintf(stderr, "api request to %s failed with status code %d: %s\n", r.url.c_str(), r.status_code, r.error_string.c_str());
return false;
}

View File

@@ -1,6 +1,6 @@
#pragma once
#include "websocket.hpp"
#include "http.hpp"
#include "httpclient.hpp"
#include "objects.hpp"
#include "store.hpp"
#include <sigc++/sigc++.h>
@@ -175,7 +175,7 @@ private:
void HandleSocketOpen();
void HandleSocketClose(uint16_t code);
bool CheckCode(const cpr::Response &r);
bool CheckCode(const http::response_type &r);
void StoreMessageData(Message &msg);

View File

@@ -1,156 +0,0 @@
#include "http.hpp"
//#define USE_LOCAL_PROXY
HTTPClient::HTTPClient(std::string api_base)
: m_api_base(api_base) {
m_dispatcher.connect(sigc::mem_fun(*this, &HTTPClient::RunCallbacks));
}
void HTTPClient::SetUserAgent(std::string agent) {
m_agent = agent;
}
void HTTPClient::SetAuth(std::string auth) {
m_authorization = auth;
}
void HTTPClient::MakeDELETE(std::string path, std::function<void(cpr::Response r)> cb) {
printf("DELETE %s\n", path.c_str());
auto url = cpr::Url { m_api_base + path };
auto headers = cpr::Header {
{ "Authorization", m_authorization },
};
auto ua = cpr::UserAgent {m_agent != "" ? m_agent : "Abaddon" };
#ifdef USE_LOCAL_PROXY
m_futures.push_back(cpr::DeleteCallback(
std::bind(&HTTPClient::OnResponse, this, std::placeholders::_1, cb),
url, headers, ua,
cpr::Proxies { { "http", "127.0.0.1:8888" }, { "https", "127.0.0.1:8888" } },
cpr::VerifySsl { false }));
#else
m_futures.push_back(cpr::DeleteCallback(
std::bind(&HTTPClient::OnResponse, this, std::placeholders::_1, cb),
url, headers, ua));
#endif
}
void HTTPClient::MakePATCH(std::string path, std::string payload, std::function<void(cpr::Response r)> cb) {
printf("PATCH %s\n", path.c_str());
auto url = cpr::Url { m_api_base + path };
auto headers = cpr::Header {
{ "Authorization", m_authorization },
{ "Content-Type", "application/json" },
};
auto ua = cpr::UserAgent { m_agent != "" ? m_agent : "Abaddon" };
auto body = cpr::Body { payload };
#ifdef USE_LOCAL_PROXY
m_futures.push_back(cpr::PatchCallback(
std::bind(&HTTPClient::OnResponse, this, std::placeholders::_1, cb),
url, headers, body, ua,
cpr::Proxies { { "http", "127.0.0.1:8888" }, { "https", "127.0.0.1:8888" } },
cpr::VerifySsl { false }));
#else
m_futures.push_back(cpr::PatchCallback(
std::bind(&HTTPClient::OnResponse, this, std::placeholders::_1, cb),
url, headers, body, ua));
#endif
}
void HTTPClient::MakePOST(std::string path, std::string payload, std::function<void(cpr::Response r)> cb) {
printf("POST %s\n", path.c_str());
auto url = cpr::Url { m_api_base + path };
auto headers = cpr::Header {
{ "Authorization", m_authorization },
{ "Content-Type", "application/json" },
};
auto ua = cpr::UserAgent { m_agent != "" ? m_agent : "Abaddon" };
auto body = cpr::Body { payload };
#ifdef USE_LOCAL_PROXY
m_futures.push_back(cpr::PostCallback(
std::bind(&HTTPClient::OnResponse, this, std::placeholders::_1, cb),
url, headers, body, ua,
cpr::Proxies { { "http", "127.0.0.1:8888" }, { "https", "127.0.0.1:8888" } },
cpr::VerifySsl { false }));
#else
m_futures.push_back(cpr::PostCallback(
std::bind(&HTTPClient::OnResponse, this, std::placeholders::_1, cb),
url, headers, body, ua));
#endif
}
void HTTPClient::MakePUT(std::string path, std::string payload, std::function<void(cpr::Response r)> cb) {
printf("PUT %s\n", path.c_str());
auto url = cpr::Url { m_api_base + path };
auto headers = cpr::Header {
{ "Authorization", m_authorization },
{ "Content-Type", "application/json" },
};
auto ua = cpr::UserAgent { m_agent != "" ? m_agent : "Abaddon" };
auto body = cpr::Body { payload };
#ifdef USE_LOCAL_PROXY
m_futures.push_back(cpr::PutCallback(
std::bind(&HTTPClient::OnResponse, this, std::placeholders::_1, cb),
url, headers, body, ua,
cpr::Proxies { { "http", "127.0.0.1:8888" }, { "https", "127.0.0.1:8888" } },
cpr::VerifySsl { false }));
#else
m_futures.push_back(cpr::PutCallback(
std::bind(&HTTPClient::OnResponse, this, std::placeholders::_1, cb),
url, headers, body, ua));
#endif
}
void HTTPClient::MakeGET(std::string path, std::function<void(cpr::Response r)> cb) {
printf("GET %s\n", path.c_str());
auto url = cpr::Url { m_api_base + path };
auto headers = cpr::Header {
{ "Authorization", m_authorization },
{ "Content-Type", "application/json" },
};
auto ua = cpr::UserAgent { m_agent != "" ? m_agent : "Abaddon" };
auto x = cpr::UserAgent {};
#ifdef USE_LOCAL_PROXY
m_futures.push_back(cpr::GetCallback(
std::bind(&HTTPClient::OnResponse, this, std::placeholders::_1, cb),
url, headers, ua,
cpr::Proxies { { "http", "127.0.0.1:8888" }, { "https", "127.0.0.1:8888" } },
cpr::VerifySsl { false }));
#else
m_futures.push_back(cpr::GetCallback(
std::bind(&HTTPClient::OnResponse, this, std::placeholders::_1, cb),
url, headers, ua));
#endif
}
void HTTPClient::CleanupFutures() {
for (auto it = m_futures.begin(); it != m_futures.end();) {
if (it->wait_for(std::chrono::seconds(0)) == std::future_status::ready)
it = m_futures.erase(it);
else
it++;
}
}
void HTTPClient::RunCallbacks() {
m_mutex.lock();
m_queue.front()();
m_queue.pop();
m_mutex.unlock();
}
void HTTPClient::OnResponse(cpr::Response r, std::function<void(cpr::Response r)> cb) {
CleanupFutures();
try {
m_mutex.lock();
m_queue.push([this, r, cb] { cb(r); });
m_dispatcher.emit();
m_mutex.unlock();
} catch (std::exception &e) {
fprintf(stderr, "error handling response (%s, code %d): %s\n", r.url.c_str(), r.status_code, e.what());
}
}

View File

@@ -1,37 +0,0 @@
#pragma once
#include <cpr/cpr.h>
#include <functional>
#include <future>
#include <string>
#include <unordered_map>
#include <memory>
#include <mutex>
#include <queue>
#include <glibmm.h>
class HTTPClient {
public:
HTTPClient(std::string api_base);
void SetUserAgent(std::string agent);
void SetAuth(std::string auth);
void MakeDELETE(std::string path, std::function<void(cpr::Response r)> cb);
void MakeGET(std::string path, std::function<void(cpr::Response r)> cb);
void MakePATCH(std::string path, std::string payload, std::function<void(cpr::Response r)> cb);
void MakePOST(std::string path, std::string payload, std::function<void(cpr::Response r)> cb);
void MakePUT(std::string path, std::string payload, std::function<void(cpr::Response r)> cb);
private:
void OnResponse(cpr::Response r, std::function<void(cpr::Response r)> cb);
void CleanupFutures();
mutable std::mutex m_mutex;
Glib::Dispatcher m_dispatcher;
std::queue<std::function<void()>> m_queue;
void RunCallbacks();
std::vector<std::future<void>> m_futures;
std::string m_api_base;
std::string m_authorization;
std::string m_agent;
};

136
discord/httpclient.cpp Normal file
View File

@@ -0,0 +1,136 @@
#include "httpclient.hpp"
#include "httpclient.hpp"
//#define USE_LOCAL_PROXY
HTTPClient::HTTPClient(std::string api_base)
: m_api_base(api_base) {
m_dispatcher.connect(sigc::mem_fun(*this, &HTTPClient::RunCallbacks));
}
void HTTPClient::SetUserAgent(std::string agent) {
m_agent = agent;
}
void HTTPClient::SetAuth(std::string auth) {
m_authorization = auth;
}
void HTTPClient::MakeDELETE(const std::string &path, std::function<void(http::response_type r)> cb) {
printf("DELETE %s\n", path.c_str());
m_futures.push_back(std::async(std::launch::async, [this, path, cb] {
http::request req(http::REQUEST_DELETE, m_api_base + path);
req.set_header("Authorization", m_authorization);
req.set_user_agent(m_agent != "" ? m_agent : "Abaddon");
#ifdef USE_LOCAL_PROXY
req.set_proxy("http://127.0.0.1:8888");
req.set_verify_ssl(false);
#endif
auto res = req.execute();
OnResponse(res, cb);
}));
}
void HTTPClient::MakePATCH(const std::string &path, const std::string &payload, std::function<void(http::response_type r)> cb) {
printf("PATCH %s\n", path.c_str());
m_futures.push_back(std::async(std::launch::async, [this, path, cb, payload] {
http::request req(http::REQUEST_PATCH, m_api_base + path);
req.set_header("Authorization", m_authorization);
req.set_header("Content-Type", "application/json");
req.set_user_agent(m_agent != "" ? m_agent : "Abaddon");
req.set_body(payload);
#ifdef USE_LOCAL_PROXY
req.set_proxy("http://127.0.0.1:8888");
req.set_verify_ssl(false);
#endif
auto res = req.execute();
OnResponse(res, cb);
}));
}
void HTTPClient::MakePOST(const std::string &path, const std::string &payload, std::function<void(http::response_type r)> cb) {
printf("POST %s\n", path.c_str());
m_futures.push_back(std::async(std::launch::async, [this, path, cb, payload] {
http::request req(http::REQUEST_POST, m_api_base + path);
req.set_header("Authorization", m_authorization);
req.set_header("Content-Type", "application/json");
req.set_user_agent(m_agent != "" ? m_agent : "Abaddon");
req.set_body(payload);
#ifdef USE_LOCAL_PROXY
req.set_proxy("http://127.0.0.1:8888");
req.set_verify_ssl(false);
#endif
auto res = req.execute();
OnResponse(res, cb);
}));
}
void HTTPClient::MakePUT(const std::string &path, const std::string &payload, std::function<void(http::response_type r)> cb) {
printf("PUT %s\n", path.c_str());
m_futures.push_back(std::async(std::launch::async, [this, path, cb, payload] {
http::request req(http::REQUEST_PUT, m_api_base + path);
req.set_header("Authorization", m_authorization);
req.set_header("Content-Type", "application/json");
req.set_user_agent(m_agent != "" ? m_agent : "Abaddon");
req.set_body(payload);
#ifdef USE_LOCAL_PROXY
req.set_proxy("http://127.0.0.1:8888");
req.set_verify_ssl(false);
#endif
auto res = req.execute();
OnResponse(res, cb);
}));
}
void HTTPClient::MakeGET(const std::string &path, std::function<void(http::response_type r)> cb) {
printf("GET %s\n", path.c_str());
m_futures.push_back(std::async(std::launch::async, [this, path, cb] {
http::request req(http::REQUEST_GET, m_api_base + path);
req.set_header("Authorization", m_authorization);
req.set_header("Content-Type", "application/json");
req.set_user_agent(m_agent != "" ? m_agent : "Abaddon");
#ifdef USE_LOCAL_PROXY
req.set_proxy("http://127.0.0.1:8888");
req.set_verify_ssl(false);
#endif
auto res = req.execute();
OnResponse(res, cb);
}));
}
void HTTPClient::CleanupFutures() {
for (auto it = m_futures.begin(); it != m_futures.end();) {
if (it->wait_for(std::chrono::seconds(0)) == std::future_status::ready)
it = m_futures.erase(it);
else
it++;
}
}
void HTTPClient::RunCallbacks() {
m_mutex.lock();
m_queue.front()();
m_queue.pop();
m_mutex.unlock();
}
void HTTPClient::OnResponse(const http::response_type &r, std::function<void(http::response_type r)> cb) {
CleanupFutures();
try {
m_mutex.lock();
m_queue.push([this, r, cb] { cb(r); });
m_dispatcher.emit();
m_mutex.unlock();
} catch (const std::exception &e) {
fprintf(stderr, "error handling response (%s, code %d): %s\n", r.url.c_str(), r.status_code, e.what());
}
}

37
discord/httpclient.hpp Normal file
View File

@@ -0,0 +1,37 @@
#pragma once
#include <functional>
#include <future>
#include <string>
#include <unordered_map>
#include <memory>
#include <mutex>
#include <queue>
#include <glibmm.h>
#include "../http.hpp"
class HTTPClient {
public:
HTTPClient(std::string api_base);
void SetUserAgent(std::string agent);
void SetAuth(std::string auth);
void MakeDELETE(const std::string &path, std::function<void(http::response_type r)> cb);
void MakeGET(const std::string &path, std::function<void(http::response_type r)> cb);
void MakePATCH(const std::string &path, const std::string &payload, std::function<void(http::response_type r)> cb);
void MakePOST(const std::string &path, const std::string &payload, std::function<void(http::response_type r)> cb);
void MakePUT(const std::string &path, const std::string &payload, std::function<void(http::response_type r)> cb);
private:
void OnResponse(const http::response_type &r, std::function<void(http::response_type r)> cb);
void CleanupFutures();
mutable std::mutex m_mutex;
Glib::Dispatcher m_dispatcher;
std::queue<std::function<void()>> m_queue;
void RunCallbacks();
std::vector<std::future<void>> m_futures;
std::string m_api_base;
std::string m_authorization;
std::string m_agent;
};