-
-
Notifications
You must be signed in to change notification settings - Fork 170
/
Copy pathhttpsclient.h
357 lines (305 loc) · 9.42 KB
/
httpsclient.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
/************************************************************************************
*
* D++, A Lightweight C++ library for Discord
*
* SPDX-License-Identifier: Apache-2.0
* Copyright 2021 Craig Edwards and D++ contributors
* (https://github.com/brainboxdotcc/DPP/graphs/contributors)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
************************************************************************************/
#pragma once
#include <dpp/export.h>
#include <string>
#include <map>
#include <list>
#include <vector>
#include <variant>
#include <dpp/sslconnection.h>
#include <dpp/version.h>
#include <dpp/stringops.h>
namespace dpp {
static inline const std::string http_version = "DiscordBot (https://github.com/brainboxdotcc/DPP, "
+ to_hex(DPP_VERSION_MAJOR, false) + "." + to_hex(DPP_VERSION_MINOR, false) + "." + to_hex(DPP_VERSION_PATCH, false) + ")";
static inline constexpr const char* DISCORD_HOST = "https://discord.com";
/**
* @brief HTTP connection status
*/
enum http_state : uint8_t {
/**
* @brief Sending/receiving HTTP headers and request body
*/
HTTPS_HEADERS,
/**
* @brief Receiving body content.
*/
HTTPS_CONTENT,
/**
* @brief Completed connection, as it was closed or the body is >= Content-Length
*
*/
HTTPS_DONE,
/**
* @brief Received chunk length
*
*/
HTTPS_CHUNK_LEN,
/**
* @brief Received chunk trailing CRLF
*/
HTTPS_CHUNK_TRAILER,
/**
* @brief The last received chunk is the final chunk
*/
HTTPS_CHUNK_LAST,
/**
* @brief Receiving contents of a chunk
*/
HTTPS_CHUNK_CONTENT
};
/**
* @brief Request headers
*/
typedef std::multimap<std::string, std::string> http_headers;
/**
* @brief Represents a multipart mime body and the correct top-level mime type
* If a non-multipart request is passed in, this is represented as a plain body
* and the application/json mime type.
*/
struct multipart_content {
/**
* @brief Multipart body
*/
std::string body;
/**
* @brief MIME type
*/
std::string mimetype;
};
/**
* @brief Represents a HTTP scheme, hostname and port
* split into parts for easy use in https_client.
*/
struct http_connect_info {
/**
* @brief True if the connection should be SSL
*/
bool is_ssl{};
/**
* @brief The request scheme, e.g. 'https' or 'http'
*/
std::string scheme;
/**
* @brief The request hostname part, e.g. 'discord.com'
*/
std::string hostname;
/**
* @brief The port number, either determined from the scheme,
* or from the part of the hostname after a colon ":" character
*/
uint16_t port{};
};
using https_client_completion_event = std::function<void(class https_client*)>;
/**
* @brief Implements a HTTPS socket client based on the SSL client.
* @note plaintext HTTP without SSL is also supported via a "downgrade" setting
*/
class DPP_EXPORT https_client : public ssl_connection {
/**
* @brief The type of the request, e.g. GET, POST
*/
std::string request_type;
/**
* @brief Path part of URL for HTTPS connection
*/
std::string path;
/**
* @brief The request body, e.g. form data
*/
std::string request_body;
/**
* @brief The response body, e.g. file content or JSON
*/
std::string body;
/**
* @brief The reported length of the content. If this is
* UULONG_MAX, then no length was reported by the server.
*/
uint64_t content_length;
/**
* @brief Headers for the request, e.g. Authorization, etc.
*/
http_headers request_headers;
/**
* @brief The status of the HTTP request from the server,
* e.g. 200 for OK, 404 for not found. A value of 0 means
* no request has been completed.
*/
uint16_t status;
/**
* @brief The HTTP protocol to use
*/
std::string http_protocol;
/**
* @brief Time at which the request should be abandoned
*/
time_t timeout;
/**
* @brief If true the content is chunked encoding
*/
bool chunked;
/**
* @brief Size of current chunk
*/
size_t chunk_size;
/**
* @brief Number of bytes received in current chunk
*/
size_t chunk_receive;
/**
* @brief Headers from the server's response, e.g. RateLimit
* headers, cookies, etc.
*/
std::multimap<std::string, std::string> response_headers;
protected:
/**
* @brief Start the connection
*/
virtual void connect() override;
/**
* @brief Get request state
* @return request state
*/
http_state get_state();
public:
/**
* @brief If true the response timed out while waiting
*/
bool timed_out;
/**
* @brief Function to call when HTTP request is completed
*/
https_client_completion_event completed;
/**
* @brief Current connection state
*/
http_state state;
/**
* @brief Connect to a specific HTTP(S) server and complete a request.
*
* The constructor will attempt the connection, and return the content.
* By the time the constructor completes, the HTTP request will be stored
* in the object.
*
* @note This is a blocking call. It starts a loop which runs non-blocking
* functions within it, but does not return until the request completes.
* See queues.cpp for how to make this asynchronous.
*
* @param hostname Hostname to connect to
* @param port Port number to connect to, usually 443 for SSL and 80 for plaintext
* @param urlpath path part of URL, e.g. "/api"
* @param verb Request verb, e.g. GET or POST
* @param req_body Request body, use dpp::https_client::build_multipart() to build a multipart MIME body (e.g. for multiple file upload)
* @param extra_headers Additional request headers, e.g. user-agent, authorization, etc
* @param plaintext_connection Set to true to make the connection plaintext (turns off SSL)
* @param request_timeout How many seconds before the connection is considered failed if not finished
* @param protocol Request HTTP protocol (default: 1.1)
* @param done Function to call when https_client request is completed
*/
https_client(cluster* creator, const std::string &hostname, uint16_t port = 443, const std::string &urlpath = "/", const std::string &verb = "GET", const std::string &req_body = "", const http_headers& extra_headers = {}, bool plaintext_connection = false, uint16_t request_timeout = 5, const std::string &protocol = "1.1", https_client_completion_event done = {});
/**
* @brief Destroy the https client object
*/
virtual ~https_client() override;
/**
* @brief Build a multipart content from a set of files and some json
*
* @param json The json content
* @param filenames File names of files to send
* @param contents Contents of each of the files to send
* @param mimetypes MIME types of each of the files to send
* @return multipart mime content and headers
*/
static multipart_content build_multipart(const std::string &json, const std::vector<std::string>& filenames = {}, const std::vector<std::string>& contents = {}, const std::vector<std::string>& mimetypes = {});
/**
* @brief Processes incoming data from the SSL socket input buffer.
*
* @param buffer The buffer contents. Can modify this value removing the head elements when processed.
*/
virtual bool handle_buffer(std::string &buffer) override;
/**
* @brief Close HTTPS socket
*/
virtual void close() override;
/**
* @brief Fires every second from the underlying socket I/O loop, used for timeouts
*/
virtual void one_second_timer() override;
/**
* @brief Get a HTTP response header
*
* @param header_name Header name to find, case insensitive
* @return Header content or empty string if not found.
* If multiple values have the same header_name, this will return one of them.
* @see get_header_count to determine if multiple are present
* @see get_header_list to retrieve all entries of the same header_name
*/
const std::string get_header(std::string header_name) const;
/**
* @brief Get the number of headers with the same header name
*
* @param header_name
* @return the number of headers with this count
*/
size_t get_header_count(std::string header_name) const;
/**
* @brief Get a set of HTTP response headers with a common name
*
* @param header_name
* @return A list of headers with the same name, or an empty list if not found
*/
const std::list<std::string> get_header_list(std::string header_name) const;
/**
* @brief Get all HTTP response headers
*
* @return headers as a map
*/
const std::multimap<std::string, std::string> get_headers() const;
/**
* @brief Get the response content
*
* @return response content
*/
const std::string get_content() const;
/**
* @brief Get the response HTTP status, e.g.
* 200 for OK, 404 for not found, 429 for rate limited.
* A value of 0 indicates the request was not completed.
*
* @return uint16_t HTTP status
*/
uint16_t get_status() const;
/**
* @brief Break down a scheme, hostname and port into
* a http_connect_info.
*
* All but the hostname portion are optional. The path component
* should not be passed to this function.
*
* @param url URL to break down
* @return Split URL
*/
static http_connect_info get_host_info(std::string url);
};
}