15#include <mbedtls/base64.h>
16#include <freertos/FreeRTOS.h>
17#include <freertos/task.h>
23#ifdef USE_WEBSERVER_AUTH_DIGEST
24#include <esp_random.h>
25#include <esp_rom_md5.h>
28#ifdef USE_WEBSERVER_OTA
29#include <multipart_parser.h>
40#include <sys/socket.h>
46#define HTTPD_401 "401 Unauthorized"
49#define HTTPD_409 "409 Conflict"
52#define HTTPD_422 "422 Unprocessable Entity"
55#define CRLF_STR "\r\n"
56#define CRLF_LEN (sizeof(CRLF_STR) - 1)
62#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 14 && defined(__OPTIMIZE_SIZE__)
63#pragma GCC diagnostic error "-Wstack-usage=2048"
68static constexpr size_t RECV_CHUNK_SIZE = 1460;
69static constexpr size_t YIELD_INTERVAL_BYTES = 16 * 1024;
96[[maybe_unused]]
int nonblocking_send(httpd_handle_t hd,
int sockfd,
const char *buf,
size_t buf_len,
int flags) {
98 return HTTPD_SOCK_ERR_INVALID;
102 int ret = send(sockfd, buf, buf_len,
flags | MSG_DONTWAIT);
104 const int err = errno;
105 if (err == EAGAIN || err == EWOULDBLOCK) {
107 return HTTPD_SOCK_ERR_TIMEOUT;
110 ESP_LOGD(TAG,
"send error: errno %d", err);
111 return HTTPD_SOCK_ERR_FAIL;
134 shutdown(sockfd, SHUT_RD);
152 httpd_config_t config = HTTPD_DEFAULT_CONFIG();
153 config.stack_size = 4512;
154 config.server_port = this->
port_;
155 config.uri_match_fn = [](
const char * ,
const char * ,
size_t ) {
return true; };
160 config.lru_purge_enable =
true;
163 if (httpd_start(&this->
server_, &config) == ESP_OK) {
164 const httpd_uri_t handler_get = {
170 httpd_register_uri_handler(this->
server_, &handler_get);
172 const httpd_uri_t handler_post = {
178 httpd_register_uri_handler(this->
server_, &handler_post);
180 const httpd_uri_t handler_options = {
182 .method = HTTP_OPTIONS,
186 httpd_register_uri_handler(this->
server_, &handler_options);
191 ESP_LOGVV(TAG,
"Enter AsyncWebServer::request_post_handler. uri=%s", r->uri);
195 ESP_LOGW(TAG,
"Content length is required for post: %s", r->uri);
196 httpd_resp_send_err(r, HTTPD_411_LENGTH_REQUIRED,
nullptr);
200 if (content_type.has_value()) {
201 const char *content_type_char = content_type.value().c_str();
204 size_t content_type_len = strlen(content_type_char);
205 if (
strcasestr_n(content_type_char, content_type_len,
"application/x-www-form-urlencoded") !=
nullptr) {
207#ifdef USE_WEBSERVER_OTA
208 }
else if (
strcasestr_n(content_type_char, content_type_len,
"multipart/form-data") !=
nullptr) {
221 if (r->content_len > CONFIG_HTTPD_MAX_REQ_HDR_LEN) {
222 ESP_LOGW(TAG,
"Request size is to big: %zu", r->content_len);
223 httpd_resp_send_err(r, HTTPD_400_BAD_REQUEST,
nullptr);
227 std::string post_query;
228 if (r->content_len > 0) {
229 post_query.resize(r->content_len);
230 const int ret = httpd_req_recv(r, &post_query[0], r->content_len + 1);
232 if (
ret == HTTPD_SOCK_ERR_TIMEOUT) {
233 httpd_resp_send_err(r, HTTPD_408_REQ_TIMEOUT,
nullptr);
234 return ESP_ERR_TIMEOUT;
236 httpd_resp_send_err(r, HTTPD_400_BAD_REQUEST,
nullptr);
246 ESP_LOGVV(TAG,
"Enter AsyncWebServer::request_handler. method=%u, uri=%s", r->method, r->uri);
253 if (handler->canHandle(request)) {
256 handler->handleRequest(request);
264 return ESP_ERR_NOT_FOUND;
271 if (
h->canHandle(&req)) {
277 if (handler ==
nullptr) {
278 ESP_LOGW(TAG,
"Unsupported content type for POST: %s", content_type);
283 const size_t total = r->content_len;
285 auto buffer = std::make_unique_for_overwrite<char[]>(RECV_CHUNK_SIZE);
286 size_t bytes_since_yield = 0;
288 for (
size_t index = 0; index < total;) {
289 int recv_len = httpd_req_recv(r,
buffer.get(), std::min(total - index, RECV_CHUNK_SIZE));
292 httpd_resp_send_err(r, recv_len == HTTPD_SOCK_ERR_TIMEOUT ? HTTPD_408_REQ_TIMEOUT : HTTPD_400_BAD_REQUEST,
294 return recv_len == HTTPD_SOCK_ERR_TIMEOUT ? ESP_ERR_TIMEOUT : ESP_FAIL;
297 handler->
handleBody(&req,
reinterpret_cast<uint8_t *
>(
buffer.get()), recv_len, index, total);
299 bytes_since_yield += recv_len;
301 if (bytes_since_yield > YIELD_INTERVAL_BYTES) {
303 bytes_since_yield = 0;
313#ifdef USE_WEBSERVER_OTA
318 for (
auto *param : this->
params_) {
330 const char *uri = this->
req_->uri;
331 const char *query_start = strchr(uri,
'?');
332 size_t uri_len = query_start ?
static_cast<size_t>(query_start - uri) : strlen(uri);
334 memcpy(
buffer.data(), uri, copy_len);
342 httpd_resp_set_status(*
this,
"302 Found");
343 httpd_resp_set_hdr(*
this,
"Location", url);
344 httpd_resp_set_hdr(*
this,
"Connection",
"close");
345 httpd_resp_send(*
this,
nullptr, 0);
377 httpd_resp_set_status(*
this,
status);
379 if (content_type && *content_type) {
380 httpd_resp_set_type(*
this, content_type);
382 httpd_resp_set_hdr(*
this,
"Accept-Ranges",
"none");
385 httpd_resp_set_hdr(*
this, header.name, header.value);
392#ifdef USE_WEBSERVER_AUTH
394#ifdef USE_WEBSERVER_AUTH_DIGEST
401 size_t key_len = strlen(
key);
402 const char *base = params.
c_str();
403 size_t n = params.
size();
406 while (i < n && (base[i] ==
' ' || base[i] ==
','))
408 size_t name_start = i;
409 while (i < n && base[i] !=
'=' && base[i] !=
',')
411 if (i >= n || base[i] ==
',')
413 size_t name_len = i - name_start;
414 while (name_len > 0 && base[name_start + name_len - 1] ==
' ')
417 const char *val_start;
419 if (i < n && base[i] ==
'"') {
421 val_start = base + i;
422 while (i < n && base[i] !=
'"')
424 val_len = (base + i) - val_start;
428 val_start = base + i;
429 while (i < n && base[i] !=
',')
431 val_len = (base + i) - val_start;
433 if (name_len == key_len && memcmp(base + name_start,
key, key_len) == 0)
434 return StringRef(val_start, val_len);
435 while (i < n && base[i] !=
',')
443bool check_digest_auth(
const char *username,
const char *password,
const std::string &header,
const char *method) {
444 const size_t prefix_len =
sizeof(
"Digest ") - 1;
445 StringRef params(header.c_str() + prefix_len, header.size() - prefix_len);
447 if (digest_param(params,
"username") != username)
450 StringRef realm = digest_param(params,
"realm");
451 StringRef nonce = digest_param(params,
"nonce");
452 StringRef uri = digest_param(params,
"uri");
453 StringRef qop = digest_param(params,
"qop");
454 StringRef nc = digest_param(params,
"nc");
455 StringRef cnonce = digest_param(params,
"cnonce");
456 StringRef response = digest_param(params,
"response");
457 if (response.size() != 32)
467 esp_rom_md5_init(&ctx);
468 esp_rom_md5_update(&ctx, username, strlen(username));
469 esp_rom_md5_update(&ctx,
":", 1);
470 esp_rom_md5_update(&ctx, realm.c_str(), realm.size());
471 esp_rom_md5_update(&ctx,
":", 1);
472 esp_rom_md5_update(&ctx, password, strlen(password));
473 esp_rom_md5_final(digest, &ctx);
478 esp_rom_md5_init(&ctx);
479 esp_rom_md5_update(&ctx, method, strlen(method));
480 esp_rom_md5_update(&ctx,
":", 1);
481 esp_rom_md5_update(&ctx, uri.c_str(), uri.size());
482 esp_rom_md5_final(digest, &ctx);
487 esp_rom_md5_init(&ctx);
488 esp_rom_md5_update(&ctx, ha1, 32);
489 esp_rom_md5_update(&ctx,
":", 1);
490 esp_rom_md5_update(&ctx, nonce.c_str(), nonce.size());
491 esp_rom_md5_update(&ctx,
":", 1);
492 esp_rom_md5_update(&ctx, nc.c_str(), nc.size());
493 esp_rom_md5_update(&ctx,
":", 1);
494 esp_rom_md5_update(&ctx, cnonce.c_str(), cnonce.size());
495 esp_rom_md5_update(&ctx,
":", 1);
496 esp_rom_md5_update(&ctx, qop.c_str(), qop.size());
497 esp_rom_md5_update(&ctx,
":", 1);
498 esp_rom_md5_update(&ctx, ha2, 32);
499 esp_rom_md5_final(digest, &ctx);
504 for (
size_t i = 0; i < 32; i++)
505 result |=
static_cast<uint8_t
>(expected[i] ^ response[i]);
513 if (username ==
nullptr || password ==
nullptr || *username == 0) {
516 auto auth = this->
get_header(
"Authorization");
517 if (!auth.has_value()) {
521 auto *auth_str = auth.value().c_str();
523#ifdef USE_WEBSERVER_AUTH_DIGEST
525 const auto auth_prefix_len =
sizeof(
"Digest ") - 1;
526 if (strncmp(
"Digest ", auth_str, auth_prefix_len) != 0) {
527 ESP_LOGW(TAG,
"Only Digest authorization supported");
530 return check_digest_auth(username, password, auth.value(), http_method_str(this->
method()));
532 const auto auth_prefix_len =
sizeof(
"Basic ") - 1;
533 if (strncmp(
"Basic ", auth_str, auth_prefix_len) != 0) {
534 ESP_LOGW(TAG,
"Only Basic authorization supported");
539 constexpr size_t max_user_info_len = 256;
540 char user_info[max_user_info_len];
541 size_t user_len = strlen(username);
542 size_t pass_len = strlen(password);
543 size_t user_info_len = user_len + 1 + pass_len;
545 if (user_info_len >= max_user_info_len) {
546 ESP_LOGW(TAG,
"Credentials too long for authentication");
550 memcpy(user_info, username, user_len);
551 user_info[user_len] =
':';
552 memcpy(user_info + user_len + 1, password, pass_len);
553 user_info[user_info_len] =
'\0';
557 constexpr size_t max_digest_len = 350;
558 char digest[max_digest_len];
562 if (mbedtls_base64_encode(
reinterpret_cast<uint8_t *
>(digest), max_digest_len, &
out,
563 reinterpret_cast<const uint8_t *
>(user_info), user_info_len) != 0) {
570 const char *provided = auth_str + auth_prefix_len;
571 size_t digest_len =
out;
574 size_t provided_len = auth.value().size() - auth_prefix_len;
578 volatile size_t result = digest_len ^ provided_len;
582 for (
size_t i = 0; i < digest_len; i++) {
583 char provided_ch = (i < provided_len) ? provided[i] : 0;
584 result |=
static_cast<uint8_t
>(digest[i] ^ provided_ch);
591 httpd_resp_set_hdr(*
this,
"Connection",
"keep-alive");
592#ifdef USE_WEBSERVER_AUTH_DIGEST
605 snprintf(header,
sizeof(header), R
"(Digest realm="Login Required", qop="auth", nonce="%s", opaque="%s")", nonce,
607 httpd_resp_set_hdr(*this,
"WWW-Authenticate", header);
609 httpd_resp_set_hdr(*
this,
"WWW-Authenticate",
"Basic realm=\"Login Required\"");
611 httpd_resp_send_err(*
this, HTTPD_401_UNAUTHORIZED,
nullptr);
617 for (
auto *param : this->
params_) {
618 if (param->name() == name) {
628 if (!
val.has_value()) {
633 this->params_.push_back(param);
640template<
typename Func>
641static auto search_query_sources(httpd_req_t *req,
const std::string &post_query,
const char *name, Func func)
642 ->
decltype(func(
nullptr,
size_t{0}, name)) {
643 if (!post_query.empty()) {
644 auto result = func(post_query.c_str(), post_query.size(), name);
652 auto len = httpd_req_get_url_query_len(req);
656 const char *query = strchr(req->uri,
'?');
657 if (query ==
nullptr) {
661 return func(query,
len, name);
675 if (
val.has_value()) {
676 return std::move(
val.value());
682 httpd_resp_set_hdr(*this->
req_, name, value);
689 int len = snprintf(buf,
sizeof(buf),
"%f", value);
697 const int length = vsnprintf(
nullptr, 0,
fmt, args);
704 vsnprintf(&str[0],
length + 1,
fmt, args);
714 for (
auto *ses : *vec) {
739 this->adopt_pending_sessions_main_loop_();
745 for (
size_t i = 0; i < this->
sessions_.size();) {
748 if (ses->safe_to_delete_()) {
763void AsyncEventSource::adopt_pending_sessions_main_loop_() {
764 std::vector<AsyncEventSourceResponse *> incoming;
770 for (
auto *rsp : incoming) {
772 if (rsp->safe_to_delete_()) {
778 if (httpd_sess_get_ctx(rsp->hd_, rsp->fd_.load()) != rsp) {
785 rsp->start_session_main_loop_();
793 if (ses->fd_.load() != 0) {
794 ses->try_send_nodefer(
message, message_len, event,
id, reconnect);
804 if (ses->fd_.load() != 0) {
805 ses->deferrable_send_state(source, detail_all, message_generator);
813 : server_(server), web_server_(ws), entities_iterator_(ws, server) {
815 httpd_req_t *req = *request;
817 httpd_resp_set_status(req, HTTPD_200);
818 httpd_resp_set_type(req,
"text/event-stream");
819 httpd_resp_set_hdr(req,
"Cache-Control",
"no-cache");
820 httpd_resp_set_hdr(req,
"Connection",
"keep-alive");
823 httpd_resp_set_hdr(req, header.name, header.value);
826 httpd_resp_send_chunk(req, CRLF_STR, CRLF_LEN);
828 req->sess_ctx =
this;
831 this->
hd_ = req->handle;
832 this->
fd_.store(httpd_req_to_sockfd(req));
835 httpd_sess_set_send_override(this->
hd_, this->
fd_.load(), nonblocking_send);
846 ESP_LOGW(TAG,
"Config not sent to fd %d", this->
fd_.load());
851#ifdef USE_WEBSERVER_SORTING
852 for (
auto &group : ws->sorting_groups_) {
854 JsonObject root = builder.
root();
856 root[
"sorting_weight"] = group.second.weight;
864 ESP_LOGW(TAG,
"Sorting groups not sent to fd %d", this->
fd_.load());
876 int fd = rsp->
fd_.exchange(0);
877 ESP_LOGD(TAG,
"Event source connection closed (fd: %d)", fd);
894 this->deferred_queue_.push_back(item);
933 const int fd = this->
fd_.load();
955 ESP_LOGW(TAG,
"Failed to queue EventSource close (%s); retrying", esp_err_to_name(err));
981 const int fd = response->
fd_.load();
982 if (response->owns_session_(fd)) {
983 httpd_sess_update_lru_counter(response->hd_, fd);
986 response->lru_work_queued_.store(
false, std::memory_order_release);
991 const int fd = response->
fd_.load();
992 if (response->owns_session_(fd)) {
995 shutdown(fd, SHUT_RDWR);
999 response->close_work_queued_.store(
false, std::memory_order_release);
1005 const int fd = this->
fd_.load();
1009 struct msghdr msg{};
1011 msg.msg_iovlen = iovcnt;
1012 const ssize_t sent = sendmsg(fd, &msg, MSG_DONTWAIT);
1016 const int err = errno;
1017 if (err == EAGAIN || err == EWOULDBLOCK) {
1020 ESP_LOGD(TAG,
"send error: errno %d", err);
1046 ESP_LOGW(TAG,
"Closing stuck EventSource connection after %" PRIu32
" ms without send progress",
1054 this->tail_sent_ += sent;
1056 ESP_LOGV(TAG,
"Partial send: %zd/%zu bytes (total: %u/%u)", sent, remaining, this->tail_sent_, this->tail_len_);
1060 this->tail_len_ = 0;
1072 this->
tail_.reset();
1079 size_t message_len,
size_t total,
size_t sent) {
1083 "the log cut in web_server.h must keep a worst case log event inside the tail ceiling");
1087 ESP_LOGW(TAG,
"Cannot buffer a %zu byte chunk, closing", total);
1094 uint8_t *dst = this->
tail_.get();
1095 std::memcpy(dst, prefix, prefix_len);
1099 [](
void *ctx,
const char *piece,
size_t len) {
1100 auto &
out = *
static_cast<uint8_t **
>(ctx);
1101 std::memcpy(
out, piece,
len);
1124#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 14 && defined(__OPTIMIZE_SIZE__)
1125#pragma GCC diagnostic push
1126#pragma GCC diagnostic ignored "-Wstack-usage="
1130 char *
buffer,
size_t *size_out) {
1133 SendGuard guard{*
this};
1143 const size_t prefix_len =
build_chunk_prefix(prefix,
sizeof(prefix),
"state", 0, 0,
true);
1146 size_t json_len = 0;
1154 json_len = builder.serialize_to(
reinterpret_cast<char *
>(this->
tail_.get()) + prefix_len, room);
1155 if (json_len < room) {
1167 uint8_t *dst = this->
tail_.get();
1168 std::memcpy(dst, prefix, prefix_len);
1174#if defined(__GNUC__) && !defined(__clang__) && __GNUC__ >= 14 && defined(__OPTIMIZE_SIZE__)
1175#pragma GCC diagnostic pop
1198 ESP_LOGW(TAG,
"State event does not fit %zu bytes, dropped",
size);
1199 this->
tail_.reset();
1213 ESP_LOGW(TAG,
"No memory for a %zu byte chunk", cap);
1223 if (this->
sending_ || this->
fd_.load() == 0 || this->close_requested_) {
1245 ESP_LOGW(TAG,
"Event name too long, dropped");
1251 struct iovec iov[MAX_SEND_IOV];
1256 g.total = prefix_len;
1259 [](
void *ctx,
const char *piece,
size_t len) {
1260 auto &g = *
static_cast<Gather *
>(ctx);
1269 g.iov[g.iovcnt++] = {
const_cast<char *
>(piece),
len};
1274 g.iov[0] = {prefix, prefix_len};
1277 const ssize_t sent = g.fits ? this->
send_(g.iov, g.iovcnt) : 0;
1281 if (
static_cast<size_t>(sent) == g.total) {
1316#ifdef USE_WEBSERVER_OTA
1319 const char *boundary_start;
1320 size_t boundary_len;
1322 ESP_LOGE(TAG,
"Failed to parse multipart boundary");
1323 httpd_resp_send_err(r, HTTPD_400_BAD_REQUEST,
nullptr);
1330 if (
h->canHandle(&req)) {
1337 ESP_LOGW(TAG,
"No handler found for OTA request");
1338 httpd_resp_send_err(r, HTTPD_404_NOT_FOUND,
nullptr);
1343 std::string filename;
1346 auto reader = std::make_unique<MultipartReader>(
"--" + std::string(boundary_start, boundary_len));
1349 reader->set_data_callback([&](
const uint8_t *data,
size_t len) {
1350 if (!reader->has_file() || !
len)
1353 if (filename.empty()) {
1354 filename = reader->get_current_part().filename;
1355 ESP_LOGV(TAG,
"Processing file: '%s'", filename.c_str());
1356 handler->
handleUpload(&req, filename, 0,
nullptr, 0,
false);
1359 handler->
handleUpload(&req, filename, index,
const_cast<uint8_t *
>(data),
len,
false);
1363 reader->set_part_complete_callback([&]() {
1365 handler->
handleUpload(&req, filename, index,
nullptr, 0,
true);
1371 auto buffer = std::make_unique_for_overwrite<char[]>(RECV_CHUNK_SIZE);
1372 size_t bytes_since_yield = 0;
1374 for (
size_t remaining = r->content_len; remaining > 0;) {
1375 int recv_len = httpd_req_recv(r,
buffer.get(), std::min(remaining, RECV_CHUNK_SIZE));
1377 if (recv_len <= 0) {
1378 httpd_resp_send_err(r, recv_len == HTTPD_SOCK_ERR_TIMEOUT ? HTTPD_408_REQ_TIMEOUT : HTTPD_400_BAD_REQUEST,
1380 return recv_len == HTTPD_SOCK_ERR_TIMEOUT ? ESP_ERR_TIMEOUT : ESP_FAIL;
1383 if (reader->parse(
buffer.get(), recv_len) !=
static_cast<size_t>(recv_len)) {
1384 ESP_LOGW(TAG,
"Multipart parser error");
1385 httpd_resp_send_err(r, HTTPD_400_BAD_REQUEST,
nullptr);
1389 remaining -= recv_len;
1390 bytes_since_yield += recv_len;
1392 if (bytes_since_yield > YIELD_INTERVAL_BYTES) {
1394 bytes_since_yield = 0;
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
void enable_loop_soon_any_context()
Thread and ISR-safe version of enable_loop() that can be called from any context.
void begin(bool include_internal=false)
ESPHOME_ALWAYS_INLINE void try_advance(size_t max_steps)
Run up to max_steps iteration steps; stops early when iteration completes or a callback refuses (that...
Helper class that wraps a mutex with a RAII-style API.
An STL allocator that uses SPI or internal RAM.
RAMUniquePtr< T[]> make_unique_array_for_overwrite(size_t n)
n elements left uninitialized, as std::make_unique_for_overwrite does; empty on exhaustion,...
StringRef is a reference to a string owned by something else.
constexpr const char * c_str() const
constexpr size_type size() const
Bump allocator over a fixed buffer for a document built and serialized in one scope.
Builder class for creating JSON documents without lambdas.
SerializationBuffer serialize()
Serialize the JSON document to a SerializationBuffer (stack-first allocation) Uses 512-byte stack buf...
This class allows users to create a web server with their ESP nodes.
json::SerializationBuffer get_config_json()
Return the webserver configuration as JSON.
std::vector< AsyncEventSourceResponse * > pending_sessions_
~AsyncEventSource() override
std::atomic< bool > has_pending_sessions_
friend class AsyncEventSourceResponse
bool loop()
Returns true if there are sessions remaining (including pending cleanup).
std::vector< AsyncEventSourceResponse * > sessions_
void try_send_nodefer(const char *message, size_t message_len, const char *event=nullptr, uint32_t id=0, uint32_t reconnect=0)
esphome::web_server::WebServer * web_server_
void handleRequest(AsyncWebServerRequest *request) override
void deferrable_send_state(void *source, bool detail_all, message_generator_t *message_generator)
static constexpr uint32_t CLOSE_CONFIRM_INTERVAL_MS
std::atomic< bool > close_work_queued_
uint32_t send_failure_started_ms_
static void destroy(void *p)
static void refresh_lru_work(void *arg)
uint32_t last_lru_refresh_ms_
std::vector< DeferredEvent > deferred_queue_
void deferrable_send_state(void *source, bool detail_all, message_generator_t *message_generator)
bool try_send_nodefer(const char *message, size_t message_len, const char *event=nullptr, uint32_t id=0, uint32_t reconnect=0)
JsonPrepareResult message_generator_t char * buffer
static void close_session_work(void *arg)
bool close_retry_warning_logged_
esphome::web_server::WebServer * web_server_
static constexpr size_t MAX_SEND_IOV
void tail_alloc_failed_(size_t cap)
bool owns_session_(int fd) const
void deq_push_back_with_dedup_(void *source, message_generator_t *message_generator)
static constexpr uint32_t LRU_REFRESH_INTERVAL_MS
void process_deferred_queue_()
uint32_t next_close_attempt_ms_
static constexpr size_t PREFIX_BUF_SIZE
static constexpr uint32_t CLOSE_RETRY_INTERVAL_MS
static constexpr size_t TAIL_MAX_SIZE
static constexpr uint32_t SEND_STALL_TIMEOUT_MS
AsyncEventSourceResponse(const AsyncWebServerRequest *request, esphome::web_server_idf::AsyncEventSource *server, esphome::web_server::WebServer *ws)
JsonPrepareResult message_generator_t * generator
bool reserve_tail_(size_t len)
JsonPrepareResult message_generator_t char size_t * size_out
ssize_t send_(struct iovec *iov, int iovcnt)
static constexpr size_t JSON_BUF_SIZE
bool stash_chunk_(const char *prefix, size_t prefix_len, const char *message, size_t message_len, size_t total, size_t sent)
@ JSON_PREPARE_RESULT_NO_MEMORY
@ JSON_PREPARE_RESULT_TAIL
@ JSON_PREPARE_RESULT_BUFFER
@ JSON_PREPARE_RESULT_TOO_LARGE
std::atomic< bool > lru_work_queued_
esphome::web_server::ListEntitiesIterator entities_iterator_
bool send_json_(void *source, message_generator_t *generator)
RAMUniquePtr< uint8_t[]> tail_
void start_session_main_loop_()
void print(const char *str)
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)
virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total)
std::function< void(AsyncWebServerRequest *request)> on_not_found_
esp_err_t handle_raw_body_(httpd_req_t *r, const char *content_type)
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 void safe_close_with_shutdown(httpd_handle_t hd, int sockfd)
static esp_err_t request_handler(httpd_req_t *r)
AsyncWebParameter * getParam(const char *name)
optional< std::string > get_header(const char *name) const
bool hasArg(const char *name)
StringRef url_to(std::span< char, URL_BUF_SIZE > buffer) const
Write URL (without query string) to buffer, returns StringRef pointing to buffer.
bool hasHeader(const char *name) const
void init_response_(AsyncWebServerResponse *rsp, int code, const char *content_type)
static constexpr size_t URL_BUF_SIZE
Buffer size for url_to()
std::function< void()> on_disconnect_
http_method method() const
std::string arg(const char *name)
optional< std::string > find_query_value_(const char *name) const
void requestAuthentication() const
AsyncWebServerResponse * rsp_
bool authenticate(const char *username, const char *password) const
std::vector< AsyncWebParameter * > params_
void redirect(const char *url)
const AsyncWebServerRequest * req_
void addHeader(const char *name, const char *value)
const LogString * message
std::vector< uint8_t > & out
JsonString linked(const char *s)
constexpr size_t CHUNK_END_LEN
size_t build_chunk_prefix(char *buf, size_t size, const char *event, uint32_t id, uint32_t reconnect, bool with_data)
bool query_has_key(const char *query_url, size_t query_len, const char *key)
void(esphome::web_server::WebServer *, void *, json::JsonBuilder &) message_generator_t
optional< std::string > request_get_header(httpd_req_t *req, const char *name)
constexpr size_t SSE_SUFFIX_LEN
bool parse_multipart_boundary(const char *content_type, const char **boundary_start, size_t *boundary_len)
optional< std::string > query_key_value(const char *query_url, size_t query_len, const char *key)
const char * strcasestr_n(const char *haystack, size_t haystack_len, const char *needle)
constexpr size_t SSE_SEP_LEN
void write_chunk_header(char *buf, size_t chunk_len)
constexpr size_t CHUNK_HDR_LEN
constexpr char SSE_SUFFIX[]
size_t url_decode(char *str)
Decode URL-encoded string in-place (e.g., %20 -> space, + -> space) Returns the new length of the dec...
ESPHOME_LOG_TAG(TAG, "multipart")
bool request_has_header(httpd_req_t *req, const char *name)
void for_each_chunk_piece(const char *message, size_t message_len, ChunkPieceSink sink, void *ctx)
constexpr size_t LOG_EVENT_MAX_LEN
bool random_bytes(uint8_t *data, size_t len)
Generate len random bytes using the platform's secure RNG (hardware RNG or OS CSPRNG).
size_t size_t const char va_start(args, fmt)
size_t size_t const char * fmt
uint32_t IRAM_ATTR HOT millis()
char * format_hex_to(char *buffer, size_t buffer_size, const uint8_t *data, size_t length)
Format byte array as lowercase hex to buffer (base implementation).
Application App
Global storage of Application pointer - only one Application can exist.
message_generator_t * message_generator_