From 544f8ae77175e086eab391cb9d958895506c8788 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 16 Sep 2026 09:14:52 -0500 Subject: [PATCH] [http_request] Take the URL and method as C strings (#19215) --- .../components/http_request/http_request.h | 64 ++++++++++++++----- .../http_request/http_request_arduino.cpp | 19 +++--- .../http_request/http_request_arduino.h | 2 +- .../http_request/http_request_host.cpp | 27 ++++---- .../http_request/http_request_host.h | 2 +- .../http_request/http_request_idf.cpp | 21 +++--- .../http_request/http_request_idf.h | 2 +- 7 files changed, 85 insertions(+), 52 deletions(-) diff --git a/esphome/components/http_request/http_request.h b/esphome/components/http_request/http_request.h index 4471dffdc2b..71668b8556f 100644 --- a/esphome/components/http_request/http_request.h +++ b/esphome/components/http_request/http_request.h @@ -331,27 +331,46 @@ class HttpRequestComponent : public Component { void set_follow_redirects(bool follow_redirects) { this->follow_redirects_ = follow_redirects; } void set_redirect_limit(uint16_t limit) { this->redirect_limit_ = limit; } - std::shared_ptr get(const std::string &url) { - return this->start(url, "GET", "", std::vector
{}); - } - std::shared_ptr get(const std::string &url, const std::vector
&request_headers) { + std::shared_ptr get(const char *url) { return this->start(url, "GET", "", std::vector
{}); } + std::shared_ptr get(const char *url, const std::vector
&request_headers) { return this->start(url, "GET", "", request_headers); } - std::shared_ptr get(const std::string &url, const std::vector
&request_headers, + std::shared_ptr get(const char *url, const std::vector
&request_headers, const std::vector &lower_case_collect_headers) { return this->start(url, "GET", "", request_headers, lower_case_collect_headers); } - std::shared_ptr post(const std::string &url, const std::string &body) { + std::shared_ptr post(const char *url, const std::string &body) { return this->start(url, "POST", body, std::vector
{}); } + std::shared_ptr post(const char *url, const std::string &body, + const std::vector
&request_headers) { + return this->start(url, "POST", body, request_headers); + } + std::shared_ptr post(const char *url, const std::string &body, + const std::vector
&request_headers, + const std::vector &lower_case_collect_headers) { + return this->start(url, "POST", body, request_headers, lower_case_collect_headers); + } + + std::shared_ptr get(const std::string &url) { return this->get(url.c_str()); } + std::shared_ptr get(const std::string &url, const std::vector
&request_headers) { + return this->get(url.c_str(), request_headers); + } + std::shared_ptr get(const std::string &url, const std::vector
&request_headers, + const std::vector &lower_case_collect_headers) { + return this->get(url.c_str(), request_headers, lower_case_collect_headers); + } + std::shared_ptr post(const std::string &url, const std::string &body) { + return this->post(url.c_str(), body); + } std::shared_ptr post(const std::string &url, const std::string &body, const std::vector
&request_headers) { - return this->start(url, "POST", body, request_headers); + return this->post(url.c_str(), body, request_headers); } std::shared_ptr post(const std::string &url, const std::string &body, const std::vector
&request_headers, const std::vector &lower_case_collect_headers) { - return this->start(url, "POST", body, request_headers, lower_case_collect_headers); + return this->post(url.c_str(), body, request_headers, lower_case_collect_headers); } // Remove before 2027.1.0 @@ -379,11 +398,15 @@ class HttpRequestComponent : public Component { return this->post(url, body, std::vector
(request_headers.begin(), request_headers.end()), collect_headers); } - std::shared_ptr start(const std::string &url, const std::string &method, const std::string &body, + std::shared_ptr start(const char *url, const char *method, const std::string &body, const std::vector
&request_headers) { // Call perform() directly to avoid ambiguity with the deprecated overloads return this->perform(url, method, body, request_headers, {}); } + std::shared_ptr start(const std::string &url, const std::string &method, const std::string &body, + const std::vector
&request_headers) { + return this->start(url.c_str(), method.c_str(), body, request_headers); + } // Remove before 2027.1.0 ESPDEPRECATED("Pass request_headers as std::vector
instead of std::list. Removed in 2027.1.0.", "2026.7.0") @@ -403,7 +426,7 @@ class HttpRequestComponent : public Component { for (const auto &h : collect_headers) { lower.push_back(str_lower_case(h)); // NOLINT } - return this->perform(url, method, body, request_headers, lower); + return this->perform(url.c_str(), method.c_str(), body, request_headers, lower); } // Remove before 2027.1.0 @@ -418,7 +441,8 @@ class HttpRequestComponent : public Component { for (const auto &h : collect_headers) { lower.push_back(str_lower_case(h)); // NOLINT } - return this->perform(url, method, body, std::vector
(request_headers.begin(), request_headers.end()), lower); + return this->perform(url.c_str(), method.c_str(), body, + std::vector
(request_headers.begin(), request_headers.end()), lower); } // Remove before 2027.1.0 @@ -426,19 +450,25 @@ class HttpRequestComponent : public Component { std::shared_ptr start(const std::string &url, const std::string &method, const std::string &body, const std::list
&request_headers, const std::vector &lower_case_collect_headers) { - return this->perform(url, method, body, std::vector
(request_headers.begin(), request_headers.end()), + return this->perform(url.c_str(), method.c_str(), body, + std::vector
(request_headers.begin(), request_headers.end()), lower_case_collect_headers); } - std::shared_ptr start(const std::string &url, const std::string &method, const std::string &body, + std::shared_ptr start(const char *url, const char *method, const std::string &body, const std::vector
&request_headers, const std::vector &lower_case_collect_headers) { return this->perform(url, method, body, request_headers, lower_case_collect_headers); } + std::shared_ptr start(const std::string &url, const std::string &method, const std::string &body, + const std::vector
&request_headers, + const std::vector &lower_case_collect_headers) { + return this->start(url.c_str(), method.c_str(), body, request_headers, lower_case_collect_headers); + } protected: - virtual std::shared_ptr perform(const std::string &url, const std::string &method, - const std::string &body, const std::vector
&request_headers, + virtual std::shared_ptr perform(const char *url, const char *method, const std::string &body, + const std::vector
&request_headers, const std::vector &lower_case_collect_headers) = 0; const char *useragent_{nullptr}; bool follow_redirects_{}; @@ -499,8 +529,8 @@ template class HttpRequestSendAction final : public Actionparent_->start(this->url_.value(x...), this->method_.value(x...), body, request_headers, - this->lower_case_collect_headers_); + auto container = this->parent_->start(this->url_.value(x...).c_str(), this->method_.value(x...), body, + request_headers, this->lower_case_collect_headers_); auto captured_args = std::make_tuple(x...); diff --git a/esphome/components/http_request/http_request_arduino.cpp b/esphome/components/http_request/http_request_arduino.cpp index 43ab2e5b53a..0d968222e9e 100644 --- a/esphome/components/http_request/http_request_arduino.cpp +++ b/esphome/components/http_request/http_request_arduino.cpp @@ -2,6 +2,8 @@ #if defined(USE_ARDUINO) && !defined(USE_ESP32) && !defined(USE_LIBRETINY) +#include + #include "esphome/components/network/util.h" #include "esphome/components/watchdog/watchdog.h" @@ -22,8 +24,7 @@ static const char *const TAG = "http_request"; static constexpr int ESP8266_SSL_ERR_OOM = -1000; #endif -std::shared_ptr HttpRequestArduino::perform(const std::string &url, const std::string &method, - const std::string &body, +std::shared_ptr HttpRequestArduino::perform(const char *url, const char *method, const std::string &body, const std::vector
&request_headers, const std::vector &lower_case_collect_headers) { if (!network::is_connected()) { @@ -37,7 +38,7 @@ std::shared_ptr HttpRequestArduino::perform(const std::string &ur const uint32_t start = millis(); - bool secure = url.find("https:") != std::string::npos; + bool secure = strstr(url, "https:") != nullptr; container->set_secure(secure); watchdog::WatchdogManager wdm(this->get_watchdog_timeout()); @@ -70,19 +71,19 @@ std::shared_ptr HttpRequestArduino::perform(const std::string &ur stream_ptr = std::make_unique(); #endif // USE_HTTP_REQUEST_ESP8266_HTTPS - bool status = container->client_.begin(*stream_ptr, url.c_str()); + bool status = container->client_.begin(*stream_ptr, url); #elif defined(USE_RP2) if (secure) { container->client_.setInsecure(); } - bool status = container->client_.begin(url.c_str()); + bool status = container->client_.begin(url); #endif App.feed_wdt(); if (!status) { - ESP_LOGW(TAG, "HTTP Request failed; URL: %s", url.c_str()); + ESP_LOGW(TAG, "HTTP Request failed; URL: %s", url); container->end(); this->status_momentary_error("failed", 1000); return nullptr; @@ -107,7 +108,7 @@ std::shared_ptr HttpRequestArduino::perform(const std::string &ur container->client_.collectHeaders(header_keys, index); App.feed_wdt(); - container->status_code = container->client_.sendRequest(method.c_str(), body.c_str()); + container->status_code = container->client_.sendRequest(method, body.c_str()); App.feed_wdt(); if (container->status_code < 0) { #if defined(USE_ESP8266) && defined(USE_HTTP_REQUEST_ESP8266_HTTPS) @@ -139,7 +140,7 @@ std::shared_ptr HttpRequestArduino::perform(const std::string &ur } #endif - ESP_LOGW(TAG, "HTTP Request failed; URL: %s; Error: %s", url.c_str(), + ESP_LOGW(TAG, "HTTP Request failed; URL: %s; Error: %s", url, HTTPClient::errorToString(container->status_code).c_str()); this->status_momentary_error("failed", 1000); @@ -147,7 +148,7 @@ std::shared_ptr HttpRequestArduino::perform(const std::string &ur return nullptr; } if (!is_success(container->status_code)) { - ESP_LOGE(TAG, "HTTP Request failed; URL: %s; Code: %d", url.c_str(), container->status_code); + ESP_LOGE(TAG, "HTTP Request failed; URL: %s; Code: %d", url, container->status_code); this->status_momentary_error("failed", 1000); // Still return the container, so it can be used to get the status code and error message } diff --git a/esphome/components/http_request/http_request_arduino.h b/esphome/components/http_request/http_request_arduino.h index 028b9f44a1c..62737f4d0d1 100644 --- a/esphome/components/http_request/http_request_arduino.h +++ b/esphome/components/http_request/http_request_arduino.h @@ -54,7 +54,7 @@ class HttpRequestArduino final : public HttpRequestComponent { #endif protected: - std::shared_ptr perform(const std::string &url, const std::string &method, const std::string &body, + std::shared_ptr perform(const char *url, const char *method, const std::string &body, const std::vector
&request_headers, const std::vector &lower_case_collect_headers) override; #ifdef USE_ESP8266 diff --git a/esphome/components/http_request/http_request_host.cpp b/esphome/components/http_request/http_request_host.cpp index cf231e20bdc..a7889702023 100644 --- a/esphome/components/http_request/http_request_host.cpp +++ b/esphome/components/http_request/http_request_host.cpp @@ -5,6 +5,8 @@ #include "httplib.h" #include "http_request_host.h" +#include + #include #include "esphome/components/network/util.h" #include "esphome/components/watchdog/watchdog.h" @@ -16,8 +18,7 @@ namespace esphome::http_request { static const char *const TAG = "http_request"; -std::shared_ptr HttpRequestHost::perform(const std::string &url, const std::string &method, - const std::string &body, +std::shared_ptr HttpRequestHost::perform(const char *url, const char *method, const std::string &body, const std::vector
&request_headers, const std::vector &lower_case_collect_headers) { if (!network::is_connected()) { @@ -27,10 +28,10 @@ std::shared_ptr HttpRequestHost::perform(const std::string &url, } std::regex url_regex(R"(^(([^:\/?#]+):)?(//([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?)", std::regex::extended); - std::smatch url_match_result; + std::cmatch url_match_result; if (!std::regex_match(url, url_match_result, url_regex) || url_match_result.length() < 7) { - ESP_LOGE(TAG, "HTTP Request failed; Malformed URL: %s", url.c_str()); + ESP_LOGE(TAG, "HTTP Request failed; Malformed URL: %s", url); return nullptr; } auto host = url_match_result[4].str(); @@ -54,7 +55,7 @@ std::shared_ptr HttpRequestHost::perform(const std::string &url, } httplib::Client client(scheme_host.c_str()); if (!client.is_valid()) { - ESP_LOGE(TAG, "HTTP Request failed; Invalid URL: %s", url.c_str()); + ESP_LOGE(TAG, "HTTP Request failed; Invalid URL: %s", url); return nullptr; } client.set_follow_location(this->follow_redirects_); @@ -64,41 +65,41 @@ std::shared_ptr HttpRequestHost::perform(const std::string &url, #endif httplib::Result result; - if (method == "GET") { + if (strcmp(method, "GET") == 0) { result = client.Get(path, h_headers, [&](const char *data, size_t data_length) { ESP_LOGV(TAG, "Got data length: %zu", data_length); container->response_body_.insert(container->response_body_.end(), (const uint8_t *) data, (const uint8_t *) data + data_length); return true; }); - } else if (method == "HEAD") { + } else if (strcmp(method, "HEAD") == 0) { result = client.Head(path, h_headers); - } else if (method == "PUT") { + } else if (strcmp(method, "PUT") == 0) { result = client.Put(path, h_headers, body, ""); if (result) { auto data = std::vector(result->body.begin(), result->body.end()); container->response_body_.insert(container->response_body_.end(), data.begin(), data.end()); } - } else if (method == "PATCH") { + } else if (strcmp(method, "PATCH") == 0) { result = client.Patch(path, h_headers, body, ""); if (result) { auto data = std::vector(result->body.begin(), result->body.end()); container->response_body_.insert(container->response_body_.end(), data.begin(), data.end()); } - } else if (method == "POST") { + } else if (strcmp(method, "POST") == 0) { result = client.Post(path, h_headers, body, ""); if (result) { auto data = std::vector(result->body.begin(), result->body.end()); container->response_body_.insert(container->response_body_.end(), data.begin(), data.end()); } } else { - ESP_LOGW(TAG, "HTTP Request failed - unsupported method %s; URL: %s", method.c_str(), url.c_str()); + ESP_LOGW(TAG, "HTTP Request failed - unsupported method %s; URL: %s", method, url); container->end(); return nullptr; } App.feed_wdt(); if (!result) { - ESP_LOGW(TAG, "HTTP Request failed; URL: %s, error code: %u", url.c_str(), (unsigned) result.error()); + ESP_LOGW(TAG, "HTTP Request failed; URL: %s, error code: %u", url, (unsigned) result.error()); container->end(); this->status_momentary_error("failed", 1000); return nullptr; @@ -107,7 +108,7 @@ std::shared_ptr HttpRequestHost::perform(const std::string &url, auto response = *result; container->status_code = response.status; if (!is_success(response.status)) { - ESP_LOGE(TAG, "HTTP Request failed; URL: %s; Code: %d", url.c_str(), response.status); + ESP_LOGE(TAG, "HTTP Request failed; URL: %s; Code: %d", url, response.status); this->status_momentary_error("failed", 1000); // Still return the container, so it can be used to get the status code and error message } diff --git a/esphome/components/http_request/http_request_host.h b/esphome/components/http_request/http_request_host.h index 9045702f46a..0ae9f2e27b9 100644 --- a/esphome/components/http_request/http_request_host.h +++ b/esphome/components/http_request/http_request_host.h @@ -18,7 +18,7 @@ class HttpContainerHost : public HttpContainer { class HttpRequestHost final : public HttpRequestComponent { public: - std::shared_ptr perform(const std::string &url, const std::string &method, const std::string &body, + std::shared_ptr perform(const char *url, const char *method, const std::string &body, const std::vector
&request_headers, const std::vector &lower_case_collect_headers) override; void set_ca_path(const char *ca_path) { this->ca_path_ = ca_path; } diff --git a/esphome/components/http_request/http_request_idf.cpp b/esphome/components/http_request/http_request_idf.cpp index 10313be89db..4e5a2c42b57 100644 --- a/esphome/components/http_request/http_request_idf.cpp +++ b/esphome/components/http_request/http_request_idf.cpp @@ -2,6 +2,8 @@ #ifdef USE_ESP32 +#include + #include "esphome/components/network/util.h" #include "esphome/components/watchdog/watchdog.h" @@ -48,8 +50,7 @@ esp_err_t HttpRequestIDF::http_event_handler(esp_http_client_event_t *evt) { return ESP_OK; } -std::shared_ptr HttpRequestIDF::perform(const std::string &url, const std::string &method, - const std::string &body, +std::shared_ptr HttpRequestIDF::perform(const char *url, const char *method, const std::string &body, const std::vector
&request_headers, const std::vector &lower_case_collect_headers) { if (!network::is_connected()) { @@ -59,15 +60,15 @@ std::shared_ptr HttpRequestIDF::perform(const std::string &url, c } esp_http_client_method_t method_idf; - if (method == "GET") { + if (strcmp(method, "GET") == 0) { method_idf = HTTP_METHOD_GET; - } else if (method == "POST") { + } else if (strcmp(method, "POST") == 0) { method_idf = HTTP_METHOD_POST; - } else if (method == "PUT") { + } else if (strcmp(method, "PUT") == 0) { method_idf = HTTP_METHOD_PUT; - } else if (method == "DELETE") { + } else if (strcmp(method, "DELETE") == 0) { method_idf = HTTP_METHOD_DELETE; - } else if (method == "PATCH") { + } else if (strcmp(method, "PATCH") == 0) { method_idf = HTTP_METHOD_PATCH; } else { this->status_momentary_error("failed", ERROR_DURATION_MS); @@ -75,11 +76,11 @@ std::shared_ptr HttpRequestIDF::perform(const std::string &url, c return nullptr; } - bool secure = url.find("https:") != std::string::npos; + bool secure = strstr(url, "https:") != nullptr; esp_http_client_config_t config = {}; - config.url = url.c_str(); + config.url = url; config.method = method_idf; config.timeout_ms = this->timeout_; config.disable_auto_redirect = !this->follow_redirects_; @@ -218,7 +219,7 @@ std::shared_ptr HttpRequestIDF::perform(const std::string &url, c } } - ESP_LOGE(TAG, "HTTP Request failed; URL: %s; Code: %d", url.c_str(), container->status_code); + ESP_LOGE(TAG, "HTTP Request failed; URL: %s; Code: %d", url, container->status_code); this->status_momentary_error("failed", ERROR_DURATION_MS); return container; } diff --git a/esphome/components/http_request/http_request_idf.h b/esphome/components/http_request/http_request_idf.h index 1c062af81b1..f84dc9576bd 100644 --- a/esphome/components/http_request/http_request_idf.h +++ b/esphome/components/http_request/http_request_idf.h @@ -41,7 +41,7 @@ class HttpRequestIDF final : public HttpRequestComponent { void set_ca_certificate(const char *ca_certificate) { this->ca_certificate_ = ca_certificate; } protected: - std::shared_ptr perform(const std::string &url, const std::string &method, const std::string &body, + std::shared_ptr perform(const char *url, const char *method, const std::string &body, const std::vector
&request_headers, const std::vector &lower_case_collect_headers) override; // if zero ESP-IDF will use DEFAULT_HTTP_BUF_SIZE