mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-30 06:33:51 +00:00 
			
		
		
		
	cleanup
This commit is contained in:
		| @@ -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; | ||||
| #endif | ||||
|  | ||||
|   if (content_type.has_value()) { | ||||
|     const std::string &ct = content_type.value(); | ||||
|     const char *boundary_start = nullptr; | ||||
|     size_t boundary_len = 0; | ||||
|     const char *content_type_char = content_type.value().c_str(); | ||||
|  | ||||
|     if (parse_multipart_boundary(ct.c_str(), &boundary_start, &boundary_len)) { | ||||
|       boundary.assign(boundary_start, boundary_len); | ||||
|     // 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; | ||||
|       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()); | ||||
| #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); | ||||
|     } | ||||
|   } | ||||
| #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 (!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); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user