ESPHome 2026.1.0-dev
Loading...
Searching...
No Matches
http_request.h
Go to the documentation of this file.
1#pragma once
2
3#include <list>
4#include <map>
5#include <memory>
6#include <set>
7#include <utility>
8#include <vector>
9
16#include "esphome/core/log.h"
17
18namespace esphome::http_request {
19
20struct Header {
21 std::string name;
22 std::string value;
23};
24
25// Some common HTTP status codes
52
59inline bool is_redirect(int const status) {
60 switch (status) {
66 return true;
67 default:
68 return false;
69 }
70}
71
80inline bool is_success(int const status) { return status >= HTTP_STATUS_OK && status < HTTP_STATUS_MULTIPLE_CHOICES; }
81
82class HttpRequestComponent;
83
84class HttpContainer : public Parented<HttpRequestComponent> {
85 public:
86 virtual ~HttpContainer() = default;
89 uint32_t duration_ms;
90
91 virtual int read(uint8_t *buf, size_t max_len) = 0;
92 virtual void end() = 0;
93
94 void set_secure(bool secure) { this->secure_ = secure; }
95
96 size_t get_bytes_read() const { return this->bytes_read_; }
97
103 std::map<std::string, std::list<std::string>> get_response_headers() { return this->response_headers_; }
104
105 std::string get_response_header(const std::string &header_name);
106
107 protected:
108 size_t bytes_read_{0};
109 bool secure_{false};
110 std::map<std::string, std::list<std::string>> response_headers_{};
111};
112
113class HttpRequestResponseTrigger : public Trigger<std::shared_ptr<HttpContainer>, std::string &> {
114 public:
115 void process(const std::shared_ptr<HttpContainer> &container, std::string &response_body) {
116 this->trigger(container, response_body);
117 }
118};
119
121 public:
122 void dump_config() override;
123 float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
124
125 void set_useragent(const char *useragent) { this->useragent_ = useragent; }
126 void set_timeout(uint32_t timeout) { this->timeout_ = timeout; }
127 void set_watchdog_timeout(uint32_t watchdog_timeout) { this->watchdog_timeout_ = watchdog_timeout; }
128 uint32_t get_watchdog_timeout() const { return this->watchdog_timeout_; }
129 void set_follow_redirects(bool follow_redirects) { this->follow_redirects_ = follow_redirects; }
130 void set_redirect_limit(uint16_t limit) { this->redirect_limit_ = limit; }
131
132 std::shared_ptr<HttpContainer> get(const std::string &url) { return this->start(url, "GET", "", {}); }
133 std::shared_ptr<HttpContainer> get(const std::string &url, const std::list<Header> &request_headers) {
134 return this->start(url, "GET", "", request_headers);
135 }
136 std::shared_ptr<HttpContainer> get(const std::string &url, const std::list<Header> &request_headers,
137 const std::set<std::string> &collect_headers) {
138 return this->start(url, "GET", "", request_headers, collect_headers);
139 }
140 std::shared_ptr<HttpContainer> post(const std::string &url, const std::string &body) {
141 return this->start(url, "POST", body, {});
142 }
143 std::shared_ptr<HttpContainer> post(const std::string &url, const std::string &body,
144 const std::list<Header> &request_headers) {
145 return this->start(url, "POST", body, request_headers);
146 }
147 std::shared_ptr<HttpContainer> post(const std::string &url, const std::string &body,
148 const std::list<Header> &request_headers,
149 const std::set<std::string> &collect_headers) {
150 return this->start(url, "POST", body, request_headers, collect_headers);
151 }
152
153 std::shared_ptr<HttpContainer> start(const std::string &url, const std::string &method, const std::string &body,
154 const std::list<Header> &request_headers) {
155 return this->start(url, method, body, request_headers, {});
156 }
157
158 std::shared_ptr<HttpContainer> start(const std::string &url, const std::string &method, const std::string &body,
159 const std::list<Header> &request_headers,
160 const std::set<std::string> &collect_headers) {
161 std::set<std::string> lower_case_collect_headers;
162 for (const std::string &collect_header : collect_headers) {
163 lower_case_collect_headers.insert(str_lower_case(collect_header));
164 }
165 return this->perform(url, method, body, request_headers, lower_case_collect_headers);
166 }
167
168 protected:
169 virtual std::shared_ptr<HttpContainer> perform(const std::string &url, const std::string &method,
170 const std::string &body, const std::list<Header> &request_headers,
171 const std::set<std::string> &collect_headers) = 0;
172 const char *useragent_{nullptr};
174 uint16_t redirect_limit_{};
175 uint32_t timeout_{4500};
176 uint32_t watchdog_timeout_{0};
177};
178
179template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> {
180 public:
182 TEMPLATABLE_VALUE(std::string, url)
183 TEMPLATABLE_VALUE(const char *, method)
184 TEMPLATABLE_VALUE(std::string, body)
185#ifdef USE_HTTP_REQUEST_RESPONSE
186 TEMPLATABLE_VALUE(bool, capture_response)
187#endif
188
190 this->request_headers_.insert({key, value});
191 }
192
193 void add_collect_header(const char *value) { this->collect_headers_.insert(value); }
194
195 void add_json(const char *key, TemplatableValue<std::string, Ts...> value) { this->json_.insert({key, value}); }
196
197 void set_json(std::function<void(Ts..., JsonObject)> json_func) { this->json_func_ = json_func; }
198
199#ifdef USE_HTTP_REQUEST_RESPONSE
203#endif
205
206 Trigger<Ts...> *get_error_trigger() const { return this->error_trigger_; }
207
208 void set_max_response_buffer_size(size_t max_response_buffer_size) {
209 this->max_response_buffer_size_ = max_response_buffer_size;
210 }
211
212 void play(const Ts &...x) override {
213 std::string body;
214 if (this->body_.has_value()) {
215 body = this->body_.value(x...);
216 }
217 if (!this->json_.empty()) {
218 auto f = std::bind(&HttpRequestSendAction<Ts...>::encode_json_, this, x..., std::placeholders::_1);
219 body = json::build_json(f);
220 }
221 if (this->json_func_ != nullptr) {
222 auto f = std::bind(&HttpRequestSendAction<Ts...>::encode_json_func_, this, x..., std::placeholders::_1);
223 body = json::build_json(f);
224 }
225 std::list<Header> request_headers;
226 for (const auto &item : this->request_headers_) {
227 auto val = item.second;
228 Header header;
229 header.name = item.first;
230 header.value = val.value(x...);
231 request_headers.push_back(header);
232 }
233
234 auto container = this->parent_->start(this->url_.value(x...), this->method_.value(x...), body, request_headers,
235 this->collect_headers_);
236
237 auto captured_args = std::make_tuple(x...);
238
239 if (container == nullptr) {
240 std::apply([this](Ts... captured_args_inner) { this->error_trigger_->trigger(captured_args_inner...); },
241 captured_args);
242 return;
243 }
244
245 size_t content_length = container->content_length;
246 size_t max_length = std::min(content_length, this->max_response_buffer_size_);
247
248#ifdef USE_HTTP_REQUEST_RESPONSE
249 if (this->capture_response_.value(x...)) {
250 std::string response_body;
251 RAMAllocator<uint8_t> allocator;
252 uint8_t *buf = allocator.allocate(max_length);
253 if (buf != nullptr) {
254 size_t read_index = 0;
255 while (container->get_bytes_read() < max_length) {
256 int read = container->read(buf + read_index, std::min<size_t>(max_length - read_index, 512));
257 if (read <= 0) {
258 break;
259 }
260 App.feed_wdt();
261 yield();
262 read_index += read;
263 }
264 response_body.reserve(read_index);
265 response_body.assign((char *) buf, read_index);
266 allocator.deallocate(buf, max_length);
267 }
268 std::apply(
269 [this, &container, &response_body](Ts... captured_args_inner) {
270 this->success_trigger_with_response_->trigger(container, response_body, captured_args_inner...);
271 },
272 captured_args);
273 } else
274#endif
275 {
276 std::apply([this, &container](
277 Ts... captured_args_inner) { this->success_trigger_->trigger(container, captured_args_inner...); },
278 captured_args);
279 }
280 container->end();
281 }
282
283 protected:
284 void encode_json_(Ts... x, JsonObject root) {
285 for (const auto &item : this->json_) {
286 auto val = item.second;
287 root[item.first] = val.value(x...);
288 }
289 }
290 void encode_json_func_(Ts... x, JsonObject root) { this->json_func_(x..., root); }
292 std::map<const char *, TemplatableValue<const char *, Ts...>> request_headers_{};
293 std::set<std::string> collect_headers_{"content-type", "content-length"};
294 std::map<const char *, TemplatableValue<std::string, Ts...>> json_{};
295 std::function<void(Ts..., JsonObject)> json_func_{nullptr};
296#ifdef USE_HTTP_REQUEST_RESPONSE
298 new Trigger<std::shared_ptr<HttpContainer>, std::string &, Ts...>();
299#endif
302 Trigger<Ts...> *error_trigger_ = new Trigger<Ts...>();
303
305};
306
307} // namespace esphome::http_request
uint8_t status
Definition bl0942.h:8
void feed_wdt(uint32_t time=0)
Helper class to easily give an object a parent of type T.
Definition helpers.h:1102
An STL allocator that uses SPI or internal RAM.
Definition helpers.h:1274
void deallocate(T *p, size_t n)
Definition helpers.h:1332
T * allocate(size_t n)
Definition helpers.h:1294
std::string get_response_header(const std::string &header_name)
std::map< std::string, std::list< std::string > > get_response_headers()
Get response headers.
virtual int read(uint8_t *buf, size_t max_len)=0
std::map< std::string, std::list< std::string > > response_headers_
std::shared_ptr< HttpContainer > post(const std::string &url, const std::string &body, const std::list< Header > &request_headers, const std::set< std::string > &collect_headers)
std::shared_ptr< HttpContainer > post(const std::string &url, const std::string &body)
std::shared_ptr< HttpContainer > post(const std::string &url, const std::string &body, const std::list< Header > &request_headers)
void set_useragent(const char *useragent)
virtual std::shared_ptr< HttpContainer > perform(const std::string &url, const std::string &method, const std::string &body, const std::list< Header > &request_headers, const std::set< std::string > &collect_headers)=0
std::shared_ptr< HttpContainer > start(const std::string &url, const std::string &method, const std::string &body, const std::list< Header > &request_headers, const std::set< std::string > &collect_headers)
std::shared_ptr< HttpContainer > get(const std::string &url, const std::list< Header > &request_headers)
void set_follow_redirects(bool follow_redirects)
std::shared_ptr< HttpContainer > start(const std::string &url, const std::string &method, const std::string &body, const std::list< Header > &request_headers)
void set_watchdog_timeout(uint32_t watchdog_timeout)
std::shared_ptr< HttpContainer > get(const std::string &url, const std::list< Header > &request_headers, const std::set< std::string > &collect_headers)
std::shared_ptr< HttpContainer > get(const std::string &url)
void process(const std::shared_ptr< HttpContainer > &container, std::string &response_body)
void set_max_response_buffer_size(size_t max_response_buffer_size)
Trigger< std::shared_ptr< HttpContainer >, Ts... > * success_trigger_
std::map< const char *, TemplatableValue< std::string, Ts... > > json_
method capture_response void add_request_header(const char *key, TemplatableValue< const char *, Ts... > value)
TEMPLATABLE_VALUE(std::string, url) TEMPLATABLE_VALUE(const char *
Trigger< std::shared_ptr< HttpContainer >, std::string &, Ts... > * success_trigger_with_response_
Trigger< std::shared_ptr< HttpContainer >, std::string &, Ts... > * get_success_trigger_with_response() const
std::function< void(Ts..., JsonObject)> json_func_
Trigger< std::shared_ptr< HttpContainer >, Ts... > * get_success_trigger() const
void encode_json_func_(Ts... x, JsonObject root)
void set_json(std::function< void(Ts..., JsonObject)> json_func)
void add_json(const char *key, TemplatableValue< std::string, Ts... > value)
void encode_json_(Ts... x, JsonObject root)
Trigger< Ts... > * get_error_trigger() const
std::map< const char *, TemplatableValue< const char *, Ts... > > request_headers_
HttpRequestSendAction(HttpRequestComponent *parent)
mopeka_std_values val[4]
bool is_success(int const status)
Checks if the given HTTP status code indicates a successful request.
bool is_redirect(int const status)
Returns true if the HTTP status code is a redirect.
std::string build_json(const json_build_t &f)
Build a JSON string with the provided json build function.
Definition json_util.cpp:18
const float AFTER_WIFI
For components that should be initialized after WiFi is connected.
Definition component.cpp:88
std::string str_lower_case(const std::string &str)
Convert the string to lower case.
Definition helpers.cpp:190
void IRAM_ATTR HOT yield()
Definition core.cpp:24
Application App
Global storage of Application pointer - only one Application can exist.
uint16_t x
Definition tt21100.cpp:5