ESPHome 2026.2.0
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
20#endif
21
22namespace esphome {
23#ifdef USE_WEBSERVER
24namespace web_server {
25class WebServer;
26}; // namespace web_server
27#endif
28namespace web_server_idf {
29
31 public:
32 AsyncWebParameter(std::string name, std::string value) : name_(std::move(name)), value_(std::move(value)) {}
33 const std::string &name() const { return this->name_; }
34 const std::string &value() const { return this->value_; }
35
36 protected:
37 std::string name_;
38 std::string value_;
39};
40
42
44 public:
47
48 // NOLINTNEXTLINE(readability-identifier-naming)
49 void addHeader(const char *name, const char *value);
50
51 virtual const char *get_content_data() const = 0;
52 virtual size_t get_content_size() const = 0;
53
54 protected:
56};
57
59 public:
61
62 const char *get_content_data() const override { return nullptr; };
63 size_t get_content_size() const override { return 0; };
64};
65
67 public:
69 : AsyncWebServerResponse(req), content_(std::move(content)) {}
70
71 const char *get_content_data() const override { return this->content_.c_str(); };
72 size_t get_content_size() const override { return this->content_.size(); };
73
74 protected:
75 std::string content_;
76};
77
79 public:
81
82 const char *get_content_data() const override { return this->content_.c_str(); };
83 size_t get_content_size() const override { return this->content_.size(); };
84
85 void print(const char *str) { this->content_.append(str); }
86 void print(const std::string &str) { this->content_.append(str); }
87 void print(float value);
88 void printf(const char *fmt, ...) __attribute__((format(printf, 2, 3)));
89 void write(uint8_t c) { this->content_.push_back(static_cast<char>(c)); }
90
91 protected:
92 std::string content_;
93};
94
96 public:
97 AsyncWebServerResponseProgmem(const AsyncWebServerRequest *req, const uint8_t *data, const size_t size)
98 : AsyncWebServerResponse(req), data_(data), size_(size) {}
99
100 const char *get_content_data() const override { return reinterpret_cast<const char *>(this->data_); };
101 size_t get_content_size() const override { return this->size_; };
102
103 protected:
104 const uint8_t *data_;
105 size_t size_;
106};
107
109 friend class AsyncWebServer;
110
111 public:
113
114 http_method method() const { return static_cast<http_method>(this->req_->method); }
115 static constexpr size_t URL_BUF_SIZE = CONFIG_HTTPD_MAX_URI_LEN + 1;
118 StringRef url_to(std::span<char, URL_BUF_SIZE> buffer) const;
120 std::string url() const {
121 char buffer[URL_BUF_SIZE];
122 return std::string(this->url_to(buffer));
123 }
124 // NOLINTNEXTLINE(readability-identifier-naming)
125 size_t contentLength() const { return this->req_->content_len; }
126
127#ifdef USE_WEBSERVER_AUTH
128 bool authenticate(const char *username, const char *password) const;
129 // NOLINTNEXTLINE(readability-identifier-naming)
130 void requestAuthentication(const char *realm = nullptr) const;
131#endif
132
133 void redirect(const std::string &url);
134
135 void send(AsyncWebServerResponse *response);
136 void send(int code, const char *content_type = nullptr, const char *content = nullptr);
137 // NOLINTNEXTLINE(readability-identifier-naming)
138 AsyncWebServerResponse *beginResponse(int code, const char *content_type) {
139 auto *res = new AsyncWebServerResponseEmpty(this); // NOLINT(cppcoreguidelines-owning-memory)
140 this->init_response_(res, code, content_type);
141 return res;
142 }
143 // NOLINTNEXTLINE(readability-identifier-naming)
144 AsyncWebServerResponse *beginResponse(int code, const char *content_type, const std::string &content) {
145 auto *res = new AsyncWebServerResponseContent(this, content); // NOLINT(cppcoreguidelines-owning-memory)
146 this->init_response_(res, code, content_type);
147 return res;
148 }
149 // NOLINTNEXTLINE(readability-identifier-naming)
150 AsyncWebServerResponse *beginResponse(int code, const char *content_type, const uint8_t *data,
151 const size_t data_size) {
152 auto *res = new AsyncWebServerResponseProgmem(this, data, data_size); // NOLINT(cppcoreguidelines-owning-memory)
153 this->init_response_(res, code, content_type);
154 return res;
155 }
156 // NOLINTNEXTLINE(readability-identifier-naming)
157 AsyncResponseStream *beginResponseStream(const char *content_type) {
158 auto *res = new AsyncResponseStream(this); // NOLINT(cppcoreguidelines-owning-memory)
159 this->init_response_(res, 200, content_type);
160 return res;
161 }
162
163 // NOLINTNEXTLINE(readability-identifier-naming)
164 bool hasParam(const char *name) { return this->getParam(name) != nullptr; }
165 // NOLINTNEXTLINE(readability-identifier-naming)
166 bool hasParam(const std::string &name) { return this->getParam(name.c_str()) != nullptr; }
167 // NOLINTNEXTLINE(readability-identifier-naming)
168 AsyncWebParameter *getParam(const char *name);
169 // NOLINTNEXTLINE(readability-identifier-naming)
170 AsyncWebParameter *getParam(const std::string &name) { return this->getParam(name.c_str()); }
171
172 // NOLINTNEXTLINE(readability-identifier-naming)
173 bool hasArg(const char *name) { return this->hasParam(name); }
174 std::string arg(const char *name) {
175 auto *param = this->getParam(name);
176 if (param) {
177 return param->value();
178 }
179 return {};
180 }
181 std::string arg(const std::string &name) { return this->arg(name.c_str()); }
182
183 operator httpd_req_t *() const { return this->req_; }
184 optional<std::string> get_header(const char *name) const;
185 // NOLINTNEXTLINE(readability-identifier-naming)
186 bool hasHeader(const char *name) const;
187
188 protected:
189 httpd_req_t *req_;
191 // Use vector instead of map/unordered_map: most requests have 0-3 params, so linear search
192 // is faster than tree/hash overhead. AsyncWebParameter stores both name and value to avoid
193 // duplicate storage. Only successful lookups are cached to prevent cache pollution when
194 // handlers check for optional parameters that don't exist.
195 std::vector<AsyncWebParameter *> params_;
196 std::string post_query_;
197 AsyncWebServerRequest(httpd_req_t *req) : req_(req) {}
198 AsyncWebServerRequest(httpd_req_t *req, std::string post_query) : req_(req), post_query_(std::move(post_query)) {}
199 void init_response_(AsyncWebServerResponse *rsp, int code, const char *content_type);
200};
201
202class AsyncWebHandler;
203
205 public:
206 AsyncWebServer(uint16_t port) : port_(port){};
207 ~AsyncWebServer() { this->end(); }
208
209 // NOLINTNEXTLINE(readability-identifier-naming)
210 void onNotFound(std::function<void(AsyncWebServerRequest *request)> fn) { on_not_found_ = std::move(fn); }
211
212 void begin();
213 void end();
214
215 // NOLINTNEXTLINE(readability-identifier-naming)
217 this->handlers_.push_back(handler);
218 return *handler;
219 }
220
221 httpd_handle_t get_server() { return this->server_; }
222
223 protected:
224 uint16_t port_{};
225 httpd_handle_t server_{};
226 static esp_err_t request_handler(httpd_req_t *r);
227 static esp_err_t request_post_handler(httpd_req_t *r);
228 esp_err_t request_handler_(AsyncWebServerRequest *request) const;
229 static void safe_close_with_shutdown(httpd_handle_t hd, int sockfd);
230#ifdef USE_WEBSERVER_OTA
231 esp_err_t handle_multipart_upload_(httpd_req_t *r, const char *content_type);
232#endif
233 std::vector<AsyncWebHandler *> handlers_;
234 std::function<void(AsyncWebServerRequest *request)> on_not_found_{};
235};
236
238 public:
239 virtual ~AsyncWebHandler() {}
240 // NOLINTNEXTLINE(readability-identifier-naming)
241 virtual bool canHandle(AsyncWebServerRequest *request) const { return false; }
242 // NOLINTNEXTLINE(readability-identifier-naming)
243 virtual void handleRequest(AsyncWebServerRequest *request) {}
244 // NOLINTNEXTLINE(readability-identifier-naming)
245 virtual void handleUpload(AsyncWebServerRequest *request, const std::string &filename, size_t index, uint8_t *data,
246 size_t len, bool final) {}
247 // NOLINTNEXTLINE(readability-identifier-naming)
248 virtual void handleBody(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) {}
249 // NOLINTNEXTLINE(readability-identifier-naming)
250 virtual bool isRequestHandlerTrivial() const { return true; }
251};
252
253#ifdef USE_WEBSERVER
254class AsyncEventSource;
256
258
259/*
260 This class holds a pointer to the source component that wants to publish a state event, and a pointer to a function
261 that will lazily generate that event. The two pointers allow dedup in the deferred queue if multiple publishes for
262 the same component are backed up, and take up only two pointers of memory. The entry in the deferred queue (a
263 std::vector) is the DeferredEvent instance itself (not a pointer to one elsewhere in heap) so still only two pointers
264 per entry (and no heap fragmentation). Even 100 backed up events (you'd have to have at least 100 sensors publishing
265 because of dedup) would take up only 0.8 kB.
266*/
269
270 protected:
271 void *source_;
273
274 public:
275 DeferredEvent(void *source, message_generator_t *message_generator)
276 : source_(source), message_generator_(message_generator) {}
277 bool operator==(const DeferredEvent &test) const {
278 return (source_ == test.source_ && message_generator_ == test.message_generator_);
279 }
280};
281static_assert(sizeof(DeferredEvent) == sizeof(void *) + sizeof(message_generator_t *),
282 "DeferredEvent should have no padding");
283
285 friend class AsyncEventSource;
286
287 public:
288 bool try_send_nodefer(const char *message, const char *event = nullptr, uint32_t id = 0, uint32_t reconnect = 0);
289 void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator);
290 void loop();
291
292 protected:
295
296 void deq_push_back_with_dedup_(void *source, message_generator_t *message_generator);
298 void process_buffer_();
299
300 static void destroy(void *p);
302 httpd_handle_t hd_{};
303 std::atomic<int> fd_{};
304 std::vector<DeferredEvent> deferred_queue_;
307 std::string event_buffer_{""};
310 static constexpr uint16_t MAX_CONSECUTIVE_SEND_FAILURES = 2500; // ~20 seconds at 125Hz loop rate
311};
312
314
317 using connect_handler_t = std::function<void(AsyncEventSourceClient *)>;
318
319 public:
320 AsyncEventSource(std::string url, esphome::web_server::WebServer *ws) : url_(std::move(url)), web_server_(ws) {}
321 ~AsyncEventSource() override;
322
323 // NOLINTNEXTLINE(readability-identifier-naming)
324 bool canHandle(AsyncWebServerRequest *request) const override {
325 if (request->method() != HTTP_GET)
326 return false;
328 return request->url_to(url_buf) == this->url_;
329 }
330 // NOLINTNEXTLINE(readability-identifier-naming)
331 void handleRequest(AsyncWebServerRequest *request) override;
332 // NOLINTNEXTLINE(readability-identifier-naming)
333 void onConnect(connect_handler_t cb) { this->on_connect_ = std::move(cb); }
334
335 void try_send_nodefer(const char *message, const char *event = nullptr, uint32_t id = 0, uint32_t reconnect = 0);
336 void deferrable_send_state(void *source, const char *event_type, message_generator_t *message_generator);
337 void loop();
338 bool empty() { return this->count() == 0; }
339
340 size_t count() const { return this->sessions_.size(); }
341
342 protected:
343 std::string url_;
344 // Use vector instead of set: SSE sessions are typically 1-5 connections (browsers, dashboards).
345 // Linear search is faster than red-black tree overhead for this small dataset.
346 // Only operations needed: add session, remove session, iterate sessions - no need for sorted order.
347 std::vector<AsyncEventSourceResponse *> sessions_;
348 connect_handler_t on_connect_{};
350};
351#endif // USE_WEBSERVER
352
354 const char *name;
355 const char *value;
356};
357
360#ifdef USE_WEBSERVER
362#endif
363
364 public:
365 // NOLINTNEXTLINE(readability-identifier-naming)
366 void addHeader(const char *name, const char *value) { this->headers_.push_back({name, value}); }
367
368 // NOLINTNEXTLINE(readability-identifier-naming)
369 static DefaultHeaders &Instance();
370
371 protected:
372 // Stack-allocated, no reallocation machinery. Count defined in web_server_base where headers are added.
374};
375
376} // namespace web_server_idf
377} // namespace esphome
378
379using namespace esphome::web_server_idf; // NOLINT(google-global-names-in-headers)
380
381#endif // !defined(USE_ESP32)
Minimal static vector - saves memory by avoiding std::vector overhead.
Definition helpers.h:137
StringRef is a reference to a string owned by something else.
Definition string_ref.h:26
This class allows users to create a web server with their ESP nodes.
Definition web_server.h:196
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)
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)
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)
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()
std::string url() const
Get URL as std::string. Prefer url_to() to avoid heap allocation.
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_
struct @65::@66 __attribute__
const char * message
Definition component.cpp:38
std::string(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:692
size_t size
Definition helpers.h:729
size_t size_t const char * fmt
Definition helpers.h:730
bool operator==(const DeferredEvent &test) const
DeferredEvent(void *source, message_generator_t *message_generator)
std::string print()