Merge branch 'web_server_use_arg_api' into integration

This commit is contained in:
J. Nick Koston
2026-02-11 18:39:53 -06:00
3 changed files with 44 additions and 48 deletions
+15 -28
View File
@@ -1,17 +1,13 @@
#ifdef USE_ESP32
#include <memory>
#include <cstring>
#include <cctype>
#include "esphome/core/helpers.h"
#include "esphome/core/log.h"
#include "http_parser.h"
#include "utils.h"
namespace esphome::web_server_idf {
static const char *const TAG = "web_server_idf_utils";
size_t url_decode(char *str) {
char *start = str;
char *ptr = str, buf;
@@ -54,38 +50,29 @@ optional<std::string> request_get_header(httpd_req_t *req, const char *name) {
return {str};
}
optional<std::string> request_get_url_query(httpd_req_t *req) {
auto len = httpd_req_get_url_query_len(req);
if (len == 0) {
return {};
}
std::string str;
str.resize(len);
auto res = httpd_req_get_url_query_str(req, &str[0], len + 1);
if (res != ESP_OK) {
ESP_LOGW(TAG, "Can't get query for request: %s", esp_err_to_name(res));
return {};
}
return {str};
}
optional<std::string> query_key_value(const char *query_url, size_t query_len, const char *key) {
if (query_url == nullptr || query_len == 0) {
return {};
}
// Use stack buffer for typical query strings, heap fallback for large ones
SmallBufferWithHeapFallback<256, char> val(query_len);
if (httpd_query_key_value(query_url, key, val.get(), query_len) != ESP_OK) {
char val[CONFIG_HTTPD_MAX_URI_LEN + 1];
if (httpd_query_key_value(query_url, key, val, query_len) != ESP_OK) {
return {};
}
url_decode(val.get());
return {val.get()};
url_decode(val);
return {val};
}
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
+1 -4
View File
@@ -13,11 +13,8 @@ size_t url_decode(char *str);
bool request_has_header(httpd_req_t *req, const char *name);
optional<std::string> request_get_header(httpd_req_t *req, const char *name);
optional<std::string> request_get_url_query(httpd_req_t *req);
optional<std::string> query_key_value(const char *query_url, size_t query_len, const char *key);
inline optional<std::string> 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); }
@@ -393,13 +393,7 @@ AsyncWebParameter *AsyncWebServerRequest::getParam(const char *name) {
}
// Look up value from query strings
optional<std::string> val = query_key_value(this->post_query_.c_str(), this->post_query_.size(), name);
if (!val.has_value()) {
auto url_query = request_get_url_query(*this);
if (url_query.has_value()) {
val = query_key_value(url_query.value().c_str(), url_query.value().size(), name);
}
}
auto val = this->find_query_value_(name);
// Don't cache misses to avoid wasting memory when handlers check for
// optional parameters that don't exist in the request
@@ -412,19 +406,37 @@ AsyncWebParameter *AsyncWebServerRequest::getParam(const char *name) {
return param;
}
optional<std::string> AsyncWebServerRequest::find_query_value_(const char *name) const {
auto val = query_key_value(this->post_query_.c_str(), this->post_query_.size(), name);
if (val.has_value()) {
return val;
/// Search post_query then URL query with a callback.
/// Returns first truthy result, or value-initialized default.
/// Uses stack buffer for URL query to avoid heap allocation.
template<typename Func>
static auto search_query_sources(httpd_req_t *req, const std::string &post_query, const char *name, Func func)
-> decltype(func(nullptr, size_t{0}, name)) {
if (!post_query.empty()) {
auto result = func(post_query.c_str(), post_query.size(), name);
if (result) {
return result;
}
}
auto url_query = request_get_url_query(*this);
if (url_query.has_value()) {
return query_key_value(url_query.value().c_str(), url_query.value().size(), name);
auto len = httpd_req_get_url_query_len(req);
if (len == 0) {
return {};
}
return {};
char buf[AsyncWebServerRequest::URL_BUF_SIZE];
if (httpd_req_get_url_query_str(req, buf, len + 1) != ESP_OK) {
return {};
}
return func(buf, len, name);
}
bool AsyncWebServerRequest::hasArg(const char *name) { return this->find_query_value_(name).has_value(); }
optional<std::string> AsyncWebServerRequest::find_query_value_(const char *name) const {
return search_query_sources(this->req_, this->post_query_, name,
[](const char *q, size_t len, const char *k) { return query_key_value(q, len, k); });
}
bool AsyncWebServerRequest::hasArg(const char *name) {
return search_query_sources(this->req_, this->post_query_, name, query_has_key);
}
std::string AsyncWebServerRequest::arg(const char *name) {
auto val = this->find_query_value_(name);