ESPHome 2026.5.0-dev
Loading...
Searching...
No Matches
web_server_idf.h
Go to the documentation of this file.
1#pragma once
2#ifdef USE_ESP32
3
7#include <esp_http_server.h>
8
9#include <atomic>
10#include <functional>
11#include <list>
12#include <map>
13#include <span>
14#include <string>
15#include <utility>
16#include <vector>
17
18#ifdef USE_WEBSERVER
21#endif
22
23namespace esphome {
24#ifdef USE_WEBSERVER
25namespace web_server {
26class WebServer;
27}; // namespace web_server
28#endif
29namespace web_server_idf {
30
32 public:
33 AsyncWebParameter(std::string name, std::string value) : name_(std::move(name)), value_(std::move(value)) {}
34 const std::string &name() const { return this->name_; }
35 const std::string &value() const { return this->value_; }
36
37 protected:
38 std::string name_;
39 std::string value_;
40};
41
43
45 public:
48
49 // NOLINTNEXTLINE(readability-identifier-naming)
50 void addHeader(const char *name, const char *value);
51
52 virtual const char *get_content_data() const = 0;
53 virtual size_t get_content_size() const = 0;
54
55 protected:
57};
58
60 public:
62
63 const char *get_content_data() const override { return nullptr; };
64 size_t get_content_size() const override { return 0; };
65};
66
68 public:
70 : AsyncWebServerResponse(req), content_(std::move(content)) {}
71
72 const char *get_content_data() const override { return this->content_.c_str(); };
73 size_t get_content_size() const override { return this->content_.size(); };
74
75 protected:
76 std::string content_;
77};
78
80 public:
82
83 const char *get_content_data() const override { return this->content_.c_str(); };
84 size_t get_content_size() const override { return this->content_.size(); };
85
86 void print(const char *str) { this->content_.append(str); }
87 void print(const std::string &str) { this->content_.append(str); }
88 void print(float value);
89 void printf(const char *fmt, ...) __attribute__((format(printf, 2, 3)));
90 void write(uint8_t c) { this->content_.push_back(static_cast<char>(c)); }
91
92 protected:
93 std::string content_;
94};
95
97 public:
98 AsyncWebServerResponseProgmem(const AsyncWebServerRequest *req, const uint8_t *data, const size_t size)
99 : AsyncWebServerResponse(req), data_(data), size_(size) {}
100
101 const char *get_content_data() const override { return reinterpret_cast<const char *>(this->data_); };
102 size_t get_content_size() const override { return this->size_; };
103
104 protected:
105 const uint8_t *data_;
106 size_t size_;
107};
108
110 friend class AsyncWebServer;
111
112 public:
114
115 http_method method() const { return static_cast<http_method>(this->req_->method); }
116 static constexpr size_t URL_BUF_SIZE = CONFIG_HTTPD_MAX_URI_LEN + 1;
119 StringRef url_to(std::span<char, URL_BUF_SIZE> buffer) const;
120 // Remove before 2026.9.0
121 ESPDEPRECATED("Use url_to() instead. Removed in 2026.9.0", "2026.3.0")
122 std::string url() const {
123 char buffer[URL_BUF_SIZE];
124 return std::string(this->url_to(buffer));
125 }
126 // NOLINTNEXTLINE(readability-identifier-naming)
127 size_t contentLength() const { return this->req_->content_len; }
128
129#ifdef USE_WEBSERVER_AUTH
130 bool authenticate(const char *username, const char *password) const;
131 // NOLINTNEXTLINE(readability-identifier-naming)
132 void requestAuthentication(const char *realm = nullptr) const;
133#endif
134
135 void redirect(const std::string &url);
136
137 inline void ESPHOME_ALWAYS_INLINE send(AsyncWebServerResponse *response) {
138 httpd_resp_send(*this, response->get_content_data(), response->get_content_size());
139 }
140 inline void ESPHOME_ALWAYS_INLINE send(int code, const char *content_type = nullptr, const char *content = nullptr) {
141 this->init_response_(nullptr, code, content_type);
142 if (content) {
143 httpd_resp_send(*this, content, HTTPD_RESP_USE_STRLEN);
144 } else {
145 httpd_resp_send(*this, nullptr, 0);
146 }
147 }
148 // NOLINTNEXTLINE(readability-identifier-naming)
149 AsyncWebServerResponse *beginResponse(int code, const char *content_type) {
150 auto *res = new AsyncWebServerResponseEmpty(this); // NOLINT(cppcoreguidelines-owning-memory)
151 this->init_response_(res, code, content_type);
152 return res;
153 }
154 // NOLINTNEXTLINE(readability-identifier-naming)
155 AsyncWebServerResponse *beginResponse(int code, const char *content_type, const std::string &content) {
156 auto *res = new AsyncWebServerResponseContent(this, content); // NOLINT(cppcoreguidelines-owning-memory)
157 this->init_response_(res, code, content_type);
158 return res;
159 }
160 // NOLINTNEXTLINE(readability-identifier-naming)
161 AsyncWebServerResponse *beginResponse(int code, const char *content_type, const uint8_t *data,
162 const size_t data_size) {
163 auto *res = new AsyncWebServerResponseProgmem(this, data, data_size); // NOLINT(cppcoreguidelines-owning-memory)
164 this->init_response_(res, code, content_type);
165 return res;
166 }
167 // NOLINTNEXTLINE(readability-identifier-naming)
168 AsyncResponseStream *beginResponseStream(const char *content_type) {
169 auto *res = new AsyncResponseStream(this); // NOLINT(cppcoreguidelines-owning-memory)
170 this->init_response_(res, 200, content_type);
171 return res;
172 }
173
174 // NOLINTNEXTLINE(readability-identifier-naming)
175 bool hasParam(const char *name) { return this->getParam(name) != nullptr; }
176 // NOLINTNEXTLINE(readability-identifier-naming)
177 bool hasParam(const std::string &name) { return this->getParam(name.c_str()) != nullptr; }
178 // NOLINTNEXTLINE(readability-identifier-naming)
179 AsyncWebParameter *getParam(const char *name);
180 // NOLINTNEXTLINE(readability-identifier-naming)
181 AsyncWebParameter *getParam(const std::string &name) { return this->getParam(name.c_str()); }
182
183 // NOLINTNEXTLINE(readability-identifier-naming)
184 bool hasArg(const char *name);
185 std::string arg(const char *name);
186 std::string arg(const std::string &name) { return this->arg(name.c_str()); }
187
188 operator httpd_req_t *() const { return this->req_; }
189 optional<std::string> get_header(const char *name) const;
190 // NOLINTNEXTLINE(readability-identifier-naming)
191 bool hasHeader(const char *name) const;
192
193 protected:
194 httpd_req_t *req_;
196 // Use vector instead of map/unordered_map: most requests have 0-3 params, so linear search
197 // is faster than tree/hash overhead. AsyncWebParameter stores both name and value to avoid
198 // duplicate storage. Only successful lookups are cached to prevent cache pollution when
199 // handlers check for optional parameters that don't exist.
200 optional<std::string> find_query_value_(const char *name) const;
201 std::vector<AsyncWebParameter *> params_;
202 std::string post_query_;
203 AsyncWebServerRequest(httpd_req_t *req) : req_(req) {}
204 AsyncWebServerRequest(httpd_req_t *req, std::string post_query) : req_(req), post_query_(std::move(post_query)) {}
205 void init_response_(AsyncWebServerResponse *rsp, int code, const char *content_type);
206};
207
208class AsyncWebHandler;
209
211 public:
212 AsyncWebServer(uint16_t port) : port_(port){};
213 ~AsyncWebServer() { this->end(); }
214
215 // NOLINTNEXTLINE(readability-identifier-naming)
216 void onNotFound(std::function<void(AsyncWebServerRequest *request)> &&fn) { on_not_found_ = std::move(fn); }
217
218 void begin();
219 void end();
220
221 // NOLINTNEXTLINE(readability-identifier-naming)
223 this->handlers_.push_back(handler);
224 return *handler;
225 }
226
227 httpd_handle_t get_server() { return this->server_; }
228
229 protected:
230 uint16_t port_{};
231 httpd_handle_t server_{};
232 static esp_err_t request_handler(httpd_req_t *r);
233 static esp_err_t request_post_handler(httpd_req_t *r);
234 esp_err_t request_handler_(AsyncWebServerRequest *request) const;
235 static void safe_close_with_shutdown(httpd_handle_t hd, int sockfd);
236#ifdef USE_WEBSERVER_OTA
237 esp_err_t handle_multipart_upload_(httpd_req_t *r, const char *content_type);
238#endif
239 std::vector<AsyncWebHandler *> handlers_;
240 std::function<void(AsyncWebServerRequest *request)> on_not_found_{};
241};
242
244 public:
245 virtual ~AsyncWebHandler() {}
246 // NOLINTNEXTLINE(readability-identifier-naming)
247 virtual bool canHandle(AsyncWebServerRequest *request) const { return false; }
248 // NOLINTNEXTLINE(readability-identifier-naming)
249 virtual void handleRequest(AsyncWebServerRequest *request) {}
250 // NOLINTNEXTLINE(readability-identifier-naming)
251 virtual void handleUpload(AsyncWebServerRequest *request, const std::string &filename, size_t index, uint8_t *data,
252 size_t len, bool final) {}
253 // NOLINTNEXTLINE(readability-identifier-naming)
254 virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {}
255 // NOLINTNEXTLINE(readability-identifier-naming)
256 virtual bool isRequestHandlerTrivial() const { return true; }
257};
258
259#ifdef USE_WEBSERVER
260class AsyncEventSource;
262
264
265/*
266 This class holds a pointer to the source component that wants to publish a state event, and a pointer to a function
267 that will lazily generate that event. The two pointers allow dedup in the deferred queue if multiple publishes for
268 the same component are backed up, and take up only two pointers of memory. The entry in the deferred queue (a
269 std::vector) is the DeferredEvent instance itself (not a pointer to one elsewhere in heap) so still only two pointers
270 per entry (and no heap fragmentation). Even 100 backed up events (you'd have to have at least 100 sensors publishing
271 because of dedup) would take up only 0.8 kB.
272*/
275
276 protected:
277 void *source_;
279
280 public:
281 DeferredEvent(void *source, message_generator_t *message_generator)
282 : source_(source), message_generator_(message_generator) {}
283 bool operator==(const DeferredEvent &test) const {
284 return (source_ == test.source_ && message_generator_ == test.message_generator_);
285 }
286};
287static_assert(sizeof(DeferredEvent) == sizeof(void *) + sizeof(message_generator_t *),
288 "DeferredEvent should have no padding");
289
291 friend class AsyncEventSource;
292
293 public:
294 bool try_send_nodefer(const char *message, const char *event = nullptr, uint32_t id = 0, uint32_t reconnect = 0);
295 void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator);
296 void loop();
297
298 protected:
301
302 void deq_push_back_with_dedup_(void *source, message_generator_t *message_generator);
304 void process_buffer_();
305
306 static void destroy(void *p);
308 httpd_handle_t hd_{};
309 std::atomic<int> fd_{};
310 std::vector<DeferredEvent> deferred_queue_;
313 std::string event_buffer_{""};
316 static constexpr uint16_t MAX_CONSECUTIVE_SEND_FAILURES = 2500; // ~20 seconds at 125Hz loop rate
317};
318
320
323 using connect_handler_t = std::function<void(AsyncEventSourceClient *)>;
324
325 public:
326 AsyncEventSource(std::string url, esphome::web_server::WebServer *ws) : url_(std::move(url)), web_server_(ws) {}
327 ~AsyncEventSource() override;
328
329 // NOLINTNEXTLINE(readability-identifier-naming)
330 bool canHandle(AsyncWebServerRequest *request) const override {
331 if (request->method() != HTTP_GET)
332 return false;
334 return request->url_to(url_buf) == this->url_;
335 }
336 // NOLINTNEXTLINE(readability-identifier-naming)
337 void handleRequest(AsyncWebServerRequest *request) override;
338 // NOLINTNEXTLINE(readability-identifier-naming)
339 void onConnect(connect_handler_t &&cb) { this->on_connect_ = std::move(cb); }
340
341 void try_send_nodefer(const char *message, const char *event = nullptr, uint32_t id = 0, uint32_t reconnect = 0);
342 void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator);
344 bool loop();
345 bool empty() { return this->count() == 0; }
346
347 size_t count() const { return this->sessions_.size(); }
348
349 protected:
350 std::string url_;
351 // Use vector instead of set: SSE sessions are typically 1-5 connections (browsers, dashboards).
352 // Linear search is faster than red-black tree overhead for this small dataset.
353 // Only operations needed: add session, remove session, iterate sessions - no need for sorted order.
354 std::vector<AsyncEventSourceResponse *> sessions_;
355 connect_handler_t on_connect_{};
357};
358#endif // USE_WEBSERVER
359
361 const char *name;
362 const char *value;
363};
364
367#ifdef USE_WEBSERVER
369#endif
370
371 public:
372 // NOLINTNEXTLINE(readability-identifier-naming)
373 void addHeader(const char *name, const char *value) { this->headers_.push_back({name, value}); }
374
375 // NOLINTNEXTLINE(readability-identifier-naming)
376 static DefaultHeaders &Instance();
377
378 protected:
379 // Stack-allocated, no reallocation machinery. Count defined in web_server_base where headers are added.
381};
382
383} // namespace web_server_idf
384} // namespace esphome
385
386using namespace esphome::web_server_idf; // NOLINT(google-global-names-in-headers)
387
388#endif // !defined(USE_ESP32)
media_source::MediaSource * source
Minimal static vector - saves memory by avoiding std::vector overhead.
Definition helpers.h:210
StringRef is a reference to a string owned by something else.
Definition string_ref.h:26
Buffer for JSON serialization that uses stack allocation for small payloads.
Definition json_util.h:22
This class allows users to create a web server with their ESP nodes.
Definition web_server.h:191
AsyncEventSource(std::string url, esphome::web_server::WebServer *ws)
bool loop()
Returns true if there are sessions remaining (including pending cleanup).
std::vector< AsyncEventSourceResponse * > sessions_
void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator)
esphome::web_server::WebServer * web_server_
bool canHandle(AsyncWebServerRequest *request) const override
void onConnect(connect_handler_t &&cb)
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)
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)
AsyncResponseStream(const AsyncWebServerRequest *req)
void printf(const char *fmt,...) __attribute__((format(printf
const char * get_content_data() const override
virtual bool canHandle(AsyncWebServerRequest *request) const
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)
AsyncWebParameter(std::string name, std::string value)
std::function< void(AsyncWebServerRequest *request)> on_not_found_
static esp_err_t request_post_handler(httpd_req_t *r)
std::vector< AsyncWebHandler * > handlers_
AsyncWebHandler & addHandler(AsyncWebHandler *handler)
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)
void onNotFound(std::function< void(AsyncWebServerRequest *request)> &&fn)
AsyncWebParameter * getParam(const std::string &name)
AsyncWebServerResponse * beginResponse(int code, const char *content_type, const uint8_t *data, const size_t data_size)
AsyncWebParameter * getParam(const char *name)
optional< std::string > get_header(const char *name) const
void ESPHOME_ALWAYS_INLINE send(int code, const char *content_type=nullptr, const char *content=nullptr)
StringRef url_to(std::span< char, URL_BUF_SIZE > buffer) const
Write URL (without query string) to buffer, returns StringRef pointing to buffer.
AsyncWebServerResponse * beginResponse(int code, const char *content_type, const std::string &content)
std::string arg(const std::string &name)
AsyncWebServerResponse * beginResponse(int code, const char *content_type)
void init_response_(AsyncWebServerResponse *rsp, int code, const char *content_type)
static constexpr size_t URL_BUF_SIZE
Buffer size for url_to()
optional< std::string > find_query_value_(const char *name) const
ESPDEPRECATED("Use url_to() instead. Removed in 2026.9.0", "2026.3.0") std void requestAuthentication(const char *realm=nullptr) const
void ESPHOME_ALWAYS_INLINE send(AsyncWebServerResponse *response)
AsyncResponseStream * beginResponseStream(const char *content_type)
AsyncWebServerRequest(httpd_req_t *req, std::string post_query)
std::vector< AsyncWebParameter * > params_
AsyncWebServerResponseContent(const AsyncWebServerRequest *req, std::string content)
AsyncWebServerResponseEmpty(const AsyncWebServerRequest *req)
virtual const char * get_content_data() const =0
AsyncWebServerResponse(const AsyncWebServerRequest *req)
void addHeader(const char *name, const char *value)
AsyncWebServerResponseProgmem(const AsyncWebServerRequest *req, const uint8_t *data, const size_t size)
void addHeader(const char *name, const char *value)
StaticVector< HttpHeader, WEB_SERVER_DEFAULT_HEADERS_COUNT > headers_
struct @65::@66 __attribute__
Wake the main loop task from an ISR. ISR-safe.
Definition main_task.h:32
const char * message
Definition component.cpp:35
json::SerializationBuffer<>(esphome::web_server::WebServer *, void *) message_generator_t
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
const char int const __FlashStringHelper * format
Definition log.h:74
std::string size_t len
Definition helpers.h:1045
uint16_t size
Definition helpers.cpp:25
size_t size_t const char * fmt
Definition helpers.h:1083
struct ESPDEPRECATED("Use std::index_sequence instead. Removed in 2026.6.0", "2025.12.0") seq
Definition automation.h:26
static void uint32_t
bool operator==(const DeferredEvent &test) const
DeferredEvent(void *source, message_generator_t *message_generator)
std::string print()