31namespace DiscordCoreInternal {
33 class DiscordCoreAPI_Dll HttpsConnectionManager;
34 struct DiscordCoreAPI_Dll RateLimitData;
36 enum class HttpsState { Collecting_Code = 0, Collecting_Headers = 1, Collecting_Size = 2, Collecting_Contents = 3 };
38 class DiscordCoreAPI_Dll HttpsError :
public DiscordCoreAPI::DCAException {
41 explicit HttpsError(std::string message);
44 struct DiscordCoreAPI_Dll HttpsResponseData {
45 friend class HttpsRnRBuilder;
46 friend class HttpsConnection;
47 friend class HttpsClient;
49 std::unordered_map<std::string, std::string> responseHeaders{};
50 HttpsResponseCode responseCode{
static_cast<uint32_t
>(-1) };
51 HttpsState currentState{ HttpsState::Collecting_Code };
52 std::string responseData{};
53 uint64_t contentLength{};
59 class DiscordCoreAPI_Dll HttpsRnRBuilder {
61 friend class HttpsClient;
63 HttpsRnRBuilder(
bool doWePrintErrorMessages);
65 void updateRateLimitData(RateLimitData& connection, std::unordered_map<std::string, std::string>& headers);
67 HttpsResponseData finalizeReturnValues(RateLimitData& rateLimitData);
69 std::string buildRequest(
const HttpsWorkloadData& workload);
71 uint64_t parseHeaders(StringBuffer& other);
73 bool parseChunk(StringBuffer& other);
75 virtual ~HttpsRnRBuilder() noexcept = default;
78 bool doWePrintErrorMessages{};
79 bool doWeHaveContentSize{};
80 bool doWeHaveHeaders{};
83 uint64_t parseSize(StringBuffer& other);
85 uint64_t parseCode(StringBuffer& other);
87 void clearCRLF(StringBuffer& other);
90 struct DiscordCoreAPI_Dll RateLimitData {
91 friend class HttpsConnectionManager;
92 friend class HttpsRnRBuilder;
93 friend class HttpsClient;
96 std::atomic<Milliseconds> sampledTimeInMs{ Milliseconds{} };
97 std::atomic<Milliseconds> msRemain{ Milliseconds{} };
98 std::counting_semaphore<1> theSemaphore{ 1 };
99 std::atomic_bool areWeASpecialBucket{};
100 std::atomic_bool didWeHitRateLimit{};
101 std::atomic_int64_t getsRemaining{};
102 std::atomic_bool haveWeGoneYet{};
103 std::atomic_bool doWeWait{};
104 std::string tempBucket{};
105 std::string bucket{};
108 class DiscordCoreAPI_Dll HttpsConnection :
public TCPSSLClient,
public HttpsRnRBuilder {
110 const int32_t maxReconnectTries{ 10 };
111 std::atomic_bool areWeCheckedOut{};
112 int32_t currentReconnectTries{};
113 StringBuffer inputBufferReal{};
114 std::string currentBaseUrl{};
115 Jsonifier::Parser parser{};
116 bool areWeDoneTheRequest{};
117 HttpsResponseData data{};
118 bool doWeConnect{
true };
120 HttpsConnection(
bool doWePrintErrorMessages);
122 void handleBuffer() noexcept;
124 void disconnect() noexcept;
128 virtual ~HttpsConnection() noexcept = default;
131 class DiscordCoreAPI_Dll HttpsConnectionManager {
133 HttpsConnectionManager(DiscordCoreAPI::ConfigManager*);
135 std::unordered_map<std::string, std::unique_ptr<RateLimitData>>& getRateLimitValues();
137 std::unordered_map<HttpsWorkloadType, std::string>& getRateLimitValueBuckets();
139 HttpsConnection* getConnection();
144 std::unordered_map<std::string, std::unique_ptr<RateLimitData>> rateLimitValues{};
145 std::unordered_map<int64_t, std::unique_ptr<HttpsConnection>> httpsConnections{};
146 std::unordered_map<HttpsWorkloadType, std::string> rateLimitValueBuckets{};
147 DiscordCoreAPI::ConfigManager* configManager{
nullptr };
148 std::mutex accessMutex{};
149 int64_t currentIndex{};
152 template<
typename OTy>
153 concept SameAsVoid = std::same_as<void, OTy>;
155 class DiscordCoreAPI_Dll HttpsClient {
157 HttpsClient(DiscordCoreAPI::ConfigManager* configManager);
159 template<
typename RTy> RTy submitWorkloadAndGetResult(
const HttpsWorkloadData& workload, RTy* returnValue) {
160 workload.headersToInsert[
"Authorization"] =
"Bot " + this->configManager->getBotToken();
161 workload.headersToInsert[
"User-Agent"] =
"DiscordBot (https://discordcoreapi.com/ 1.0)";
162 if (workload.payloadType == PayloadType::Application_Json) {
163 workload.headersToInsert[
"Content-Type"] =
"application/json";
164 }
else if (workload.payloadType == PayloadType::Multipart_Form) {
165 workload.headersToInsert[
"Content-Type"] =
"multipart/form-data; boundary=boundary25";
167 auto httpsConnection = this->connectionManager.getConnection();
168 HttpsResponseData returnData = this->httpsRequest(httpsConnection, workload);
170 if (
static_cast<uint32_t
>(returnData.responseCode) != 200 &&
static_cast<uint32_t
>(returnData.responseCode) != 204 &&
171 static_cast<uint32_t
>(returnData.responseCode) != 201) {
172 HttpsError theError{ DiscordCoreAPI::shiftToBrightRed() + workload.callStack +
173 " Https Error: " +
static_cast<std::string
>(returnData.responseCode) +
"\nThe Request: " + workload.content +
174 DiscordCoreAPI::reset() +
"" };
175 theError.errorCode = returnData.responseCode;
176 httpsConnection->areWeCheckedOut.store(
false);
179 if (returnData.responseData.size() > 0) {
180 Jsonifier::Value document{};
181 returnData.responseData.reserve(returnData.responseData.size() + 256);
182 if (httpsConnection->parser.parseJson(returnData.responseData).get(document) == Jsonifier::Success) {
184 *returnValue = RTy{ document };
185 httpsConnection->areWeCheckedOut.store(
false);
188 httpsConnection->areWeCheckedOut.store(
false);
189 return RTy{ document };
193 httpsConnection->areWeCheckedOut.store(
false);
197 template<SameAsVo
id RTy> RTy submitWorkloadAndGetResult(
const HttpsWorkloadData& workload, RTy* returnValue =
nullptr);
199 HttpsResponseData submitWorkloadAndGetResult(
const HttpsWorkloadData& workloadNew);
201 HttpsResponseData httpsRequest(HttpsConnection* httpsConnection,
const HttpsWorkloadData& workload);
204 DiscordCoreAPI::ConfigManager* configManager{
nullptr };
205 HttpsConnectionManager connectionManager{
nullptr };
207 HttpsResponseData httpsRequestInternal(HttpsConnection* connection,
const HttpsWorkloadData& workload,
208 RateLimitData& rateLimitData);
210 HttpsResponseData executeByRateLimitData(HttpsConnection* httpsConnection,
const HttpsWorkloadData& workload,
211 RateLimitData& rateLimitData);
213 HttpsResponseData getResponse(HttpsConnection* connection, RateLimitData& rateLimitData);