ESPHome 2025.9.0-dev
Loading...
Searching...
No Matches
web_server_idf.cpp
Go to the documentation of this file.
1#ifdef USE_ESP_IDF
2
3#include <cstdarg>
4#include <memory>
5#include <cstring>
6#include <cctype>
7
9#include "esphome/core/log.h"
10
11#include "esp_tls_crypto.h"
12#include <freertos/FreeRTOS.h>
13#include <freertos/task.h>
14
15#include "utils.h"
16#include "web_server_idf.h"
17
18#ifdef USE_WEBSERVER_OTA
19#include <multipart_parser.h>
20#include "multipart.h" // For parse_multipart_boundary and other utils
21#endif
22
23#ifdef USE_WEBSERVER
26#endif // USE_WEBSERVER
27
28namespace esphome {
29namespace web_server_idf {
30
31#ifndef HTTPD_409
32#define HTTPD_409 "409 Conflict"
33#endif
34
35#define CRLF_STR "\r\n"
36#define CRLF_LEN (sizeof(CRLF_STR) - 1)
37
38static const char *const TAG = "web_server_idf";
39
40// Global instance to avoid guard variable (saves 8 bytes)
41// This is initialized at program startup before any threads
42namespace {
43// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
44DefaultHeaders default_headers_instance;
45} // namespace
46
47DefaultHeaders &DefaultHeaders::Instance() { return default_headers_instance; }
48
50 if (this->server_) {
51 httpd_stop(this->server_);
52 this->server_ = nullptr;
53 }
54}
55
57 if (this->server_) {
58 this->end();
59 }
60 httpd_config_t config = HTTPD_DEFAULT_CONFIG();
61 config.server_port = this->port_;
62 config.uri_match_fn = [](const char * /*unused*/, const char * /*unused*/, size_t /*unused*/) { return true; };
63 if (httpd_start(&this->server_, &config) == ESP_OK) {
64 const httpd_uri_t handler_get = {
65 .uri = "",
66 .method = HTTP_GET,
68 .user_ctx = this,
69 };
70 httpd_register_uri_handler(this->server_, &handler_get);
71
72 const httpd_uri_t handler_post = {
73 .uri = "",
74 .method = HTTP_POST,
76 .user_ctx = this,
77 };
78 httpd_register_uri_handler(this->server_, &handler_post);
79
80 const httpd_uri_t handler_options = {
81 .uri = "",
82 .method = HTTP_OPTIONS,
84 .user_ctx = this,
85 };
86 httpd_register_uri_handler(this->server_, &handler_options);
87 }
88}
89
90esp_err_t AsyncWebServer::request_post_handler(httpd_req_t *r) {
91 ESP_LOGVV(TAG, "Enter AsyncWebServer::request_post_handler. uri=%s", r->uri);
92 auto content_type = request_get_header(r, "Content-Type");
93
94 if (!request_has_header(r, "Content-Length")) {
95 ESP_LOGW(TAG, "Content length is required for post: %s", r->uri);
96 httpd_resp_send_err(r, HTTPD_411_LENGTH_REQUIRED, nullptr);
97 return ESP_OK;
98 }
99
100 if (content_type.has_value()) {
101 const char *content_type_char = content_type.value().c_str();
102
103 // Check most common case first
104 if (stristr(content_type_char, "application/x-www-form-urlencoded") != nullptr) {
105 // Normal form data - proceed with regular handling
106#ifdef USE_WEBSERVER_OTA
107 } else if (stristr(content_type_char, "multipart/form-data") != nullptr) {
108 auto *server = static_cast<AsyncWebServer *>(r->user_ctx);
109 return server->handle_multipart_upload_(r, content_type_char);
110#endif
111 } else {
112 ESP_LOGW(TAG, "Unsupported content type for POST: %s", content_type_char);
113 // fallback to get handler to support backward compatibility
115 }
116 }
117
118 // Handle regular form data
119 if (r->content_len > CONFIG_HTTPD_MAX_REQ_HDR_LEN) {
120 ESP_LOGW(TAG, "Request size is to big: %zu", r->content_len);
121 httpd_resp_send_err(r, HTTPD_400_BAD_REQUEST, nullptr);
122 return ESP_FAIL;
123 }
124
125 std::string post_query;
126 if (r->content_len > 0) {
127 post_query.resize(r->content_len);
128 const int ret = httpd_req_recv(r, &post_query[0], r->content_len + 1);
129 if (ret <= 0) { // 0 return value indicates connection closed
130 if (ret == HTTPD_SOCK_ERR_TIMEOUT) {
131 httpd_resp_send_err(r, HTTPD_408_REQ_TIMEOUT, nullptr);
132 return ESP_ERR_TIMEOUT;
133 }
134 httpd_resp_send_err(r, HTTPD_400_BAD_REQUEST, nullptr);
135 return ESP_FAIL;
136 }
137 }
138
139 AsyncWebServerRequest req(r, std::move(post_query));
140 return static_cast<AsyncWebServer *>(r->user_ctx)->request_handler_(&req);
141}
142
143esp_err_t AsyncWebServer::request_handler(httpd_req_t *r) {
144 ESP_LOGVV(TAG, "Enter AsyncWebServer::request_handler. method=%u, uri=%s", r->method, r->uri);
146 return static_cast<AsyncWebServer *>(r->user_ctx)->request_handler_(&req);
147}
148
150 for (auto *handler : this->handlers_) {
151 if (handler->canHandle(request)) {
152 // At now process only basic requests.
153 // OTA requires multipart request support and handleUpload for it
154 handler->handleRequest(request);
155 return ESP_OK;
156 }
157 }
158 if (this->on_not_found_) {
159 this->on_not_found_(request);
160 return ESP_OK;
161 }
162 return ESP_ERR_NOT_FOUND;
163}
164
166 delete this->rsp_;
167 for (const auto &pair : this->params_) {
168 delete pair.second; // NOLINT(cppcoreguidelines-owning-memory)
169 }
170}
171
172bool AsyncWebServerRequest::hasHeader(const char *name) const { return request_has_header(*this, name); }
173
175 return request_get_header(*this, name);
176}
177
178std::string AsyncWebServerRequest::url() const {
179 auto *str = strchr(this->req_->uri, '?');
180 if (str == nullptr) {
181 return this->req_->uri;
182 }
183 return std::string(this->req_->uri, str - this->req_->uri);
184}
185
186std::string AsyncWebServerRequest::host() const { return this->get_header("Host").value(); }
187
189 httpd_resp_send(*this, response->get_content_data(), response->get_content_size());
190}
191
192void AsyncWebServerRequest::send(int code, const char *content_type, const char *content) {
193 this->init_response_(nullptr, code, content_type);
194 if (content) {
195 httpd_resp_send(*this, content, HTTPD_RESP_USE_STRLEN);
196 } else {
197 httpd_resp_send(*this, nullptr, 0);
198 }
199}
200
201void AsyncWebServerRequest::redirect(const std::string &url) {
202 httpd_resp_set_status(*this, "302 Found");
203 httpd_resp_set_hdr(*this, "Location", url.c_str());
204 httpd_resp_send(*this, nullptr, 0);
205}
206
207void AsyncWebServerRequest::init_response_(AsyncWebServerResponse *rsp, int code, const char *content_type) {
208 httpd_resp_set_status(*this, code == 200 ? HTTPD_200
209 : code == 404 ? HTTPD_404
210 : code == 409 ? HTTPD_409
211 : to_string(code).c_str());
212
213 if (content_type && *content_type) {
214 httpd_resp_set_type(*this, content_type);
215 }
216 httpd_resp_set_hdr(*this, "Accept-Ranges", "none");
217
218 for (const auto &pair : DefaultHeaders::Instance().headers_) {
219 httpd_resp_set_hdr(*this, pair.first.c_str(), pair.second.c_str());
220 }
221
222 delete this->rsp_;
223 this->rsp_ = rsp;
224}
225
226#ifdef USE_WEBSERVER_AUTH
227bool AsyncWebServerRequest::authenticate(const char *username, const char *password) const {
228 if (username == nullptr || password == nullptr || *username == 0) {
229 return true;
230 }
231 auto auth = this->get_header("Authorization");
232 if (!auth.has_value()) {
233 return false;
234 }
235
236 auto *auth_str = auth.value().c_str();
237
238 const auto auth_prefix_len = sizeof("Basic ") - 1;
239 if (strncmp("Basic ", auth_str, auth_prefix_len) != 0) {
240 ESP_LOGW(TAG, "Only Basic authorization supported yet");
241 return false;
242 }
243
244 std::string user_info;
245 user_info += username;
246 user_info += ':';
247 user_info += password;
248
249 size_t n = 0, out;
250 esp_crypto_base64_encode(nullptr, 0, &n, reinterpret_cast<const uint8_t *>(user_info.c_str()), user_info.size());
251
252 auto digest = std::unique_ptr<char[]>(new char[n + 1]);
253 esp_crypto_base64_encode(reinterpret_cast<uint8_t *>(digest.get()), n, &out,
254 reinterpret_cast<const uint8_t *>(user_info.c_str()), user_info.size());
255
256 return strncmp(digest.get(), auth_str + auth_prefix_len, auth.value().size() - auth_prefix_len) == 0;
257}
258
259void AsyncWebServerRequest::requestAuthentication(const char *realm) const {
260 httpd_resp_set_hdr(*this, "Connection", "keep-alive");
261 auto auth_val = str_sprintf("Basic realm=\"%s\"", realm ? realm : "Login Required");
262 httpd_resp_set_hdr(*this, "WWW-Authenticate", auth_val.c_str());
263 httpd_resp_send_err(*this, HTTPD_401_UNAUTHORIZED, nullptr);
264}
265#endif
266
268 auto find = this->params_.find(name);
269 if (find != this->params_.end()) {
270 return find->second;
271 }
272
274 if (!val.has_value()) {
275 auto url_query = request_get_url_query(*this);
276 if (url_query.has_value()) {
277 val = query_key_value(url_query.value(), name);
278 }
279 }
280
281 AsyncWebParameter *param = nullptr;
282 if (val.has_value()) {
283 param = new AsyncWebParameter(val.value()); // NOLINT(cppcoreguidelines-owning-memory)
284 }
285 this->params_.insert({name, param});
286 return param;
287}
288
289void AsyncWebServerResponse::addHeader(const char *name, const char *value) {
290 httpd_resp_set_hdr(*this->req_, name, value);
291}
292
293void AsyncResponseStream::print(float value) { this->print(to_string(value)); }
294
295void AsyncResponseStream::printf(const char *fmt, ...) {
296 va_list args;
297
298 va_start(args, fmt);
299 const int length = vsnprintf(nullptr, 0, fmt, args);
300 va_end(args);
301
302 std::string str;
303 str.resize(length);
304
305 va_start(args, fmt);
306 vsnprintf(&str[0], length + 1, fmt, args);
307 va_end(args);
308
309 this->print(str);
310}
311
312#ifdef USE_WEBSERVER
314 for (auto *ses : this->sessions_) {
315 delete ses; // NOLINT(cppcoreguidelines-owning-memory)
316 }
317}
318
320 auto *rsp = // NOLINT(cppcoreguidelines-owning-memory)
321 new AsyncEventSourceResponse(request, this, this->web_server_);
322 if (this->on_connect_) {
323 this->on_connect_(rsp);
324 }
325 this->sessions_.insert(rsp);
326}
327
329 // Clean up dead sessions safely
330 // This follows the ESP-IDF pattern where free_ctx marks resources as dead
331 // and the main loop handles the actual cleanup to avoid race conditions
332 auto it = this->sessions_.begin();
333 while (it != this->sessions_.end()) {
334 auto *ses = *it;
335 // If the session has a dead socket (marked by destroy callback)
336 if (ses->fd_.load() == 0) {
337 ESP_LOGD(TAG, "Removing dead event source session");
338 it = this->sessions_.erase(it);
339 delete ses; // NOLINT(cppcoreguidelines-owning-memory)
340 } else {
341 ses->loop();
342 ++it;
343 }
344 }
345}
346
347void AsyncEventSource::try_send_nodefer(const char *message, const char *event, uint32_t id, uint32_t reconnect) {
348 for (auto *ses : this->sessions_) {
349 if (ses->fd_.load() != 0) { // Skip dead sessions
350 ses->try_send_nodefer(message, event, id, reconnect);
351 }
352 }
353}
354
355void AsyncEventSource::deferrable_send_state(void *source, const char *event_type,
356 message_generator_t *message_generator) {
357 for (auto *ses : this->sessions_) {
358 if (ses->fd_.load() != 0) { // Skip dead sessions
359 ses->deferrable_send_state(source, event_type, message_generator);
360 }
361 }
362}
363
367 : server_(server), web_server_(ws), entities_iterator_(new esphome::web_server::ListEntitiesIterator(ws, server)) {
368 httpd_req_t *req = *request;
369
370 httpd_resp_set_status(req, HTTPD_200);
371 httpd_resp_set_type(req, "text/event-stream");
372 httpd_resp_set_hdr(req, "Cache-Control", "no-cache");
373 httpd_resp_set_hdr(req, "Connection", "keep-alive");
374
375 for (const auto &pair : DefaultHeaders::Instance().headers_) {
376 httpd_resp_set_hdr(req, pair.first.c_str(), pair.second.c_str());
377 }
378
379 httpd_resp_send_chunk(req, CRLF_STR, CRLF_LEN);
380
381 req->sess_ctx = this;
382 req->free_ctx = AsyncEventSourceResponse::destroy;
383
384 this->hd_ = req->handle;
385 this->fd_.store(httpd_req_to_sockfd(req));
386
387 // Configure reconnect timeout and send config
388 // this should always go through since the tcp send buffer is empty on connect
389 std::string message = ws->get_config_json();
390 this->try_send_nodefer(message.c_str(), "ping", millis(), 30000);
391
392#ifdef USE_WEBSERVER_SORTING
393 for (auto &group : ws->sorting_groups_) {
394 // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson
395 message = json::build_json([group](JsonObject root) {
396 root["name"] = group.second.name;
397 root["sorting_weight"] = group.second.weight;
398 });
399 // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks)
400
401 // a (very) large number of these should be able to be queued initially without defer
402 // since the only thing in the send buffer at this point is the initial ping/config
403 this->try_send_nodefer(message.c_str(), "sorting_group");
404 }
405#endif
406
408
409 // just dump them all up-front and take advantage of the deferred queue
410 // on second thought that takes too long, but leaving the commented code here for debug purposes
411 // while(!this->entities_iterator_->completed()) {
412 // this->entities_iterator_->advance();
413 //}
414}
415
417 auto *rsp = static_cast<AsyncEventSourceResponse *>(ptr);
418 ESP_LOGD(TAG, "Event source connection closed (fd: %d)", rsp->fd_.load());
419 // Mark as dead by setting fd to 0 - will be cleaned up in the main loop
420 rsp->fd_.store(0);
421 // Note: We don't delete or remove from set here to avoid race conditions
422}
423
424// helper for allowing only unique entries in the queue
426 DeferredEvent item(source, message_generator);
427
428 // Use range-based for loop instead of std::find_if to reduce template instantiation overhead and binary size
429 for (auto &event : this->deferred_queue_) {
430 if (event == item) {
431 event = item;
432 return;
433 }
434 }
435 this->deferred_queue_.push_back(item);
436}
437
439 while (!deferred_queue_.empty()) {
440 DeferredEvent &de = deferred_queue_.front();
441 std::string message = de.message_generator_(web_server_, de.source_);
442 if (this->try_send_nodefer(message.c_str(), "state")) {
443 // O(n) but memory efficiency is more important than speed here which is why std::vector was chosen
444 deferred_queue_.erase(deferred_queue_.begin());
445 } else {
446 break;
447 }
448 }
449}
450
452 if (event_buffer_.empty()) {
453 return;
454 }
455 if (event_bytes_sent_ == event_buffer_.size()) {
456 event_buffer_.resize(0);
458 return;
459 }
460
461 int bytes_sent = httpd_socket_send(this->hd_, this->fd_.load(), event_buffer_.c_str() + event_bytes_sent_,
462 event_buffer_.size() - event_bytes_sent_, 0);
463 if (bytes_sent == HTTPD_SOCK_ERR_TIMEOUT || bytes_sent == HTTPD_SOCK_ERR_FAIL) {
464 // Socket error - just return, the connection will be closed by httpd
465 // and our destroy callback will be called
466 return;
467 }
468 event_bytes_sent_ += bytes_sent;
469
470 if (event_bytes_sent_ == event_buffer_.size()) {
471 event_buffer_.resize(0);
473 }
474}
475
482
483bool AsyncEventSourceResponse::try_send_nodefer(const char *message, const char *event, uint32_t id,
484 uint32_t reconnect) {
485 if (this->fd_.load() == 0) {
486 return false;
487 }
488
490 if (!event_buffer_.empty()) {
491 // there is still pending event data to send first
492 return false;
493 }
494
495 // 8 spaces are standing in for the hexidecimal chunk length to print later
496 const char chunk_len_header[] = " " CRLF_STR;
497 const int chunk_len_header_len = sizeof(chunk_len_header) - 1;
498
499 event_buffer_.append(chunk_len_header);
500
501 if (reconnect) {
502 event_buffer_.append("retry: ", sizeof("retry: ") - 1);
503 event_buffer_.append(to_string(reconnect));
504 event_buffer_.append(CRLF_STR, CRLF_LEN);
505 }
506
507 if (id) {
508 event_buffer_.append("id: ", sizeof("id: ") - 1);
509 event_buffer_.append(to_string(id));
510 event_buffer_.append(CRLF_STR, CRLF_LEN);
511 }
512
513 if (event && *event) {
514 event_buffer_.append("event: ", sizeof("event: ") - 1);
515 event_buffer_.append(event);
516 event_buffer_.append(CRLF_STR, CRLF_LEN);
517 }
518
519 if (message && *message) {
520 event_buffer_.append("data: ", sizeof("data: ") - 1);
521 event_buffer_.append(message);
522 event_buffer_.append(CRLF_STR, CRLF_LEN);
523 }
524
525 if (event_buffer_.empty()) {
526 return true;
527 }
528
529 event_buffer_.append(CRLF_STR, CRLF_LEN);
530 event_buffer_.append(CRLF_STR, CRLF_LEN);
531
532 // chunk length header itself and the final chunk terminating CRLF are not counted as part of the chunk
533 int chunk_len = event_buffer_.size() - CRLF_LEN - chunk_len_header_len;
534 char chunk_len_str[9];
535 snprintf(chunk_len_str, 9, "%08x", chunk_len);
536 std::memcpy(&event_buffer_[0], chunk_len_str, 8);
537
540
541 return true;
542}
543
544void AsyncEventSourceResponse::deferrable_send_state(void *source, const char *event_type,
545 message_generator_t *message_generator) {
546 // allow all json "details_all" to go through before publishing bare state events, this avoids unnamed entries showing
547 // up in the web GUI and reduces event load during initial connect
548 if (!entities_iterator_->completed() && 0 != strcmp(event_type, "state_detail_all"))
549 return;
550
551 if (source == nullptr)
552 return;
553 if (event_type == nullptr)
554 return;
555 if (message_generator == nullptr)
556 return;
557
558 if (0 != strcmp(event_type, "state_detail_all") && 0 != strcmp(event_type, "state")) {
559 ESP_LOGE(TAG, "Can't defer non-state event");
560 }
561
564
565 if (!event_buffer_.empty() || !deferred_queue_.empty()) {
566 // outgoing event buffer or deferred queue still not empty which means downstream tcp send buffer full, no point
567 // trying to send first
568 deq_push_back_with_dedup_(source, message_generator);
569 } else {
570 std::string message = message_generator(web_server_, source);
571 if (!this->try_send_nodefer(message.c_str(), "state")) {
572 deq_push_back_with_dedup_(source, message_generator);
573 }
574 }
575}
576#endif
577
578#ifdef USE_WEBSERVER_OTA
579esp_err_t AsyncWebServer::handle_multipart_upload_(httpd_req_t *r, const char *content_type) {
580 static constexpr size_t MULTIPART_CHUNK_SIZE = 1460; // Match Arduino AsyncWebServer buffer size
581 static constexpr size_t YIELD_INTERVAL_BYTES = 16 * 1024; // Yield every 16KB to prevent watchdog
582
583 // Parse boundary and create reader
584 const char *boundary_start;
585 size_t boundary_len;
586 if (!parse_multipart_boundary(content_type, &boundary_start, &boundary_len)) {
587 ESP_LOGE(TAG, "Failed to parse multipart boundary");
588 httpd_resp_send_err(r, HTTPD_400_BAD_REQUEST, nullptr);
589 return ESP_FAIL;
590 }
591
593 AsyncWebHandler *handler = nullptr;
594 for (auto *h : this->handlers_) {
595 if (h->canHandle(&req)) {
596 handler = h;
597 break;
598 }
599 }
600
601 if (!handler) {
602 ESP_LOGW(TAG, "No handler found for OTA request");
603 httpd_resp_send_err(r, HTTPD_404_NOT_FOUND, nullptr);
604 return ESP_OK;
605 }
606
607 // Upload state
608 std::string filename;
609 size_t index = 0;
610 // Create reader on heap to reduce stack usage
611 auto reader = std::make_unique<MultipartReader>("--" + std::string(boundary_start, boundary_len));
612
613 // Configure callbacks
614 reader->set_data_callback([&](const uint8_t *data, size_t len) {
615 if (!reader->has_file() || !len)
616 return;
617
618 if (filename.empty()) {
619 filename = reader->get_current_part().filename;
620 ESP_LOGV(TAG, "Processing file: '%s'", filename.c_str());
621 handler->handleUpload(&req, filename, 0, nullptr, 0, false); // Start
622 }
623
624 handler->handleUpload(&req, filename, index, const_cast<uint8_t *>(data), len, false);
625 index += len;
626 });
627
628 reader->set_part_complete_callback([&]() {
629 if (index > 0) {
630 handler->handleUpload(&req, filename, index, nullptr, 0, true); // End
631 filename.clear();
632 index = 0;
633 }
634 });
635
636 // Process data
637 std::unique_ptr<char[]> buffer(new char[MULTIPART_CHUNK_SIZE]);
638 size_t bytes_since_yield = 0;
639
640 for (size_t remaining = r->content_len; remaining > 0;) {
641 int recv_len = httpd_req_recv(r, buffer.get(), std::min(remaining, MULTIPART_CHUNK_SIZE));
642
643 if (recv_len <= 0) {
644 httpd_resp_send_err(r, recv_len == HTTPD_SOCK_ERR_TIMEOUT ? HTTPD_408_REQ_TIMEOUT : HTTPD_400_BAD_REQUEST,
645 nullptr);
646 return recv_len == HTTPD_SOCK_ERR_TIMEOUT ? ESP_ERR_TIMEOUT : ESP_FAIL;
647 }
648
649 if (reader->parse(buffer.get(), recv_len) != static_cast<size_t>(recv_len)) {
650 ESP_LOGW(TAG, "Multipart parser error");
651 httpd_resp_send_err(r, HTTPD_400_BAD_REQUEST, nullptr);
652 return ESP_FAIL;
653 }
654
655 remaining -= recv_len;
656 bytes_since_yield += recv_len;
657
658 if (bytes_since_yield > YIELD_INTERVAL_BYTES) {
659 vTaskDelay(1);
660 bytes_since_yield = 0;
661 }
662 }
663
664 handler->handleRequest(&req);
665 return ESP_OK;
666}
667#endif // USE_WEBSERVER_OTA
668
669} // namespace web_server_idf
670} // namespace esphome
671
672#endif // !defined(USE_ESP_IDF)
uint8_t h
Definition bl0906.h:2
void begin(bool include_internal=false)
value_type const & value() const
Definition optional.h:94
This class allows users to create a web server with their ESP nodes.
Definition web_server.h:166
std::string get_config_json()
Return the webserver configuration as JSON.
std::map< uint64_t, SortingGroup > sorting_groups_
Definition web_server.h:494
std::set< AsyncEventSourceResponse * > sessions_
void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator)
esphome::web_server::WebServer * web_server_
void try_send_nodefer(const char *message, const char *event=nullptr, uint32_t id=0, uint32_t reconnect=0)
void handleRequest(AsyncWebServerRequest *request) override
void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator)
esphome::web_server::WebServer * web_server_
void deq_push_back_with_dedup_(void *source, message_generator_t *message_generator)
AsyncEventSourceResponse(const AsyncWebServerRequest *request, esphome::web_server_idf::AsyncEventSource *server, esphome::web_server::WebServer *ws)
std::unique_ptr< esphome::web_server::ListEntitiesIterator > entities_iterator_
bool try_send_nodefer(const char *message, const char *event=nullptr, uint32_t id=0, uint32_t reconnect=0)
void printf(const char *fmt,...) __attribute__((format(printf
virtual void handleRequest(AsyncWebServerRequest *request)
virtual void handleUpload(AsyncWebServerRequest *request, const std::string &filename, size_t index, uint8_t *data, size_t len, bool final)
std::function< void(AsyncWebServerRequest *request)> on_not_found_
static esp_err_t request_post_handler(httpd_req_t *r)
std::vector< AsyncWebHandler * > handlers_
esp_err_t request_handler_(AsyncWebServerRequest *request) const
esp_err_t handle_multipart_upload_(httpd_req_t *r, const char *content_type)
static esp_err_t request_handler(httpd_req_t *r)
AsyncWebParameter * getParam(const std::string &name)
optional< std::string > get_header(const char *name) const
void send(AsyncWebServerResponse *response)
std::map< std::string, AsyncWebParameter * > params_
void init_response_(AsyncWebServerResponse *rsp, int code, const char *content_type)
void requestAuthentication(const char *realm=nullptr) const
bool authenticate(const char *username, const char *password) const
virtual const char * get_content_data() const =0
void addHeader(const char *name, const char *value)
mopeka_std_values val[4]
std::string build_json(const json_build_t &f)
Build a JSON string with the provided json build function.
Definition json_util.cpp:33
const char *const TAG
Definition spi.cpp:8
optional< std::string > request_get_url_query(httpd_req_t *req)
Definition utils.cpp:56
optional< std::string > request_get_header(httpd_req_t *req, const char *name)
Definition utils.cpp:39
bool parse_multipart_boundary(const char *content_type, const char **boundary_start, size_t *boundary_len)
std::string(esphome::web_server::WebServer *, void *) message_generator_t
optional< std::string > query_key_value(const std::string &query_url, const std::string &key)
Definition utils.cpp:74
const char * stristr(const char *haystack, const char *needle)
Definition utils.cpp:104
bool request_has_header(httpd_req_t *req, const char *name)
Definition utils.cpp:37
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string size_t len
Definition helpers.h:279
std::string str_sprintf(const char *fmt,...)
Definition helpers.cpp:208
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:28
std::string print()
uint16_t length
Definition tt21100.cpp:0