ESPHome 2026.1.2
Loading...
Searching...
No Matches
web_server_idf.h
Go to the documentation of this file.
1#pragma once
2#ifdef USE_ESP32
6#include <esp_http_server.h>
8#include <atomic>
9#include <functional>
10#include <list>
11#include <map>
12#include <string>
13#include <utility>
14#include <vector>
15
16namespace esphome {
17#ifdef USE_WEBSERVER
18namespace web_server {
19class WebServer;
20class ListEntitiesIterator;
21}; // namespace web_server
22#endif
23namespace web_server_idf {
24
26 public:
27 AsyncWebParameter(std::string name, std::string value) : name_(std::move(name)), value_(std::move(value)) {}
28 const std::string &name() const { return this->name_; }
29 const std::string &value() const { return this->value_; }
30
31 protected:
32 std::string name_;
33 std::string value_;
34};
35
37
39 public:
42
43 // NOLINTNEXTLINE(readability-identifier-naming)
44 void addHeader(const char *name, const char *value);
45
46 virtual const char *get_content_data() const = 0;
47 virtual size_t get_content_size() const = 0;
48
49 protected:
51};
52
54 public:
56
57 const char *get_content_data() const override { return nullptr; };
58 size_t get_content_size() const override { return 0; };
59};
60
62 public:
64 : AsyncWebServerResponse(req), content_(std::move(content)) {}
65
66 const char *get_content_data() const override { return this->content_.c_str(); };
67 size_t get_content_size() const override { return this->content_.size(); };
68
69 protected:
70 std::string content_;
71};
72
74 public:
76
77 const char *get_content_data() const override { return this->content_.c_str(); };
78 size_t get_content_size() const override { return this->content_.size(); };
79
80 void print(const char *str) { this->content_.append(str); }
81 void print(const std::string &str) { this->content_.append(str); }
82 void print(float value);
83 void printf(const char *fmt, ...) __attribute__((format(printf, 2, 3)));
84 void write(uint8_t c) { this->content_.push_back(static_cast<char>(c)); }
85
86 protected:
87 std::string content_;
88};
89
91 public:
92 AsyncWebServerResponseProgmem(const AsyncWebServerRequest *req, const uint8_t *data, const size_t size)
93 : AsyncWebServerResponse(req), data_(data), size_(size) {}
94
95 const char *get_content_data() const override { return reinterpret_cast<const char *>(this->data_); };
96 size_t get_content_size() const override { return this->size_; };
97
98 protected:
99 const uint8_t *data_;
100 size_t size_;
101};
102
104 friend class AsyncWebServer;
105
106 public:
108
109 http_method method() const { return static_cast<http_method>(this->req_->method); }
110 std::string url() const;
111 std::string host() const;
112 // NOLINTNEXTLINE(readability-identifier-naming)
113 size_t contentLength() const { return this->req_->content_len; }
114
115#ifdef USE_WEBSERVER_AUTH
116 bool authenticate(const char *username, const char *password) const;
117 // NOLINTNEXTLINE(readability-identifier-naming)
118 void requestAuthentication(const char *realm = nullptr) const;
119#endif
120
121 void redirect(const std::string &url);
122
123 void send(AsyncWebServerResponse *response);
124 void send(int code, const char *content_type = nullptr, const char *content = nullptr);
125 // NOLINTNEXTLINE(readability-identifier-naming)
126 AsyncWebServerResponse *beginResponse(int code, const char *content_type) {
127 auto *res = new AsyncWebServerResponseEmpty(this); // NOLINT(cppcoreguidelines-owning-memory)
128 this->init_response_(res, code, content_type);
129 return res;
130 }
131 // NOLINTNEXTLINE(readability-identifier-naming)
132 AsyncWebServerResponse *beginResponse(int code, const char *content_type, const std::string &content) {
133 auto *res = new AsyncWebServerResponseContent(this, content); // NOLINT(cppcoreguidelines-owning-memory)
134 this->init_response_(res, code, content_type);
135 return res;
136 }
137 // NOLINTNEXTLINE(readability-identifier-naming)
138 AsyncWebServerResponse *beginResponse(int code, const char *content_type, const uint8_t *data,
139 const size_t data_size) {
140 auto *res = new AsyncWebServerResponseProgmem(this, data, data_size); // NOLINT(cppcoreguidelines-owning-memory)
141 this->init_response_(res, code, content_type);
142 return res;
143 }
144 // NOLINTNEXTLINE(readability-identifier-naming)
145 AsyncResponseStream *beginResponseStream(const char *content_type) {
146 auto *res = new AsyncResponseStream(this); // NOLINT(cppcoreguidelines-owning-memory)
147 this->init_response_(res, 200, content_type);
148 return res;
149 }
150
151 // NOLINTNEXTLINE(readability-identifier-naming)
152 bool hasParam(const std::string &name) { return this->getParam(name) != nullptr; }
153 // NOLINTNEXTLINE(readability-identifier-naming)
154 AsyncWebParameter *getParam(const std::string &name);
155
156 // NOLINTNEXTLINE(readability-identifier-naming)
157 bool hasArg(const char *name) { return this->hasParam(name); }
158 std::string arg(const std::string &name) {
159 auto *param = this->getParam(name);
160 if (param) {
161 return param->value();
162 }
163 return {};
164 }
165
166 operator httpd_req_t *() const { return this->req_; }
167 optional<std::string> get_header(const char *name) const;
168 // NOLINTNEXTLINE(readability-identifier-naming)
169 bool hasHeader(const char *name) const;
170
171 protected:
172 httpd_req_t *req_;
174 // Use vector instead of map/unordered_map: most requests have 0-3 params, so linear search
175 // is faster than tree/hash overhead. AsyncWebParameter stores both name and value to avoid
176 // duplicate storage. Only successful lookups are cached to prevent cache pollution when
177 // handlers check for optional parameters that don't exist.
178 std::vector<AsyncWebParameter *> params_;
179 std::string post_query_;
180 AsyncWebServerRequest(httpd_req_t *req) : req_(req) {}
181 AsyncWebServerRequest(httpd_req_t *req, std::string post_query) : req_(req), post_query_(std::move(post_query)) {}
182 void init_response_(AsyncWebServerResponse *rsp, int code, const char *content_type);
183};
184
185class AsyncWebHandler;
186
188 public:
189 AsyncWebServer(uint16_t port) : port_(port){};
190 ~AsyncWebServer() { this->end(); }
191
192 // NOLINTNEXTLINE(readability-identifier-naming)
193 void onNotFound(std::function<void(AsyncWebServerRequest *request)> fn) { on_not_found_ = std::move(fn); }
194
195 void begin();
196 void end();
197
198 // NOLINTNEXTLINE(readability-identifier-naming)
200 this->handlers_.push_back(handler);
201 return *handler;
202 }
203
204 httpd_handle_t get_server() { return this->server_; }
205
206 protected:
207 uint16_t port_{};
208 httpd_handle_t server_{};
209 static esp_err_t request_handler(httpd_req_t *r);
210 static esp_err_t request_post_handler(httpd_req_t *r);
211 esp_err_t request_handler_(AsyncWebServerRequest *request) const;
212 static void safe_close_with_shutdown(httpd_handle_t hd, int sockfd);
213#ifdef USE_WEBSERVER_OTA
214 esp_err_t handle_multipart_upload_(httpd_req_t *r, const char *content_type);
215#endif
216 std::vector<AsyncWebHandler *> handlers_;
217 std::function<void(AsyncWebServerRequest *request)> on_not_found_{};
218};
219
221 public:
222 virtual ~AsyncWebHandler() {}
223 // NOLINTNEXTLINE(readability-identifier-naming)
224 virtual bool canHandle(AsyncWebServerRequest *request) const { return false; }
225 // NOLINTNEXTLINE(readability-identifier-naming)
226 virtual void handleRequest(AsyncWebServerRequest *request) {}
227 // NOLINTNEXTLINE(readability-identifier-naming)
228 virtual void handleUpload(AsyncWebServerRequest *request, const std::string &filename, size_t index, uint8_t *data,
229 size_t len, bool final) {}
230 // NOLINTNEXTLINE(readability-identifier-naming)
231 virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {}
232 // NOLINTNEXTLINE(readability-identifier-naming)
233 virtual bool isRequestHandlerTrivial() const { return true; }
234};
235
236#ifdef USE_WEBSERVER
237class AsyncEventSource;
239
241
242/*
243 This class holds a pointer to the source component that wants to publish a state event, and a pointer to a function
244 that will lazily generate that event. The two pointers allow dedup in the deferred queue if multiple publishes for
245 the same component are backed up, and take up only 8 bytes of memory. The entry in the deferred queue (a
246 std::vector) is the DeferredEvent instance itself (not a pointer to one elsewhere in heap) so still only 8 bytes per
247 entry (and no heap fragmentation). Even 100 backed up events (you'd have to have at least 100 sensors publishing
248 because of dedup) would take up only 0.8 kB.
249*/
252
253 protected:
254 void *source_;
256
257 public:
258 DeferredEvent(void *source, message_generator_t *message_generator)
259 : source_(source), message_generator_(message_generator) {}
260 bool operator==(const DeferredEvent &test) const {
261 return (source_ == test.source_ && message_generator_ == test.message_generator_);
262 }
263} __attribute__((packed));
264
266 friend class AsyncEventSource;
267
268 public:
269 bool try_send_nodefer(const char *message, const char *event = nullptr, uint32_t id = 0, uint32_t reconnect = 0);
270 void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator);
271 void loop();
272
273 protected:
276
277 void deq_push_back_with_dedup_(void *source, message_generator_t *message_generator);
279 void process_buffer_();
280
281 static void destroy(void *p);
283 httpd_handle_t hd_{};
284 std::atomic<int> fd_{};
285 std::vector<DeferredEvent> deferred_queue_;
287 std::unique_ptr<esphome::web_server::ListEntitiesIterator> entities_iterator_;
288 std::string event_buffer_{""};
291 static constexpr uint16_t MAX_CONSECUTIVE_SEND_FAILURES = 2500; // ~20 seconds at 125Hz loop rate
293
295
298 using connect_handler_t = std::function<void(AsyncEventSourceClient *)>;
299
300 public:
301 AsyncEventSource(std::string url, esphome::web_server::WebServer *ws) : url_(std::move(url)), web_server_(ws) {}
302 ~AsyncEventSource() override;
303
304 // NOLINTNEXTLINE(readability-identifier-naming)
305 bool canHandle(AsyncWebServerRequest *request) const override {
306 return request->method() == HTTP_GET && request->url() == this->url_;
307 }
308 // NOLINTNEXTLINE(readability-identifier-naming)
309 void handleRequest(AsyncWebServerRequest *request) override;
310 // NOLINTNEXTLINE(readability-identifier-naming)
311 void onConnect(connect_handler_t cb) { this->on_connect_ = std::move(cb); }
312
313 void try_send_nodefer(const char *message, const char *event = nullptr, uint32_t id = 0, uint32_t reconnect = 0);
314 void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator);
315 void loop();
316 bool empty() { return this->count() == 0; }
317
318 size_t count() const { return this->sessions_.size(); }
319
320 protected:
321 std::string url_;
322 // Use vector instead of set: SSE sessions are typically 1-5 connections (browsers, dashboards).
323 // Linear search is faster than red-black tree overhead for this small dataset.
324 // Only operations needed: add session, remove session, iterate sessions - no need for sorted order.
325 std::vector<AsyncEventSourceResponse *> sessions_;
326 connect_handler_t on_connect_{};
328};
329#endif // USE_WEBSERVER
330
332 const char *name;
333 const char *value;
334};
335
338#ifdef USE_WEBSERVER
340#endif
341
342 public:
343 // NOLINTNEXTLINE(readability-identifier-naming)
344 void addHeader(const char *name, const char *value) { this->headers_.push_back({name, value}); }
345
346 // NOLINTNEXTLINE(readability-identifier-naming)
347 static DefaultHeaders &Instance();
348
349 protected:
350 // Stack-allocated, no reallocation machinery. Count defined in web_server_base where headers are added.
352};
353
354} // namespace web_server_idf
355} // namespace esphome
356
357using namespace esphome::web_server_idf; // NOLINT(google-global-names-in-headers)
358
359#endif // !defined(USE_ESP32)
Minimal static vector - saves memory by avoiding std::vector overhead.
Definition helpers.h:132
This class allows users to create a web server with their ESP nodes.
Definition web_server.h:187
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 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)
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)
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)
void onNotFound(std::function< void(AsyncWebServerRequest *request)> fn)
static esp_err_t request_handler(httpd_req_t *r)
AsyncWebParameter * getParam(const std::string &name)
AsyncWebServerResponse * beginResponse(int code, const char *content_type, const uint8_t *data, const size_t data_size)
optional< std::string > get_header(const char *name) const
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)
void requestAuthentication(const char *realm=nullptr) const
AsyncResponseStream * beginResponseStream(const char *content_type)
bool authenticate(const char *username, const char *password) const
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_
const char * message
Definition component.cpp:38
std::string(esphome::web_server::WebServer *, void *) message_generator_t
class esphome::web_server_idf::AsyncEventSourceResponse __attribute__
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string size_t len
Definition helpers.h:595
bool operator==(const DeferredEvent &test) const
DeferredEvent(void *source, message_generator_t *message_generator)
std::string print()
friend class AsyncEventSourceResponse