mirror of
https://github.com/esphome/esphome.git
synced 2025-08-07 02:47:47 +00:00
cleanup
This commit is contained in:
parent
ed2c3e626b
commit
d065f4ae62
@ -1,21 +1,12 @@
|
||||
#include "esphome/core/defines.h"
|
||||
#if defined(USE_ESP_IDF) && defined(USE_WEBSERVER_OTA)
|
||||
#include "multipart_parser_utils.h"
|
||||
#include "parser_utils.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome {
|
||||
namespace web_server_idf {
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Case-insensitive string prefix check
|
||||
bool str_startswith_case_insensitive(const std::string &str, const std::string &prefix) {
|
||||
if (str.length() < prefix.length()) {
|
||||
@ -108,26 +99,6 @@ std::string extract_header_param(const std::string &header, const std::string &p
|
||||
return "";
|
||||
}
|
||||
|
||||
// Case-insensitive string search (like strstr but case-insensitive)
|
||||
const char *stristr(const char *haystack, const char *needle) {
|
||||
if (!haystack || !needle) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t needle_len = strlen(needle);
|
||||
if (needle_len == 0) {
|
||||
return haystack;
|
||||
}
|
||||
|
||||
for (const char *p = haystack; *p; p++) {
|
||||
if (str_ncmp_ci(p, needle, needle_len)) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Parse boundary from Content-Type header
|
||||
// Returns true if boundary found, false otherwise
|
||||
// boundary_start and boundary_len will point to the boundary value
|
||||
@ -188,15 +159,6 @@ bool parse_multipart_boundary(const char *content_type, const char **boundary_st
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if content type is form-urlencoded (case-insensitive)
|
||||
bool is_form_urlencoded(const char *content_type) {
|
||||
if (!content_type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return stristr(content_type, "application/x-www-form-urlencoded") != nullptr;
|
||||
}
|
||||
|
||||
// Trim whitespace from both ends of a string
|
||||
std::string str_trim(const std::string &str) {
|
||||
size_t start = str.find_first_not_of(" \t\r\n");
|
||||
|
@ -9,12 +9,6 @@
|
||||
namespace esphome {
|
||||
namespace web_server_idf {
|
||||
|
||||
// Helper function for case-insensitive character comparison
|
||||
inline bool char_equals_ci(char a, char b) { return ::tolower(a) == ::tolower(b); }
|
||||
|
||||
// Helper function for case-insensitive string region comparison
|
||||
bool str_ncmp_ci(const char *s1, const char *s2, size_t n);
|
||||
|
||||
// Case-insensitive string prefix check
|
||||
bool str_startswith_case_insensitive(const std::string &str, const std::string &prefix);
|
||||
|
||||
@ -25,17 +19,11 @@ size_t str_find_case_insensitive(const std::string &haystack, const std::string
|
||||
// Handles both quoted and unquoted values
|
||||
std::string extract_header_param(const std::string &header, const std::string ¶m);
|
||||
|
||||
// Case-insensitive string search (like strstr but case-insensitive)
|
||||
const char *stristr(const char *haystack, const char *needle);
|
||||
|
||||
// Parse boundary from Content-Type header
|
||||
// Returns true if boundary found, false otherwise
|
||||
// boundary_start and boundary_len will point to the boundary value
|
||||
bool parse_multipart_boundary(const char *content_type, const char **boundary_start, size_t *boundary_len);
|
||||
|
||||
// Check if content type is form-urlencoded (case-insensitive)
|
||||
bool is_form_urlencoded(const char *content_type);
|
||||
|
||||
// Trim whitespace from both ends of a string
|
||||
std::string str_trim(const std::string &str);
|
||||
|
||||
|
51
esphome/components/web_server_idf/parser_utils.cpp
Normal file
51
esphome/components/web_server_idf/parser_utils.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
#include "esphome/core/defines.h"
|
||||
#ifdef USE_ESP_IDF
|
||||
#include "parser_utils.h"
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
|
||||
namespace esphome {
|
||||
namespace web_server_idf {
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Case-insensitive string search (like strstr but case-insensitive)
|
||||
const char *stristr(const char *haystack, const char *needle) {
|
||||
if (!haystack || !needle) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t needle_len = strlen(needle);
|
||||
if (needle_len == 0) {
|
||||
return haystack;
|
||||
}
|
||||
|
||||
for (const char *p = haystack; *p; p++) {
|
||||
if (str_ncmp_ci(p, needle, needle_len)) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Check if content type is form-urlencoded (case-insensitive)
|
||||
bool is_form_urlencoded(const char *content_type) {
|
||||
if (!content_type) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return stristr(content_type, "application/x-www-form-urlencoded") != nullptr;
|
||||
}
|
||||
|
||||
} // namespace web_server_idf
|
||||
} // namespace esphome
|
||||
#endif // USE_ESP_IDF
|
24
esphome/components/web_server_idf/parser_utils.h
Normal file
24
esphome/components/web_server_idf/parser_utils.h
Normal file
@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include "esphome/core/defines.h"
|
||||
#ifdef USE_ESP_IDF
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace esphome {
|
||||
namespace web_server_idf {
|
||||
|
||||
// Helper function for case-insensitive character comparison
|
||||
inline bool char_equals_ci(char a, char b) { return ::tolower(a) == ::tolower(b); }
|
||||
|
||||
// Helper function for case-insensitive string region comparison
|
||||
bool str_ncmp_ci(const char *s1, const char *s2, size_t n);
|
||||
|
||||
// Case-insensitive string search (like strstr but case-insensitive)
|
||||
const char *stristr(const char *haystack, const char *needle);
|
||||
|
||||
// Check if content type is form-urlencoded (case-insensitive)
|
||||
bool is_form_urlencoded(const char *content_type);
|
||||
|
||||
} // namespace web_server_idf
|
||||
} // namespace esphome
|
||||
#endif // USE_ESP_IDF
|
@ -2,6 +2,8 @@
|
||||
|
||||
#include <cstdarg>
|
||||
#include <memory>
|
||||
#include <cstring>
|
||||
#include <cctype>
|
||||
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
@ -12,6 +14,7 @@
|
||||
|
||||
#include "utils.h"
|
||||
#include "web_server_idf.h"
|
||||
#include "parser_utils.h"
|
||||
|
||||
#ifdef USE_WEBSERVER_OTA
|
||||
#include "multipart_reader.h"
|
||||
@ -88,40 +91,46 @@ esp_err_t AsyncWebServer::request_post_handler(httpd_req_t *r) {
|
||||
#ifdef USE_WEBSERVER_OTA
|
||||
// Check if this is a multipart form data request (for OTA updates)
|
||||
bool is_multipart = false;
|
||||
std::string boundary;
|
||||
|
||||
if (content_type.has_value()) {
|
||||
const std::string &ct = content_type.value();
|
||||
const char *boundary_start = nullptr;
|
||||
size_t boundary_len = 0;
|
||||
|
||||
if (parse_multipart_boundary(ct.c_str(), &boundary_start, &boundary_len)) {
|
||||
boundary.assign(boundary_start, boundary_len);
|
||||
is_multipart = true;
|
||||
ESP_LOGV(TAG, "Multipart upload detected, boundary: '%s' (len: %zu)", boundary.c_str(), boundary_len);
|
||||
} else if (!is_form_urlencoded(ct.c_str())) {
|
||||
ESP_LOGW(TAG, "Unsupported content type for POST: %s", ct.c_str());
|
||||
// fallback to get handler to support backward compatibility
|
||||
return AsyncWebServer::request_handler(r);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (content_type.has_value() && content_type.value() != "application/x-www-form-urlencoded") {
|
||||
ESP_LOGW(TAG, "Only application/x-www-form-urlencoded supported for POST request");
|
||||
// fallback to get handler to support backward compatibility
|
||||
return AsyncWebServer::request_handler(r);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (content_type.has_value()) {
|
||||
const char *content_type_char = content_type.value().c_str();
|
||||
|
||||
// Check most common case first
|
||||
if (is_form_urlencoded(content_type_char)) {
|
||||
// Normal form data - proceed with regular handling
|
||||
#ifdef USE_WEBSERVER_OTA
|
||||
} else if (stristr(content_type_char, "multipart/form-data") != nullptr) {
|
||||
is_multipart = true;
|
||||
#endif
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Unsupported content type for POST: %s", content_type_char);
|
||||
// fallback to get handler to support backward compatibility
|
||||
return AsyncWebServer::request_handler(r);
|
||||
}
|
||||
}
|
||||
|
||||
if (!request_has_header(r, "Content-Length")) {
|
||||
ESP_LOGW(TAG, "Content length is requred for post: %s", r->uri);
|
||||
ESP_LOGW(TAG, "Content length is required for post: %s", r->uri);
|
||||
httpd_resp_send_err(r, HTTPD_411_LENGTH_REQUIRED, nullptr);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#ifdef USE_WEBSERVER_OTA
|
||||
// Handle multipart form data
|
||||
if (is_multipart && !boundary.empty()) {
|
||||
if (is_multipart) {
|
||||
// Parse the boundary from the content type
|
||||
const char *boundary_start = nullptr;
|
||||
size_t boundary_len = 0;
|
||||
|
||||
if (!parse_multipart_boundary(content_type.value().c_str(), &boundary_start, &boundary_len)) {
|
||||
ESP_LOGE(TAG, "Failed to parse multipart boundary");
|
||||
httpd_resp_send_err(r, HTTPD_400_BAD_REQUEST, nullptr);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
std::string boundary(boundary_start, boundary_len);
|
||||
ESP_LOGV(TAG, "Multipart upload boundary: '%s'", boundary.c_str());
|
||||
// Create request object
|
||||
AsyncWebServerRequest req(r);
|
||||
auto *server = static_cast<AsyncWebServer *>(r->user_ctx);
|
||||
|
Loading…
x
Reference in New Issue
Block a user