mirror of
https://github.com/esphome/esphome.git
synced 2026-02-12 04:27:43 -07:00
Compare commits
24 Commits
web-server
...
web_server
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
220c2acf0e | ||
|
|
52461f10e7 | ||
|
|
2348ad2a03 | ||
|
|
2b5bd961ef | ||
|
|
5a88bb6d8a | ||
|
|
53345724f2 | ||
|
|
92d800412a | ||
|
|
e57612d522 | ||
|
|
f0828928b4 | ||
|
|
e42cc2e394 | ||
|
|
592d5ec24c | ||
|
|
91a0b0989e | ||
|
|
f35dfefdf3 | ||
|
|
892804e02e | ||
|
|
5585b5967e | ||
|
|
73867c62be | ||
|
|
920f84fa1d | ||
|
|
c2bb55ff5d | ||
|
|
c6b51d3434 | ||
|
|
f638b65f1e | ||
|
|
f92725f76e | ||
|
|
2a89088bc3 | ||
|
|
db831ebee0 | ||
|
|
4f3c95ced2 |
@@ -47,8 +47,8 @@ void CaptivePortal::handle_config(AsyncWebServerRequest *request) {
|
||||
request->send(stream);
|
||||
}
|
||||
void CaptivePortal::handle_wifisave(AsyncWebServerRequest *request) {
|
||||
std::string ssid = request->arg("ssid").c_str(); // NOLINT(readability-redundant-string-cstr)
|
||||
std::string psk = request->arg("psk").c_str(); // NOLINT(readability-redundant-string-cstr)
|
||||
const auto &ssid = request->arg("ssid");
|
||||
const auto &psk = request->arg("psk");
|
||||
ESP_LOGI(TAG,
|
||||
"Requested WiFi Settings Change:\n"
|
||||
" SSID='%s'\n"
|
||||
@@ -56,10 +56,10 @@ void CaptivePortal::handle_wifisave(AsyncWebServerRequest *request) {
|
||||
ssid.c_str(), psk.c_str());
|
||||
#ifdef USE_ESP8266
|
||||
// ESP8266 is single-threaded, call directly
|
||||
wifi::global_wifi_component->save_wifi_sta(ssid, psk);
|
||||
wifi::global_wifi_component->save_wifi_sta(ssid.c_str(), psk.c_str());
|
||||
#else
|
||||
// Defer save to main loop thread to avoid NVS operations from HTTP thread
|
||||
this->defer([ssid, psk]() { wifi::global_wifi_component->save_wifi_sta(ssid, psk); });
|
||||
this->defer([ssid, psk]() { wifi::global_wifi_component->save_wifi_sta(ssid.c_str(), psk.c_str()); });
|
||||
#endif
|
||||
request->redirect(ESPHOME_F("/?save"));
|
||||
}
|
||||
|
||||
@@ -583,8 +583,7 @@ static void set_json_icon_state_value(JsonObject &root, EntityBase *obj, const c
|
||||
|
||||
// Helper to get request detail parameter
|
||||
static JsonDetail get_request_detail(AsyncWebServerRequest *request) {
|
||||
auto *param = request->getParam(ESPHOME_F("detail"));
|
||||
return (param && param->value() == "all") ? DETAIL_ALL : DETAIL_STATE;
|
||||
return request->arg(ESPHOME_F("detail")) == "all" ? DETAIL_ALL : DETAIL_STATE;
|
||||
}
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
@@ -861,10 +860,10 @@ void WebServer::handle_fan_request(AsyncWebServerRequest *request, const UrlMatc
|
||||
}
|
||||
auto call = is_on ? obj->turn_on() : obj->turn_off();
|
||||
|
||||
parse_int_param_(request, ESPHOME_F("speed_level"), call, &decltype(call)::set_speed);
|
||||
parse_num_param_(request, ESPHOME_F("speed_level"), call, &decltype(call)::set_speed);
|
||||
|
||||
if (request->hasParam(ESPHOME_F("oscillation"))) {
|
||||
auto speed = request->getParam(ESPHOME_F("oscillation"))->value();
|
||||
if (request->hasArg(ESPHOME_F("oscillation"))) {
|
||||
auto speed = request->arg(ESPHOME_F("oscillation"));
|
||||
auto val = parse_on_off(speed.c_str());
|
||||
switch (val) {
|
||||
case PARSE_ON:
|
||||
@@ -1040,14 +1039,14 @@ void WebServer::handle_cover_request(AsyncWebServerRequest *request, const UrlMa
|
||||
}
|
||||
|
||||
auto traits = obj->get_traits();
|
||||
if ((request->hasParam(ESPHOME_F("position")) && !traits.get_supports_position()) ||
|
||||
(request->hasParam(ESPHOME_F("tilt")) && !traits.get_supports_tilt())) {
|
||||
if ((request->hasArg(ESPHOME_F("position")) && !traits.get_supports_position()) ||
|
||||
(request->hasArg(ESPHOME_F("tilt")) && !traits.get_supports_tilt())) {
|
||||
request->send(409);
|
||||
return;
|
||||
}
|
||||
|
||||
parse_float_param_(request, ESPHOME_F("position"), call, &decltype(call)::set_position);
|
||||
parse_float_param_(request, ESPHOME_F("tilt"), call, &decltype(call)::set_tilt);
|
||||
parse_num_param_(request, ESPHOME_F("position"), call, &decltype(call)::set_position);
|
||||
parse_num_param_(request, ESPHOME_F("tilt"), call, &decltype(call)::set_tilt);
|
||||
|
||||
DEFER_ACTION(call, call.perform());
|
||||
request->send(200);
|
||||
@@ -1106,7 +1105,7 @@ void WebServer::handle_number_request(AsyncWebServerRequest *request, const UrlM
|
||||
}
|
||||
|
||||
auto call = obj->make_call();
|
||||
parse_float_param_(request, ESPHOME_F("value"), call, &decltype(call)::set_value);
|
||||
parse_num_param_(request, ESPHOME_F("value"), call, &decltype(call)::set_value);
|
||||
|
||||
DEFER_ACTION(call, call.perform());
|
||||
request->send(200);
|
||||
@@ -1174,12 +1173,13 @@ void WebServer::handle_date_request(AsyncWebServerRequest *request, const UrlMat
|
||||
|
||||
auto call = obj->make_call();
|
||||
|
||||
if (!request->hasParam(ESPHOME_F("value"))) {
|
||||
const auto &value = request->arg(ESPHOME_F("value"));
|
||||
// Arduino String has isEmpty() not empty(), use length() for cross-platform compatibility
|
||||
if (value.length() == 0) { // NOLINT(readability-container-size-empty)
|
||||
request->send(409);
|
||||
return;
|
||||
}
|
||||
|
||||
parse_string_param_(request, ESPHOME_F("value"), call, &decltype(call)::set_date);
|
||||
call.set_date(value.c_str(), value.length());
|
||||
|
||||
DEFER_ACTION(call, call.perform());
|
||||
request->send(200);
|
||||
@@ -1234,12 +1234,13 @@ void WebServer::handle_time_request(AsyncWebServerRequest *request, const UrlMat
|
||||
|
||||
auto call = obj->make_call();
|
||||
|
||||
if (!request->hasParam(ESPHOME_F("value"))) {
|
||||
const auto &value = request->arg(ESPHOME_F("value"));
|
||||
// Arduino String has isEmpty() not empty(), use length() for cross-platform compatibility
|
||||
if (value.length() == 0) { // NOLINT(readability-container-size-empty)
|
||||
request->send(409);
|
||||
return;
|
||||
}
|
||||
|
||||
parse_string_param_(request, ESPHOME_F("value"), call, &decltype(call)::set_time);
|
||||
call.set_time(value.c_str(), value.length());
|
||||
|
||||
DEFER_ACTION(call, call.perform());
|
||||
request->send(200);
|
||||
@@ -1293,12 +1294,13 @@ void WebServer::handle_datetime_request(AsyncWebServerRequest *request, const Ur
|
||||
|
||||
auto call = obj->make_call();
|
||||
|
||||
if (!request->hasParam(ESPHOME_F("value"))) {
|
||||
const auto &value = request->arg(ESPHOME_F("value"));
|
||||
// Arduino String has isEmpty() not empty(), use length() for cross-platform compatibility
|
||||
if (value.length() == 0) { // NOLINT(readability-container-size-empty)
|
||||
request->send(409);
|
||||
return;
|
||||
}
|
||||
|
||||
parse_string_param_(request, ESPHOME_F("value"), call, &decltype(call)::set_datetime);
|
||||
call.set_datetime(value.c_str(), value.length());
|
||||
|
||||
DEFER_ACTION(call, call.perform());
|
||||
request->send(200);
|
||||
@@ -1477,10 +1479,14 @@ void WebServer::handle_climate_request(AsyncWebServerRequest *request, const Url
|
||||
parse_string_param_(request, ESPHOME_F("swing_mode"), call, &decltype(call)::set_swing_mode);
|
||||
|
||||
// Parse temperature parameters
|
||||
parse_float_param_(request, ESPHOME_F("target_temperature_high"), call,
|
||||
&decltype(call)::set_target_temperature_high);
|
||||
parse_float_param_(request, ESPHOME_F("target_temperature_low"), call, &decltype(call)::set_target_temperature_low);
|
||||
parse_float_param_(request, ESPHOME_F("target_temperature"), call, &decltype(call)::set_target_temperature);
|
||||
// static_cast needed to disambiguate overloaded setters (float vs optional<float>)
|
||||
using ClimateCall = decltype(call);
|
||||
parse_num_param_(request, ESPHOME_F("target_temperature_high"), call,
|
||||
static_cast<ClimateCall &(ClimateCall::*) (float)>(&ClimateCall::set_target_temperature_high));
|
||||
parse_num_param_(request, ESPHOME_F("target_temperature_low"), call,
|
||||
static_cast<ClimateCall &(ClimateCall::*) (float)>(&ClimateCall::set_target_temperature_low));
|
||||
parse_num_param_(request, ESPHOME_F("target_temperature"), call,
|
||||
static_cast<ClimateCall &(ClimateCall::*) (float)>(&ClimateCall::set_target_temperature));
|
||||
|
||||
DEFER_ACTION(call, call.perform());
|
||||
request->send(200);
|
||||
@@ -1721,12 +1727,12 @@ void WebServer::handle_valve_request(AsyncWebServerRequest *request, const UrlMa
|
||||
}
|
||||
|
||||
auto traits = obj->get_traits();
|
||||
if (request->hasParam(ESPHOME_F("position")) && !traits.get_supports_position()) {
|
||||
if (request->hasArg(ESPHOME_F("position")) && !traits.get_supports_position()) {
|
||||
request->send(409);
|
||||
return;
|
||||
}
|
||||
|
||||
parse_float_param_(request, ESPHOME_F("position"), call, &decltype(call)::set_position);
|
||||
parse_num_param_(request, ESPHOME_F("position"), call, &decltype(call)::set_position);
|
||||
|
||||
DEFER_ACTION(call, call.perform());
|
||||
request->send(200);
|
||||
@@ -1870,12 +1876,12 @@ void WebServer::handle_water_heater_request(AsyncWebServerRequest *request, cons
|
||||
parse_string_param_(request, ESPHOME_F("mode"), base_call, &water_heater::WaterHeaterCall::set_mode);
|
||||
|
||||
// Parse temperature parameters
|
||||
parse_float_param_(request, ESPHOME_F("target_temperature"), base_call,
|
||||
&water_heater::WaterHeaterCall::set_target_temperature);
|
||||
parse_float_param_(request, ESPHOME_F("target_temperature_low"), base_call,
|
||||
&water_heater::WaterHeaterCall::set_target_temperature_low);
|
||||
parse_float_param_(request, ESPHOME_F("target_temperature_high"), base_call,
|
||||
&water_heater::WaterHeaterCall::set_target_temperature_high);
|
||||
parse_num_param_(request, ESPHOME_F("target_temperature"), base_call,
|
||||
&water_heater::WaterHeaterCall::set_target_temperature);
|
||||
parse_num_param_(request, ESPHOME_F("target_temperature_low"), base_call,
|
||||
&water_heater::WaterHeaterCall::set_target_temperature_low);
|
||||
parse_num_param_(request, ESPHOME_F("target_temperature_high"), base_call,
|
||||
&water_heater::WaterHeaterCall::set_target_temperature_high);
|
||||
|
||||
// Parse away mode parameter
|
||||
parse_bool_param_(request, ESPHOME_F("away"), base_call, &water_heater::WaterHeaterCall::set_away);
|
||||
@@ -1979,16 +1985,16 @@ void WebServer::handle_infrared_request(AsyncWebServerRequest *request, const Ur
|
||||
auto call = obj->make_call();
|
||||
|
||||
// Parse carrier frequency (optional)
|
||||
if (request->hasParam(ESPHOME_F("carrier_frequency"))) {
|
||||
auto value = parse_number<uint32_t>(request->getParam(ESPHOME_F("carrier_frequency"))->value().c_str());
|
||||
{
|
||||
auto value = parse_number<uint32_t>(request->arg(ESPHOME_F("carrier_frequency")).c_str());
|
||||
if (value.has_value()) {
|
||||
call.set_carrier_frequency(*value);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse repeat count (optional, defaults to 1)
|
||||
if (request->hasParam(ESPHOME_F("repeat_count"))) {
|
||||
auto value = parse_number<uint32_t>(request->getParam(ESPHOME_F("repeat_count"))->value().c_str());
|
||||
{
|
||||
auto value = parse_number<uint32_t>(request->arg(ESPHOME_F("repeat_count")).c_str());
|
||||
if (value.has_value()) {
|
||||
call.set_repeat_count(*value);
|
||||
}
|
||||
@@ -1996,18 +2002,12 @@ void WebServer::handle_infrared_request(AsyncWebServerRequest *request, const Ur
|
||||
|
||||
// Parse base64url-encoded raw timings (required)
|
||||
// Base64url is URL-safe: uses A-Za-z0-9-_ (no special characters needing escaping)
|
||||
if (!request->hasParam(ESPHOME_F("data"))) {
|
||||
request->send(400, ESPHOME_F("text/plain"), ESPHOME_F("Missing 'data' parameter"));
|
||||
return;
|
||||
}
|
||||
const auto &data_arg = request->arg(ESPHOME_F("data"));
|
||||
|
||||
// .c_str() is required for Arduino framework where value() returns Arduino String instead of std::string
|
||||
std::string encoded =
|
||||
request->getParam(ESPHOME_F("data"))->value().c_str(); // NOLINT(readability-redundant-string-cstr)
|
||||
|
||||
// Validate base64url is not empty
|
||||
if (encoded.empty()) {
|
||||
request->send(400, ESPHOME_F("text/plain"), ESPHOME_F("Empty 'data' parameter"));
|
||||
// Validate base64url is not empty (also catches missing parameter since arg() returns empty string)
|
||||
// Arduino String has isEmpty() not empty(), use length() for cross-platform compatibility
|
||||
if (data_arg.length() == 0) { // NOLINT(readability-container-size-empty)
|
||||
request->send(400, ESPHOME_F("text/plain"), ESPHOME_F("Missing or empty 'data' parameter"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2015,7 +2015,7 @@ void WebServer::handle_infrared_request(AsyncWebServerRequest *request, const Ur
|
||||
// it outlives the call - set_raw_timings_base64url stores a pointer, so the string
|
||||
// must remain valid until perform() completes.
|
||||
// ESP8266 also needs this because ESPAsyncWebServer callbacks run in "sys" context.
|
||||
this->defer([call, encoded = std::move(encoded)]() mutable {
|
||||
this->defer([call, encoded = std::string(data_arg.c_str(), data_arg.length())]() mutable {
|
||||
call.set_raw_timings_base64url(encoded);
|
||||
call.perform();
|
||||
});
|
||||
|
||||
@@ -513,11 +513,9 @@ class WebServer : public Controller,
|
||||
template<typename T, typename Ret>
|
||||
void parse_light_param_(AsyncWebServerRequest *request, ParamNameType param_name, T &call, Ret (T::*setter)(float),
|
||||
float scale = 1.0f) {
|
||||
if (request->hasParam(param_name)) {
|
||||
auto value = parse_number<float>(request->getParam(param_name)->value().c_str());
|
||||
if (value.has_value()) {
|
||||
(call.*setter)(*value / scale);
|
||||
}
|
||||
auto value = parse_number<float>(request->arg(param_name).c_str());
|
||||
if (value.has_value()) {
|
||||
(call.*setter)(*value / scale);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -525,34 +523,19 @@ class WebServer : public Controller,
|
||||
template<typename T, typename Ret>
|
||||
void parse_light_param_uint_(AsyncWebServerRequest *request, ParamNameType param_name, T &call,
|
||||
Ret (T::*setter)(uint32_t), uint32_t scale = 1) {
|
||||
if (request->hasParam(param_name)) {
|
||||
auto value = parse_number<uint32_t>(request->getParam(param_name)->value().c_str());
|
||||
if (value.has_value()) {
|
||||
(call.*setter)(*value * scale);
|
||||
}
|
||||
auto value = parse_number<uint32_t>(request->arg(param_name).c_str());
|
||||
if (value.has_value()) {
|
||||
(call.*setter)(*value * scale);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Generic helper to parse and apply a float parameter
|
||||
template<typename T, typename Ret>
|
||||
void parse_float_param_(AsyncWebServerRequest *request, ParamNameType param_name, T &call, Ret (T::*setter)(float)) {
|
||||
if (request->hasParam(param_name)) {
|
||||
auto value = parse_number<float>(request->getParam(param_name)->value().c_str());
|
||||
if (value.has_value()) {
|
||||
(call.*setter)(*value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generic helper to parse and apply an int parameter
|
||||
template<typename T, typename Ret>
|
||||
void parse_int_param_(AsyncWebServerRequest *request, ParamNameType param_name, T &call, Ret (T::*setter)(int)) {
|
||||
if (request->hasParam(param_name)) {
|
||||
auto value = parse_number<int>(request->getParam(param_name)->value().c_str());
|
||||
if (value.has_value()) {
|
||||
(call.*setter)(*value);
|
||||
}
|
||||
// Generic helper to parse and apply a numeric parameter
|
||||
template<typename NumT, typename T, typename Ret>
|
||||
void parse_num_param_(AsyncWebServerRequest *request, ParamNameType param_name, T &call, Ret (T::*setter)(NumT)) {
|
||||
auto value = parse_number<NumT>(request->arg(param_name).c_str());
|
||||
if (value.has_value()) {
|
||||
(call.*setter)(*value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -560,10 +543,9 @@ class WebServer : public Controller,
|
||||
template<typename T, typename Ret>
|
||||
void parse_string_param_(AsyncWebServerRequest *request, ParamNameType param_name, T &call,
|
||||
Ret (T::*setter)(const std::string &)) {
|
||||
if (request->hasParam(param_name)) {
|
||||
// .c_str() is required for Arduino framework where value() returns Arduino String instead of std::string
|
||||
std::string value = request->getParam(param_name)->value().c_str(); // NOLINT(readability-redundant-string-cstr)
|
||||
(call.*setter)(value);
|
||||
if (request->hasArg(param_name)) {
|
||||
const auto &value = request->arg(param_name);
|
||||
(call.*setter)(std::string(value.c_str(), value.length()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -573,8 +555,9 @@ class WebServer : public Controller,
|
||||
// Invalid values are ignored (setter not called)
|
||||
template<typename T, typename Ret>
|
||||
void parse_bool_param_(AsyncWebServerRequest *request, ParamNameType param_name, T &call, Ret (T::*setter)(bool)) {
|
||||
if (request->hasParam(param_name)) {
|
||||
auto param_value = request->getParam(param_name)->value();
|
||||
const auto ¶m_value = request->arg(param_name);
|
||||
// Arduino String has isEmpty() not empty(), use length() for cross-platform compatibility
|
||||
if (param_value.length() > 0) { // NOLINT(readability-container-size-empty)
|
||||
// First check on/off (default), then true/false (custom)
|
||||
auto val = parse_on_off(param_value.c_str());
|
||||
if (val == PARSE_NONE) {
|
||||
|
||||
@@ -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,32 +50,15 @@ 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);
|
||||
|
||||
// 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 {};
|
||||
}
|
||||
@@ -88,6 +67,18 @@ optional<std::string> 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 found, ESP_ERR_HTTPD_RESULT_TRUNC if found
|
||||
// but value truncated (expected with 1-byte buffer), or other errors for invalid input
|
||||
auto err = httpd_query_key_value(query_url, key, buf, sizeof(buf));
|
||||
return err == ESP_OK || err == ESP_ERR_HTTPD_RESULT_TRUNC;
|
||||
}
|
||||
|
||||
// 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++) {
|
||||
|
||||
@@ -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,6 +406,50 @@ AsyncWebParameter *AsyncWebServerRequest::getParam(const char *name) {
|
||||
return param;
|
||||
}
|
||||
|
||||
/// Search post_query then URL query with a callback.
|
||||
/// Returns first truthy result, or value-initialized default.
|
||||
/// URL query is accessed directly from req->uri (same pattern as url_to()).
|
||||
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;
|
||||
}
|
||||
}
|
||||
// Use httpd API for query length, then access string directly from URI.
|
||||
// http_parser identifies components by offset/length without modifying the URI string.
|
||||
// This is the same pattern used by url_to().
|
||||
auto len = httpd_req_get_url_query_len(req);
|
||||
if (len == 0) {
|
||||
return {};
|
||||
}
|
||||
const char *query = strchr(req->uri, '?');
|
||||
if (query == nullptr) {
|
||||
return {};
|
||||
}
|
||||
query++; // skip '?'
|
||||
return func(query, len, name);
|
||||
}
|
||||
|
||||
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);
|
||||
if (val.has_value()) {
|
||||
return std::move(val.value());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void AsyncWebServerResponse::addHeader(const char *name, const char *value) {
|
||||
httpd_resp_set_hdr(*this->req_, name, value);
|
||||
}
|
||||
|
||||
@@ -170,14 +170,8 @@ class AsyncWebServerRequest {
|
||||
AsyncWebParameter *getParam(const std::string &name) { return this->getParam(name.c_str()); }
|
||||
|
||||
// NOLINTNEXTLINE(readability-identifier-naming)
|
||||
bool hasArg(const char *name) { return this->hasParam(name); }
|
||||
std::string arg(const char *name) {
|
||||
auto *param = this->getParam(name);
|
||||
if (param) {
|
||||
return param->value();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
bool hasArg(const char *name);
|
||||
std::string arg(const char *name);
|
||||
std::string arg(const std::string &name) { return this->arg(name.c_str()); }
|
||||
|
||||
operator httpd_req_t *() const { return this->req_; }
|
||||
@@ -192,6 +186,7 @@ class AsyncWebServerRequest {
|
||||
// is faster than tree/hash overhead. AsyncWebParameter stores both name and value to avoid
|
||||
// duplicate storage. Only successful lookups are cached to prevent cache pollution when
|
||||
// handlers check for optional parameters that don't exist.
|
||||
optional<std::string> find_query_value_(const char *name) const;
|
||||
std::vector<AsyncWebParameter *> params_;
|
||||
std::string post_query_;
|
||||
AsyncWebServerRequest(httpd_req_t *req) : req_(req) {}
|
||||
|
||||
Reference in New Issue
Block a user