DiscordCoreAPI
A Discord bot library written in C++, with custom asynchronous coroutines.
Loading...
Searching...
No Matches
HttpsClient.hpp
Go to the documentation of this file.
1/*
2 MIT License
3
4 DiscordCoreAPI, A bot library for Discord, written in C++, and featuring explicit multithreading through the usage of custom, asynchronous C++ CoRoutines.
5
6 Copyright 2022, 2023 Chris M. (RealTimeChris)
7
8 Permission is hereby granted, free of charge, to any person obtaining a copy
9 of this software and associated documentation files (the "Software"), to deal
10 in the Software without restriction, including without limitation the rights
11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the Software is
13 furnished to do so, subject to the following conditions:
14
15 The above copyright notice and this permission notice shall be included in all
16 copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 SOFTWARE.
25*/
26/// HttpsClient.hpp - Header file for the "Https stuff".
27/// May 12, 2021
28/// https://discordcoreapi.com
29/// \file HttpsClient.hpp
30#pragma once
31
33
34namespace discord_core_api {
35
36 namespace discord_core_internal {
37
38 /// \brief Voice websocket close codes.
40 public:
41 /// \brief Voice websocket close codes.
42 enum class https_response_codes : uint64_t {
43 Unset = std::numeric_limits<uint64_t>::max(),
44 ok = 200,///< The request completed successfully.
45 created = 201,///< The entity was created successfully.
46 No_Content = 204,///< The request completed successfully but returned no content.
47 Not_Modifies = 304,///< The entity was not modified (no action was taken).
48 Bad_Request = 400,///< The request was improperly formatted, or the server couldn't understand it.
49 unauthorized = 401,///< The authorization header was missing or invalid.
50 forbidden = 403,///< The authorization token you passed did not have permission to the resource.
51 Not_Found = 404,///< The resource at the location specified doesn't exist.
52 Method_Not_Allowed = 405,///< The https method used is not valid for the location specified.
53 Too_Many_Requests = 429,///< You are being rate limited, see rate limits.
54 Gateway_Unavailable = 502,///< There was not a gateway available to process your request. wait a bit and retry.
55 };
56
57 DCA_INLINE static unordered_map<https_response_codes, jsonifier::string> outputErrorValues{
58 { static_cast<https_response_codes>(200), "The request completed successfully" }, { static_cast<https_response_codes>(201), "The entity was created successfully" },
59 { static_cast<https_response_codes>(204), "The request completed successfully but returned no content" },
60 { static_cast<https_response_codes>(304), "The entity was not modified (no action was taken)" },
61 { static_cast<https_response_codes>(400), "The request was improperly formatted, or the server couldn't understand it" },
62 { static_cast<https_response_codes>(401), "The authorization header was missing or invalid" },
63 { static_cast<https_response_codes>(403), "The authorization token you passed did not have permission to the resource" },
64 { static_cast<https_response_codes>(404), "The resource at the location specified doesn't exist" },
65 { static_cast<https_response_codes>(405), "The https method used is not valid for the location specified" },
66 { static_cast<https_response_codes>(429), "you are being rate limited, see rate limits" },
67 { static_cast<https_response_codes>(502), "There was not a gateway available to process your request.wait a bit and retry" },
68 { static_cast<https_response_codes>(500), "The server had an error processing your request(these are rare)" }
69 };
70
72
73 DCA_INLINE https_response_code() = default;
74
75 DCA_INLINE https_response_code& operator=(uint64_t valueNew) {
76 value = static_cast<https_response_codes>(valueNew);
77 return *this;
78 }
79
80 DCA_INLINE https_response_code(uint64_t value) {
81 *this = value;
82 }
83
84 DCA_INLINE operator jsonifier::string() {
85 return jsonifier::string{ "Code: " + jsonifier::toString(static_cast<uint32_t>(value)) + jsonifier::string{ ", message: " } +
86 static_cast<jsonifier::string>(https_response_code::outputErrorValues[value]) };
87 }
88
89 DCA_INLINE operator uint64_t() {
90 return static_cast<uint64_t>(value);
91 }
92 };
93
95 struct rate_limit_data;
96
97 enum class https_state { Collecting_Headers = 0, Collecting_Contents = 1, Collecting_Chunked_Contents = 2, complete = 3 };
98
99 class https_error : public dca_exception {
100 public:
101 https_response_code errorCode{};
102 DCA_INLINE https_error(const jsonifier::string_view& message, std::source_location location = std::source_location::current()) : dca_exception{ message, location } {};
103 };
104
105 struct DiscordCoreAPI_Dll https_response_data {
106 friend class https_rnr_builder;
107 friend class https_connection;
108 friend class https_client;
109
110 https_response_code responseCode{ std::numeric_limits<uint32_t>::max() };
111 unordered_map<jsonifier::string, jsonifier::string> responseHeaders{};
112 https_state currentState{ https_state::Collecting_Headers };
113 jsonifier::string responseData{};
114 uint64_t contentLength{};
115
116 protected:
117 bool isItChunked{};
118 };
119
120 class DiscordCoreAPI_Dll https_rnr_builder {
121 public:
122 friend class https_client;
123
124 https_rnr_builder() = default;
125
126 https_response_data finalizeReturnValues(rate_limit_data& rateLimitData);
127
128 jsonifier::string buildRequest(const https_workload_data& workload);
129
130 void updateRateLimitData(rate_limit_data& rateLimitData);
131
132 bool parseHeaders();
133
134 virtual ~https_rnr_builder() = default;
135
136 protected:
137 bool parseContents();
138
139 bool parseChunk();
140 };
141
142 class DiscordCoreAPI_Dll https_connection : public https_rnr_builder, public tcp_connection<https_connection> {
143 public:
144 template<typename value_type> friend class https_tcp_connection;
145
146 rate_limit_data* currentRateLimitData{};
147 const int32_t maxReconnectTries{ 3 };
148 jsonifier::string inputBufferReal{};
149 jsonifier::string currentBaseUrl{};
150 int32_t currentReconnectTries{};
151 https_workload_data workload{};
152 https_response_data data{};
153
154 https_connection() = default;
155
156 https_connection(const jsonifier::string& baseUrlNew, const uint16_t portNew);
157
158 void resetValues(https_workload_data&& workloadNew, rate_limit_data* newRateLimitData);
159
160 void handleBuffer() override;
161
162 bool areWeConnected();
163
164 void disconnect();
165
166 virtual ~https_connection() = default;
167 };
168
169 /// @class https_connection_manager.
170 /// @brief For managing the collection of Https connections.
171 class DiscordCoreAPI_Dll https_connection_manager {
172 public:
173 friend class https_client;
174
175 https_connection_manager() = default;
176
177 https_connection_manager(rate_limit_queue*);
178
179 https_connection& getConnection(https_workload_type workloadType);
180
181 rate_limit_queue& getRateLimitQueue();
182
183 protected:
184 unordered_map<https_workload_type, unique_ptr<https_connection>> httpsConnections{};///< Collection of Https connections.
185 rate_limit_queue* rateLimitQueue{};
186 std::mutex accessMutex{};
187 };
188
189 class DiscordCoreAPI_Dll https_connection_stack_holder {
190 public:
191 https_connection_stack_holder(https_connection_manager& connectionManager, https_workload_data&& workload);
192
193 https_connection& getConnection();
194
195 ~https_connection_stack_holder();
196
197 protected:
198 rate_limit_queue* rateLimitQueue{};
199 https_connection* connection{};
200 };
201
202 class DiscordCoreAPI_Dll https_client_core {
203 public:
204 https_client_core(jsonifier::string_view botTokenNew);
205
206 DCA_INLINE https_response_data submitWorkloadAndGetResult(https_workload_data&& workloadNew) {
207 https_connection connection{};
208 rate_limit_data rateLimitData{};
209 connection.resetValues(std::move(workloadNew), &rateLimitData);
210 auto returnData = httpsRequestInternal(connection);
211 if (returnData.responseCode != 200 && returnData.responseCode != 204 && returnData.responseCode != 201) {
212 jsonifier::string errorMessage{};
213 if (connection.workload.callStack != "") {
214 errorMessage += connection.workload.callStack + " ";
215 }
216 errorMessage += "Https error: " + returnData.responseCode.operator jsonifier::string() + "\nThe request: base url: " + connection.workload.baseUrl + "\n";
217 if (!connection.workload.relativePath.empty()) {
218 errorMessage += "Relative Url: " + connection.workload.relativePath + "\n";
219 }
220 if (!connection.workload.content.empty()) {
221 errorMessage += "Content: " + connection.workload.content + "\n";
222 }
223 if (!returnData.responseData.empty()) {
224 errorMessage += "The Response: " + static_cast<jsonifier::string>(returnData.responseData);
225 }
226 https_error theError{ errorMessage };
227 theError.errorCode = returnData.responseCode;
228 }
229 return returnData;
230 }
231
232 protected:
233 jsonifier::string botToken{};
234
235 https_response_data httpsRequestInternal(https_connection& connection);
236
237 https_response_data recoverFromError(https_connection& connection);
238
239 https_response_data getResponse(https_connection& connection);
240 };
241
242 /**
243 * \addtogroup discord_core_internal
244 * @{
245 */
246
247 /// @class https_client
248 /// @brief For sending Https requests.
249 class DiscordCoreAPI_Dll https_client : public https_client_core {
250 public:
251 https_client(jsonifier::string_view botTokenNew);
252
253 template<typename value_type, typename string_type> void getParseErrors(jsonifier::jsonifier_core<false>& parser, value_type& value, string_type& stringNew) {
254 parser.parseJson(value, parser.minifyJson(parser.prettifyJson(stringNew)));
255 if (auto result = parser.getErrors(); result.size() > 0) {
256 for (auto& valueNew: result) {
257 message_printer::printError<print_message_type::websocket>(valueNew.reportError());
258 }
259 }
260 }
261
262 template<typename workload_type, typename... args> void submitWorkloadAndGetResult(workload_type&& workload, args&... argsNew) {
263 https_connection_stack_holder stackHolder{ connectionManager, std::move(workload) };
264 https_response_data returnData = httpsRequest(stackHolder.getConnection());
265 if (static_cast<uint32_t>(returnData.responseCode) != 200 && static_cast<uint32_t>(returnData.responseCode) != 204 &&
266 static_cast<uint32_t>(returnData.responseCode) != 201) {
267 jsonifier::string errorMessage{};
268 if (stackHolder.getConnection().workload.callStack != "") {
269 errorMessage += stackHolder.getConnection().workload.callStack + " ";
270 }
271 errorMessage +=
272 "Https error: " + returnData.responseCode.operator jsonifier::string() + "\nThe request: base url: " + stackHolder.getConnection().workload.baseUrl + "\n";
273 if (!stackHolder.getConnection().workload.relativePath.empty()) {
274 errorMessage += "Relative Url: " + stackHolder.getConnection().workload.relativePath + "\n";
275 }
276 if (!stackHolder.getConnection().workload.content.empty()) {
277 errorMessage += "Content: " + stackHolder.getConnection().workload.content + "\n";
278 }
279 if (!returnData.responseData.empty()) {
280 errorMessage += "The Response: " + static_cast<jsonifier::string>(returnData.responseData);
281 }
282 https_error theError{ errorMessage };
283 theError.errorCode = returnData.responseCode;
284 }
285
286 if constexpr ((( !std::is_void_v<args> ) || ...)) {
287 if (returnData.responseData.size() > 0) {
288 (getParseErrors(parser, argsNew, returnData.responseData), ...);
289 }
290 }
291 }
292
293 protected:
294 https_connection_manager connectionManager{};
295 rate_limit_queue rateLimitQueue{};
296
297 https_response_data executeByRateLimitData(https_connection& connection);
298
299 https_response_data httpsRequest(https_connection& connection);
300 };
301
302 /**@}*/
303
304 }// namespace discord_core_internal
305}
For managing the collection of Https connections.
@ No_Content
The request completed successfully but returned no content.
@ Gateway_Unavailable
There was not a gateway available to process your request. wait a bit and retry.
@ forbidden
The authorization token you passed did not have permission to the resource.
@ Not_Found
The resource at the location specified doesn't exist.
@ Bad_Request
The request was improperly formatted, or the server couldn't understand it.
@ Method_Not_Allowed
The https method used is not valid for the location specified.
The main namespace for the forward-facing interfaces.
DCA_INLINE dca_exception(jsonifier::string_view error, std::source_location location=std::source_location::current())
Constructor to create a dca_exception with an error message and optional source location.
Definition Base.hpp:845