diff --git a/buildtools/classes/Generator/CoroGenerator.php b/buildtools/classes/Generator/CoroGenerator.php index 1a68809a70..7749054bc9 100644 --- a/buildtools/classes/Generator/CoroGenerator.php +++ b/buildtools/classes/Generator/CoroGenerator.php @@ -116,7 +116,7 @@ public function getCommentArray(): array */ public function saveHeader(string $content): void { - $content .= "[[nodiscard]] async co_request(const std::string &url, http_method method, const std::string &postdata = \"\", const std::string &mimetype = \"text/plain\", const std::multimap &headers = {}, const std::string &protocol = \"1.1\");\n\n"; + $content .= "[[nodiscard]] async co_request(const std::string &url, http_method method, const std::string &postdata = \"\", const std::string &mimetype = \"text/plain\", const std::multimap &headers = {}, const std::string &protocol = \"1.1\", time_t request_timeout = 5);\n\n"; file_put_contents('include/dpp/cluster_coro_calls.h', $content); } @@ -125,7 +125,7 @@ public function saveHeader(string $content): void */ public function saveCpp(string $cppcontent): void { - $cppcontent .= "dpp::async dpp::cluster::co_request(const std::string &url, http_method method, const std::string &postdata, const std::string &mimetype, const std::multimap &headers, const std::string &protocol) {\n\treturn async{ [&, this] (C &&cc) { return this->request(url, method, std::forward(cc), postdata, mimetype, headers, protocol); }};\n} + $cppcontent .= "dpp::async dpp::cluster::co_request(const std::string &url, http_method method, const std::string &postdata, const std::string &mimetype, const std::multimap &headers, const std::string &protocol, time_t request_timeout) {\n\treturn async{ [&, this] (C &&cc) { return this->request(url, method, std::forward(cc), postdata, mimetype, headers, protocol, request_timeout); }};\n} #endif "; diff --git a/include/dpp/cluster.h b/include/dpp/cluster.h index 9b486bbd53..48d5203492 100644 --- a/include/dpp/cluster.h +++ b/include/dpp/cluster.h @@ -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 &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 &headers = {}, const std::string &protocol = "1.1", time_t request_timeout = 5); /** * @brief Respond to a slash command diff --git a/include/dpp/cluster_coro_calls.h b/include/dpp/cluster_coro_calls.h index f46efaf581..831537f9f4 100644 --- a/include/dpp/cluster_coro_calls.h +++ b/include/dpp/cluster_coro_calls.h @@ -2566,5 +2566,5 @@ /* End of auto-generated definitions */ -[[nodiscard]] async co_request(const std::string &url, http_method method, const std::string &postdata = "", const std::string &mimetype = "text/plain", const std::multimap &headers = {}, const std::string &protocol = "1.1"); +[[nodiscard]] async co_request(const std::string &url, http_method method, const std::string &postdata = "", const std::string &mimetype = "text/plain", const std::multimap &headers = {}, const std::string &protocol = "1.1", time_t request_timeout = 5); diff --git a/include/dpp/httpsclient.h b/include/dpp/httpsclient.h index 7884baab39..0090c68af4 100644 --- a/include/dpp/httpsclient.h +++ b/include/dpp/httpsclient.h @@ -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. * diff --git a/include/dpp/queues.h b/include/dpp/queues.h index 3097d5bcb6..1ae28b5400 100644 --- a/include/dpp/queues.h +++ b/include/dpp/queues.h @@ -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 @@ -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 &_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 &_headers = {}, const std::string &http_protocol = "1.1", time_t _request_timeout = 5); /** * @brief Destroy the http request object diff --git a/src/dpp/cluster.cpp b/src/dpp/cluster.cpp index 0bad50e268..83d53a0528 100644 --- a/src/dpp/cluster.cpp +++ b/src/dpp/cluster.cpp @@ -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 &headers, const std::string &protocol) { - raw_rest->post_request(std::make_unique(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 &headers, const std::string &protocol, time_t request_timeout) { + raw_rest->post_request(std::make_unique(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) { diff --git a/src/dpp/cluster_coro_calls.cpp b/src/dpp/cluster_coro_calls.cpp index 27e274a81e..52bf1e814e 100644 --- a/src/dpp/cluster_coro_calls.cpp +++ b/src/dpp/cluster_coro_calls.cpp @@ -851,8 +851,8 @@ async cluster::co_get_webhook_with_token(snowflake webh }; /* End of auto-generated definitions */ -dpp::async dpp::cluster::co_request(const std::string &url, http_method method, const std::string &postdata, const std::string &mimetype, const std::multimap &headers, const std::string &protocol) { - return async{ [&, this] (C &&cc) { return this->request(url, method, std::forward(cc), postdata, mimetype, headers, protocol); }}; +dpp::async dpp::cluster::co_request(const std::string &url, http_method method, const std::string &postdata, const std::string &mimetype, const std::multimap &headers, const std::string &protocol, time_t request_timeout) { + return async{ [&, this] (C &&cc) { return this->request(url, method, std::forward(cc), postdata, mimetype, headers, protocol, request_timeout); }}; } #endif diff --git a/src/dpp/httpsclient.cpp b/src/dpp/httpsclient.cpp index b18675cd65..29598cd375 100644 --- a/src/dpp/httpsclient.cpp +++ b/src/dpp/httpsclient.cpp @@ -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; @@ -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(); } diff --git a/src/dpp/queues.cpp b/src/dpp/queues.cpp index 3625fadd2b..b6d691210a 100644 --- a/src/dpp/queues.cpp +++ b/src/dpp/queues.cpp @@ -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); @@ -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 &filename, const std::vector &filecontent, const std::vector &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 &_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 &_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) { } @@ -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 {