mirror of
https://github.com/esphome/esphome.git
synced 2026-03-01 02:14:19 -07:00
Revert direct req->uri access (unsafe — ESP-IDF uses parsed offsets, not strchr). Instead fix the root cause: query_key_value used a full CONFIG_HTTPD_MAX_URI_LEN+1 (513 byte) stack buffer while search_query_sources had another 513 byte buffer on the stack simultaneously, totaling ~1KB on the httpd thread's limited stack. Use SmallBufferWithHeapFallback<128> for the value extraction buffer. 128 bytes covers typical parameter values on stack; longer values (e.g. base64 IR data) fall back to heap.
118 lines
2.9 KiB
C++
118 lines
2.9 KiB
C++
#ifdef USE_ESP32
|
|
#include <cstring>
|
|
#include <cctype>
|
|
#include "esphome/core/helpers.h"
|
|
#include "http_parser.h"
|
|
|
|
#include "utils.h"
|
|
|
|
namespace esphome::web_server_idf {
|
|
|
|
size_t url_decode(char *str) {
|
|
char *start = str;
|
|
char *ptr = str, buf;
|
|
for (; *str; str++, ptr++) {
|
|
if (*str == '%') {
|
|
str++;
|
|
if (parse_hex(str, 2, reinterpret_cast<uint8_t *>(&buf), 1) == 2) {
|
|
*ptr = buf;
|
|
str++;
|
|
} else {
|
|
str--;
|
|
*ptr = *str;
|
|
}
|
|
} else if (*str == '+') {
|
|
*ptr = ' ';
|
|
} else {
|
|
*ptr = *str;
|
|
}
|
|
}
|
|
*ptr = '\0';
|
|
return ptr - start;
|
|
}
|
|
|
|
bool request_has_header(httpd_req_t *req, const char *name) { return httpd_req_get_hdr_value_len(req, name); }
|
|
|
|
optional<std::string> request_get_header(httpd_req_t *req, const char *name) {
|
|
size_t len = httpd_req_get_hdr_value_len(req, name);
|
|
if (len == 0) {
|
|
return {};
|
|
}
|
|
|
|
std::string str;
|
|
str.resize(len);
|
|
|
|
auto res = httpd_req_get_hdr_value_str(req, name, &str[0], len + 1);
|
|
if (res != ESP_OK) {
|
|
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 {};
|
|
}
|
|
|
|
// Value can't exceed query_len. Use small stack buffer for typical values,
|
|
// heap fallback for long ones (e.g. base64 IR data) to limit stack usage
|
|
// since callers may also have stack buffers for the query string.
|
|
SmallBufferWithHeapFallback<128, char> val(query_len);
|
|
if (httpd_query_key_value(query_url, key, val.get(), query_len) != ESP_OK) {
|
|
return {};
|
|
}
|
|
|
|
url_decode(val.get());
|
|
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++) {
|
|
if (!char_equals_ci(s1[i], s2[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Bounded case-insensitive string search (like strcasestr but length-bounded)
|
|
const char *strcasestr_n(const char *haystack, size_t haystack_len, const char *needle) {
|
|
if (!haystack) {
|
|
return nullptr;
|
|
}
|
|
|
|
size_t needle_len = strlen(needle);
|
|
if (needle_len == 0) {
|
|
return haystack;
|
|
}
|
|
|
|
if (haystack_len < needle_len) {
|
|
return nullptr;
|
|
}
|
|
|
|
const char *end = haystack + haystack_len - needle_len + 1;
|
|
for (const char *p = haystack; p < end; p++) {
|
|
if (str_ncmp_ci(p, needle, needle_len)) {
|
|
return p;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
} // namespace esphome::web_server_idf
|
|
#endif // USE_ESP32
|