Instead of treating any non-ESP_ERR_NOT_FOUND result as key-present,
explicitly check for the two success codes. This avoids false positives
from unexpected error codes like ESP_ERR_INVALID_ARG.
Instead of treating any non-ESP_ERR_NOT_FOUND result as key-present,
explicitly check for the two success codes. This avoids false positives
from unexpected error codes like ESP_ERR_INVALID_ARG.
The query string already lives in req->uri. Access it via strchr('?')
instead of copying into a 513-byte stack buffer via httpd_req_get_url_query_str.
This is the same pattern url_to() uses — http_parser identifies URI
components by offset/length without modifying the source string.
Eliminates 513 bytes of stack usage on the httpd task, leaving only
the 128-byte value extraction buffer in query_key_value.
The query string already lives in req->uri. Access it via strchr('?')
instead of copying into a 513-byte stack buffer via httpd_req_get_url_query_str.
This is the same pattern url_to() uses — http_parser identifies URI
components by offset/length without modifying the source string.
Eliminates 513 bytes of stack usage on the httpd task, leaving only
the 128-byte value extraction buffer in query_key_value.
768 bytes on the httpd task stack contributes to stack overflow when
combined with other stack-allocated buffers (query string parsing, etc.).
512 bytes still covers all typical JSON payloads (sensors ~200B, lights
~170B, climate ~500-700B). Only extreme edge cases with 40+ options
trigger heap fallback.
768 bytes on the httpd task stack contributes to stack overflow when
combined with other stack-allocated buffers (query string parsing, etc.).
512 bytes still covers all typical JSON payloads (sensors ~200B, lights
~170B, climate ~500-700B). Only extreme edge cases with 40+ options
trigger heap fallback.
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.
search_query_sources was copying the URL query string into a 513-byte
stack buffer, then query_key_value added another 513-byte buffer for
the extracted value — 1026 bytes simultaneously on the httpd thread's
limited stack, causing a crash in lwip_select.
The query string already lives in req->uri after the '?'. Access it
directly via pointer instead of copying, eliminating one buffer entirely.
Query strings cannot exceed the max URI length, so SmallBufferWithHeapFallback
is unnecessary. Use a plain stack array instead for zero heap allocation.
- Add early return for empty post_query (common GET request path)
- Refactor getParam to use find_query_value_ instead of duplicating search logic
- Remove now-unused request_get_url_query (and its heap allocation)
- Remove unused std::string overload of query_key_value
Replace request_get_url_query (returns std::string) with inline
httpd_req_get_url_query_str into a SmallBufferWithHeapFallback
stack buffer. Typical query strings (<256 bytes) now use zero
heap allocations for parameter lookups.
Both methods iterated post_query_ then url_query with the same
pattern. Extracted a file-local template that takes a callback,
avoiding duplicated request_get_url_query heap allocation logic.
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.
Use const auto& to bind arg() result directly, avoiding
unnecessary std::string intermediate copies. Construct
std::string only where needed (lambda capture, setter call).
Use const auto& to bind directly to arg() result (std::string on
IDF, Arduino String on Arduino) and pass c_str()/length() to the
setter. No intermediate std::string copy needed.
ClimateCall has overloaded set_target_temperature*(float) and
set_target_temperature*(optional<float>), so the compiler can't
infer NumT. Use static_cast to select the float overload.
ClimateCall has overloaded set_target_temperature*(float) and
set_target_temperature*(optional<float>), so the compiler can't
infer NumT. Use static_cast to select the float overload.
- Mark find_query_value_ as const
- Remove redundant hasArg() guards where parse_number() already
handles empty strings (returns nullopt)
- Use !empty() instead of hasArg() for parse_bool_param_
- Merge parse_float_param_ and parse_int_param_ into single
parse_num_param_ template
- Combine missing/empty checks for IR data parameter
On Arduino, arg() returns const String&, so auto copies unnecessarily.
const auto& binds to the reference on Arduino and extends the temporary
lifetime on IDF.
Switch all web_server callers from getParam()/hasParam() to arg()/hasArg().
Both APIs exist on Arduino ESPAsyncWebServer and our IDF implementation.
On the IDF side, getParam() allocated a new AsyncWebParameter on the heap
for every successful lookup, cached it in a vector, and required cleanup
in the destructor. No caller ever held the pointer or called getParam
twice with the same name - every use was just getParam("x")->value()
immediately.
Rewrite IDF arg()/hasArg() to call query_key_value() directly, bypassing
getParam entirely. The linker strips the now-unreferenced getParam,
AsyncWebParameter, and cache machinery.
Saves ~348 bytes flash on ESP32-IDF, ~272 bytes on ESP8266 Arduino.
The 1460-byte MULTIPART_CHUNK_SIZE buffer was moved from heap to stack
in #13549, but the httpd task stack is only ~4096-5632 bytes. This
causes a stack overflow crash when processing OTA uploads, especially
on configs with BLE components that add additional stack pressure.
Move it back to heap since this buffer is only used during OTA uploads
(not a hot path), so heap fragmentation is not a concern here.
Fixes stack overflow: "A stack overflow in task httpd has been detected"