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