Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add request_timeout, a parameter as optional to cluster::request() which allows user to set http_client::timeout and add error message for request timeouts #1186

Merged
merged 3 commits into from
Jul 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions buildtools/classes/Generator/CoroGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ public function getCommentArray(): array
*/
public function saveHeader(string $content): void
{
$content .= "[[nodiscard]] async<http_request_completion_t> co_request(const std::string &url, http_method method, const std::string &postdata = \"\", const std::string &mimetype = \"text/plain\", const std::multimap<std::string, std::string> &headers = {}, const std::string &protocol = \"1.1\");\n\n";
$content .= "[[nodiscard]] async<http_request_completion_t> co_request(const std::string &url, http_method method, const std::string &postdata = \"\", const std::string &mimetype = \"text/plain\", const std::multimap<std::string, std::string> &headers = {}, const std::string &protocol = \"1.1\", time_t request_timeout = 5);\n\n";
file_put_contents('include/dpp/cluster_coro_calls.h', $content);
}

Expand All @@ -125,7 +125,7 @@ public function saveHeader(string $content): void
*/
public function saveCpp(string $cppcontent): void
{
$cppcontent .= "dpp::async<dpp::http_request_completion_t> dpp::cluster::co_request(const std::string &url, http_method method, const std::string &postdata, const std::string &mimetype, const std::multimap<std::string, std::string> &headers, const std::string &protocol) {\n\treturn async<http_request_completion_t>{ [&, this] <typename C> (C &&cc) { return this->request(url, method, std::forward<C>(cc), postdata, mimetype, headers, protocol); }};\n}
$cppcontent .= "dpp::async<dpp::http_request_completion_t> dpp::cluster::co_request(const std::string &url, http_method method, const std::string &postdata, const std::string &mimetype, const std::multimap<std::string, std::string> &headers, const std::string &protocol, time_t request_timeout) {\n\treturn async<http_request_completion_t>{ [&, this] <typename C> (C &&cc) { return this->request(url, method, std::forward<C>(cc), postdata, mimetype, headers, protocol, request_timeout); }};\n}

#endif
";
Expand Down
3 changes: 2 additions & 1 deletion include/dpp/cluster.h
Original file line number Diff line number Diff line change
Expand Up @@ -1400,8 +1400,9 @@ class DPP_EXPORT cluster {
* @param mimetype MIME type of POST data
* @param headers Headers to send with the request
* @param protocol HTTP protocol to use (1.1 and 1.0 are supported)
* @param request_timeout How many seconds before the connection is considered failed if not finished
*/
void request(const std::string &url, http_method method, http_completion_event callback, const std::string &postdata = "", const std::string &mimetype = "text/plain", const std::multimap<std::string, std::string> &headers = {}, const std::string &protocol = "1.1");
void request(const std::string &url, http_method method, http_completion_event callback, const std::string &postdata = "", const std::string &mimetype = "text/plain", const std::multimap<std::string, std::string> &headers = {}, const std::string &protocol = "1.1", time_t request_timeout = 5);

/**
* @brief Respond to a slash command
Expand Down
2 changes: 1 addition & 1 deletion include/dpp/cluster_coro_calls.h
Original file line number Diff line number Diff line change
Expand Up @@ -2566,5 +2566,5 @@


/* End of auto-generated definitions */
[[nodiscard]] async<http_request_completion_t> co_request(const std::string &url, http_method method, const std::string &postdata = "", const std::string &mimetype = "text/plain", const std::multimap<std::string, std::string> &headers = {}, const std::string &protocol = "1.1");
[[nodiscard]] async<http_request_completion_t> co_request(const std::string &url, http_method method, const std::string &postdata = "", const std::string &mimetype = "text/plain", const std::multimap<std::string, std::string> &headers = {}, const std::string &protocol = "1.1", time_t request_timeout = 5);

5 changes: 5 additions & 0 deletions include/dpp/httpsclient.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,11 @@ class DPP_EXPORT https_client : public ssl_client {
http_state get_state();

public:
/**
* @brief If true the response timed out while waiting
*/
bool timed_out;

/**
* @brief Connect to a specific HTTP(S) server and complete a request.
*
Expand Down
8 changes: 7 additions & 1 deletion include/dpp/queues.h
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,11 @@ class DPP_EXPORT http_request {
*/
std::string protocol;

/**
* @brief How many seconds before the connection is considered failed if not finished
*/
time_t request_timeout;

/**
* @brief Constructor. When constructing one of these objects it should be passed to request_queue::post_request().
* @param _endpoint The API endpoint, e.g. /api/guilds
Expand Down Expand Up @@ -332,8 +337,9 @@ class DPP_EXPORT http_request {
* @param _mimetype POST data mime type
* @param _headers HTTP headers to send
* @param http_protocol HTTP protocol
* @param _request_timeout How many seconds before the connection is considered failed if not finished
*/
http_request(const std::string &_url, http_completion_event completion, http_method method = m_get, const std::string &_postdata = "", const std::string &_mimetype = "text/plain", const std::multimap<std::string, std::string> &_headers = {}, const std::string &http_protocol = "1.1");
http_request(const std::string &_url, http_completion_event completion, http_method method = m_get, const std::string &_postdata = "", const std::string &_mimetype = "text/plain", const std::multimap<std::string, std::string> &_headers = {}, const std::string &http_protocol = "1.1", time_t _request_timeout = 5);

/**
* @brief Destroy the http request object
Expand Down
4 changes: 2 additions & 2 deletions src/dpp/cluster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -382,8 +382,8 @@ void cluster::post_rest_multipart(const std::string &endpoint, const std::string
}


void cluster::request(const std::string &url, http_method method, http_completion_event callback, const std::string &postdata, const std::string &mimetype, const std::multimap<std::string, std::string> &headers, const std::string &protocol) {
raw_rest->post_request(std::make_unique<http_request>(url, callback, method, postdata, mimetype, headers, protocol));
void cluster::request(const std::string &url, http_method method, http_completion_event callback, const std::string &postdata, const std::string &mimetype, const std::multimap<std::string, std::string> &headers, const std::string &protocol, time_t request_timeout) {
raw_rest->post_request(std::make_unique<http_request>(url, callback, method, postdata, mimetype, headers, protocol, request_timeout));
}

gateway::gateway() : shards(0), session_start_total(0), session_start_remaining(0), session_start_reset_after(0), session_start_max_concurrency(0) {
Expand Down
4 changes: 2 additions & 2 deletions src/dpp/cluster_coro_calls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -851,8 +851,8 @@ async<confirmation_callback_t> cluster::co_get_webhook_with_token(snowflake webh
};

/* End of auto-generated definitions */
dpp::async<dpp::http_request_completion_t> dpp::cluster::co_request(const std::string &url, http_method method, const std::string &postdata, const std::string &mimetype, const std::multimap<std::string, std::string> &headers, const std::string &protocol) {
return async<http_request_completion_t>{ [&, this] <typename C> (C &&cc) { return this->request(url, method, std::forward<C>(cc), postdata, mimetype, headers, protocol); }};
dpp::async<dpp::http_request_completion_t> dpp::cluster::co_request(const std::string &url, http_method method, const std::string &postdata, const std::string &mimetype, const std::multimap<std::string, std::string> &headers, const std::string &protocol, time_t request_timeout) {
return async<http_request_completion_t>{ [&, this] <typename C> (C &&cc) { return this->request(url, method, std::forward<C>(cc), postdata, mimetype, headers, protocol, request_timeout); }};
}

#endif
7 changes: 6 additions & 1 deletion src/dpp/httpsclient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ https_client::https_client(const std::string &hostname, uint16_t port, const st
request_headers(extra_headers),
status(0),
http_protocol(protocol),
timeout(request_timeout)
timeout(request_timeout),
timed_out(false)
{
nonblocking = false;
timeout = time(nullptr) + request_timeout;
Expand Down Expand Up @@ -313,6 +314,10 @@ http_state https_client::get_state() {

void https_client::one_second_timer() {
if ((this->sfd == SOCKET_ERROR || time(nullptr) >= timeout) && this->state != HTTPS_DONE) {
/* if and only if response is timed out */
if (this->sfd != SOCKET_ERROR) {
timed_out = true;
}
keepalive = false;
this->close();
}
Expand Down
15 changes: 9 additions & 6 deletions src/dpp/queues.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
namespace dpp {

http_request::http_request(const std::string &_endpoint, const std::string &_parameters, http_completion_event completion, const std::string &_postdata, http_method _method, const std::string &audit_reason, const std::string &filename, const std::string &filecontent, const std::string &filemimetype, const std::string &http_protocol)
: complete_handler(completion), completed(false), non_discord(false), endpoint(_endpoint), parameters(_parameters), postdata(_postdata), method(_method), reason(audit_reason), mimetype("application/json"), waiting(false), protocol(http_protocol)
: complete_handler(completion), completed(false), non_discord(false), endpoint(_endpoint), parameters(_parameters), postdata(_postdata), method(_method), reason(audit_reason), mimetype("application/json"), waiting(false), protocol(http_protocol), request_timeout(5)
{
if (!filename.empty()) {
file_name.push_back(filename);
Expand All @@ -47,13 +47,13 @@ http_request::http_request(const std::string &_endpoint, const std::string &_par
}

http_request::http_request(const std::string &_endpoint, const std::string &_parameters, http_completion_event completion, const std::string &_postdata, http_method method, const std::string &audit_reason, const std::vector<std::string> &filename, const std::vector<std::string> &filecontent, const std::vector<std::string> &filemimetypes, const std::string &http_protocol)
: complete_handler(completion), completed(false), non_discord(false), endpoint(_endpoint), parameters(_parameters), postdata(_postdata), method(method), reason(audit_reason), file_name(filename), file_content(filecontent), file_mimetypes(filemimetypes), mimetype("application/json"), waiting(false), protocol(http_protocol)
: complete_handler(completion), completed(false), non_discord(false), endpoint(_endpoint), parameters(_parameters), postdata(_postdata), method(method), reason(audit_reason), file_name(filename), file_content(filecontent), file_mimetypes(filemimetypes), mimetype("application/json"), waiting(false), protocol(http_protocol), request_timeout(5)
{
}


http_request::http_request(const std::string &_url, http_completion_event completion, http_method _method, const std::string &_postdata, const std::string &_mimetype, const std::multimap<std::string, std::string> &_headers, const std::string &http_protocol)
: complete_handler(completion), completed(false), non_discord(true), endpoint(_url), postdata(_postdata), method(_method), mimetype(_mimetype), req_headers(_headers), waiting(false), protocol(http_protocol)
http_request::http_request(const std::string &_url, http_completion_event completion, http_method _method, const std::string &_postdata, const std::string &_mimetype, const std::multimap<std::string, std::string> &_headers, const std::string &http_protocol, time_t _request_timeout)
: complete_handler(completion), completed(false), non_discord(true), endpoint(_url), postdata(_postdata), method(_method), mimetype(_mimetype), req_headers(_headers), waiting(false), protocol(http_protocol), request_timeout(_request_timeout)
{
}

Expand Down Expand Up @@ -176,9 +176,12 @@ http_request_completion_t http_request::run(cluster* owner) {
}
http_connect_info hci = https_client::get_host_info(_host);
try {
https_client cli(hci.hostname, hci.port, _url, request_verb[method], multipart.body, headers, !hci.is_ssl, 5, protocol);
https_client cli(hci.hostname, hci.port, _url, request_verb[method], multipart.body, headers, !hci.is_ssl, request_timeout, protocol);
rv.latency = dpp::utility::time_f() - start;
if (cli.get_status() < 100) {
if (cli.timed_out) {
rv.error = h_connection;
owner->log(ll_error, "HTTP(S) error on " + hci.scheme + " connection to " + hci.hostname + ":" + std::to_string(hci.port) + ": Timed out while waiting for the response");
} else if (cli.get_status() < 100) {
rv.error = h_connection;
owner->log(ll_error, "HTTP(S) error on " + hci.scheme + " connection to " + hci.hostname + ":" + std::to_string(hci.port) + ": Malformed HTTP response");
} else {
Expand Down