From e42cc2e3944d30e5ac64fb16addaceb14c2847ee Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Wed, 11 Feb 2026 18:23:38 -0600 Subject: [PATCH] Add query_has_key for efficient hasArg without string allocation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hasArg only needs to know if a key exists, not its value. query_has_key uses a 1-byte buffer and checks the return code from httpd_query_key_value — no url_decode, no std::string. --- esphome/components/web_server_idf/utils.cpp | 11 +++++++++++ esphome/components/web_server_idf/utils.h | 1 + esphome/components/web_server_idf/web_server_idf.cpp | 11 ++++++++++- 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/esphome/components/web_server_idf/utils.cpp b/esphome/components/web_server_idf/utils.cpp index 81ae626277..542a7fc0c0 100644 --- a/esphome/components/web_server_idf/utils.cpp +++ b/esphome/components/web_server_idf/utils.cpp @@ -88,6 +88,17 @@ optional query_key_value(const char *query_url, size_t query_len, c return {val.get()}; } +bool query_has_key(const char *query_url, size_t query_len, const char *key) { + if (query_url == nullptr || query_len == 0) { + return false; + } + // Minimal buffer — we only care if the key exists, not the value + char buf[1]; + // httpd_query_key_value returns ESP_OK if key found (even if buffer too small for value), + // ESP_ERR_NOT_FOUND if key absent + return httpd_query_key_value(query_url, key, buf, sizeof(buf)) != ESP_ERR_NOT_FOUND; +} + // Helper function for case-insensitive string region comparison bool str_ncmp_ci(const char *s1, const char *s2, size_t n) { for (size_t i = 0; i < n; i++) { diff --git a/esphome/components/web_server_idf/utils.h b/esphome/components/web_server_idf/utils.h index 87635c0458..8eee82696f 100644 --- a/esphome/components/web_server_idf/utils.h +++ b/esphome/components/web_server_idf/utils.h @@ -18,6 +18,7 @@ optional query_key_value(const char *query_url, size_t query_len, c inline optional query_key_value(const std::string &query_url, const std::string &key) { return query_key_value(query_url.c_str(), query_url.size(), key.c_str()); } +bool query_has_key(const char *query_url, size_t query_len, const char *key); // Helper function for case-insensitive character comparison inline bool char_equals_ci(char a, char b) { return ::tolower(a) == ::tolower(b); } diff --git a/esphome/components/web_server_idf/web_server_idf.cpp b/esphome/components/web_server_idf/web_server_idf.cpp index 1f59835268..1c33df9710 100644 --- a/esphome/components/web_server_idf/web_server_idf.cpp +++ b/esphome/components/web_server_idf/web_server_idf.cpp @@ -424,7 +424,16 @@ optional AsyncWebServerRequest::find_query_value_(const char *name) return {}; } -bool AsyncWebServerRequest::hasArg(const char *name) { return this->find_query_value_(name).has_value(); } +bool AsyncWebServerRequest::hasArg(const char *name) { + if (query_has_key(this->post_query_.c_str(), this->post_query_.size(), name)) { + return true; + } + auto url_query = request_get_url_query(*this); + if (url_query.has_value()) { + return query_has_key(url_query.value().c_str(), url_query.value().size(), name); + } + return false; +} std::string AsyncWebServerRequest::arg(const char *name) { auto val = this->find_query_value_(name);