ESPHome 2026.3.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 void send(AsyncWebServerResponse *response);
138 void send(int code, const char *content_type = nullptr, const char *content = nullptr);
139 // NOLINTNEXTLINE(readability-identifier-naming)
140 AsyncWebServerResponse *beginResponse(int code, const char *content_type) {
141 auto *res = new AsyncWebServerResponseEmpty(this); // NOLINT(cppcoreguidelines-owning-memory)
142 this->init_response_(res, code, content_type);
143 return res;
144 }
145 // NOLINTNEXTLINE(readability-identifier-naming)
146 AsyncWebServerResponse *beginResponse(int code, const char *content_type, const std::string &content) {
147 auto *res = new AsyncWebServerResponseContent(this, content); // NOLINT(cppcoreguidelines-owning-memory)
148 this->init_response_(res, code, content_type);
149 return res;
150 }
151 // NOLINTNEXTLINE(readability-identifier-naming)
152 AsyncWebServerResponse *beginResponse(int code, const char *content_type, const uint8_t *data,
153 const size_t data_size) {
154 auto *res = new AsyncWebServerResponseProgmem(this, data, data_size); // NOLINT(cppcoreguidelines-owning-memory)
155 this->init_response_(res, code, content_type);
156 return res;
157 }
158 // NOLINTNEXTLINE(readability-identifier-naming)
159 AsyncResponseStream *beginResponseStream(const char *content_type) {
160 auto *res = new AsyncResponseStream(this); // NOLINT(cppcoreguidelines-owning-memory)
161 this->init_response_(res, 200, content_type);
162 return res;
163 }
164
165 // NOLINTNEXTLINE(readability-identifier-naming)
166 bool hasParam(const char *name) { return this->getParam(name) != nullptr; }
167 // NOLINTNEXTLINE(readability-identifier-naming)
168 bool hasParam(const std::string &name) { return this->getParam(name.c_str()) != nullptr; }
169 // NOLINTNEXTLINE(readability-identifier-naming)
170 AsyncWebParameter *getParam(const char *name);
171 // NOLINTNEXTLINE(readability-identifier-naming)
172 AsyncWebParameter *getParam(const std::string &name) { return this->getParam(name.c_str()); }
173
174 // NOLINTNEXTLINE(readability-identifier-naming)
175 bool hasArg(const char *name);
176 std::string arg(const char *name);
177 std::string arg(const std::string &name) { return this->arg(name.c_str()); }
178
179 operator httpd_req_t *() const { return this->req_; }
180 optional<std::string> get_header(const char *name) const;
181 // NOLINTNEXTLINE(readability-identifier-naming)
182 bool hasHeader(const char *name) const;
183
184 protected:
185 httpd_req_t *req_;
187 // Use vector instead of map/unordered_map: most requests have 0-3 params, so linear search
188 // is faster than tree/hash overhead. AsyncWebParameter stores both name and value to avoid
189 // duplicate storage. Only successful lookups are cached to prevent cache pollution when
190 // handlers check for optional parameters that don't exist.
191 optional<std::string> find_query_value_(const char *name) const;
192 std::vector<AsyncWebParameter *> params_;
193 std::string post_query_;
194 AsyncWebServerRequest(httpd_req_t *req) : req_(req) {}
195 AsyncWebServerRequest(httpd_req_t *req, std::string post_query) : req_(req), post_query_(std::move(post_query)) {}
196 void init_response_(AsyncWebServerResponse *rsp, int code, const char *content_type);
197};
198
199class AsyncWebHandler;
200
202 public:
203 AsyncWebServer(uint16_t port) : port_(port){};
204 ~AsyncWebServer() { this->end(); }
205
206 // NOLINTNEXTLINE(readability-identifier-naming)
207 void onNotFound(std::function<void(AsyncWebServerRequest *request)> &&fn) { on_not_found_ = std::move(fn); }
208
209 void begin();
210 void end();
211
212 // NOLINTNEXTLINE(readability-identifier-naming)
214 this->handlers_.push_back(handler);
215 return *handler;
216 }
217
218 httpd_handle_t get_server() { return this->server_; }
219
220 protected:
221 uint16_t port_{};
222 httpd_handle_t server_{};
223 static esp_err_t request_handler(httpd_req_t *r);
224 static esp_err_t request_post_handler(httpd_req_t *r);
225 esp_err_t request_handler_(AsyncWebServerRequest *request) const;
226 static void safe_close_with_shutdown(httpd_handle_t hd, int sockfd);
227#ifdef USE_WEBSERVER_OTA
228 esp_err_t handle_multipart_upload_(httpd_req_t *r, const char *content_type);
229#endif
230 std::vector<AsyncWebHandler *> handlers_;
231 std::function<void(AsyncWebServerRequest *request)> on_not_found_{};
232};
233
235 public:
236 virtual ~AsyncWebHandler() {}
237 // NOLINTNEXTLINE(readability-identifier-naming)
238 virtual bool canHandle(AsyncWebServerRequest *request) const { return false; }
239 // NOLINTNEXTLINE(readability-identifier-naming)
240 virtual void handleRequest(AsyncWebServerRequest *request) {}
241 // NOLINTNEXTLINE(readability-identifier-naming)
242 virtual void handleUpload(AsyncWebServerRequest *request, const std::string &filename, size_t index, uint8_t *data,
243 size_t len, bool final) {}
244 // NOLINTNEXTLINE(readability-identifier-naming)
245 virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {}
246 // NOLINTNEXTLINE(readability-identifier-naming)
247 virtual bool isRequestHandlerTrivial() const { return true; }
248};
249
250#ifdef USE_WEBSERVER
251class AsyncEventSource;
253
255
256/*
257 This class holds a pointer to the source component that wants to publish a state event, and a pointer to a function
258 that will lazily generate that event. The two pointers allow dedup in the deferred queue if multiple publishes for
259 the same component are backed up, and take up only two pointers of memory. The entry in the deferred queue (a
260 std::vector) is the DeferredEvent instance itself (not a pointer to one elsewhere in heap) so still only two pointers
261 per entry (and no heap fragmentation). Even 100 backed up events (you'd have to have at least 100 sensors publishing
262 because of dedup) would take up only 0.8 kB.
263*/
266
267 protected:
268 void *source_;
270
271 public:
272 DeferredEvent(void *source, message_generator_t *message_generator)
273 : source_(source), message_generator_(message_generator) {}
274 bool operator==(const DeferredEvent &test) const {
275 return (source_ == test.source_ && message_generator_ == test.message_generator_);
276 }
277};
278static_assert(sizeof(DeferredEvent) == sizeof(void *) + sizeof(message_generator_t *),
279 "DeferredEvent should have no padding");
280
282 friend class AsyncEventSource;
283
284 public:
285 bool try_send_nodefer(const char *message, const char *event = nullptr, uint32_t id = 0, uint32_t reconnect = 0);
286 void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator);
287 void loop();
288
289 protected:
292
293 void deq_push_back_with_dedup_(void *source, message_generator_t *message_generator);
295 void process_buffer_();
296
297 static void destroy(void *p);
299 httpd_handle_t hd_{};
300 std::atomic<int> fd_{};
301 std::vector<DeferredEvent> deferred_queue_;
304 std::string event_buffer_{""};
307 static constexpr uint16_t MAX_CONSECUTIVE_SEND_FAILURES = 2500; // ~20 seconds at 125Hz loop rate
308};
309
311
314 using connect_handler_t = std::function<void(AsyncEventSourceClient *)>;
315
316 public:
317 AsyncEventSource(std::string url, esphome::web_server::WebServer *ws) : url_(std::move(url)), web_server_(ws) {}
318 ~AsyncEventSource() override;
319
320 // NOLINTNEXTLINE(readability-identifier-naming)
321 bool canHandle(AsyncWebServerRequest *request) const override {
322 if (request->method() != HTTP_GET)
323 return false;
325 return request->url_to(url_buf) == this->url_;
326 }
327 // NOLINTNEXTLINE(readability-identifier-naming)
328 void handleRequest(AsyncWebServerRequest *request) override;
329 // NOLINTNEXTLINE(readability-identifier-naming)
330 void onConnect(connect_handler_t &&cb) { this->on_connect_ = std::move(cb); }
331
332 void try_send_nodefer(const char *message, const char *event = nullptr, uint32_t id = 0, uint32_t reconnect = 0);
333 void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator);
334 void loop();
335 bool empty() { return this->count() == 0; }
336
337 size_t count() const { return this->sessions_.size(); }
338
339 protected:
340 std::string url_;
341 // Use vector instead of set: SSE sessions are typically 1-5 connections (browsers, dashboards).
342 // Linear search is faster than red-black tree overhead for this small dataset.
343 // Only operations needed: add session, remove session, iterate sessions - no need for sorted order.
344 std::vector<AsyncEventSourceResponse *> sessions_;
345 connect_handler_t on_connect_{};
347};
348#endif // USE_WEBSERVER
349
351 const char *name;
352 const char *value;
353};
354
357#ifdef USE_WEBSERVER
359#endif
360
361 public:
362 // NOLINTNEXTLINE(readability-identifier-naming)
363 void addHeader(const char *name, const char *value) { this->headers_.push_back({name, value}); }
364
365 // NOLINTNEXTLINE(readability-identifier-naming)
366 static DefaultHeaders &Instance();
367
368 protected:
369 // Stack-allocated, no reallocation machinery. Count defined in web_server_base where headers are added.
371};
372
373} // namespace web_server_idf
374} // namespace esphome
375
376using namespace esphome::web_server_idf; // NOLINT(google-global-names-in-headers)
377
378#endif // !defined(USE_ESP32)
Minimal static vector - saves memory by avoiding std::vector overhead.
Definition helpers.h:209
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:190
AsyncEventSource(std::string url, esphome::web_server::WebServer *ws)
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
StringRef url_to(std::span< char, URL_BUF_SIZE > buffer) const
Write URL (without query string) to buffer, returns StringRef pointing to buffer.
void send(AsyncWebServerResponse *response)
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
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__
const char * message
Definition component.cpp:38
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
std::string size_t len
Definition helpers.h:817
size_t size
Definition helpers.h:854
size_t size_t const char * fmt
Definition helpers.h:855
struct ESPDEPRECATED("Use std::index_sequence instead. Removed in 2026.6.0", "2025.12.0") seq
Definition automation.h:26
bool operator==(const DeferredEvent &test) const
DeferredEvent(void *source, message_generator_t *message_generator)
std::string print()