ESPHome 2025.12.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 {
19namespace http_request {
20
21struct Header {
22 std::string name;
23 std::string value;
24};
25
26// Some common HTTP status codes
53
60inline bool is_redirect(int const status) {
61 switch (status) {
67 return true;
68 default:
69 return false;
70 }
71}
72
81inline bool is_success(int const status) { return status >= HTTP_STATUS_OK && status < HTTP_STATUS_MULTIPLE_CHOICES; }
82
83class HttpRequestComponent;
84
85class HttpContainer : public Parented<HttpRequestComponent> {
86 public:
87 virtual ~HttpContainer() = default;
90 uint32_t duration_ms;
91
92 virtual int read(uint8_t *buf, size_t max_len) = 0;
93 virtual void end() = 0;
94
95 void set_secure(bool secure) { this->secure_ = secure; }
96
97 size_t get_bytes_read() const { return this->bytes_read_; }
98
104 std::map<std::string, std::list<std::string>> get_response_headers() { return this->response_headers_; }
105
106 std::string get_response_header(const std::string &header_name);
107
108 protected:
109 size_t bytes_read_{0};
110 bool secure_{false};
111 std::map<std::string, std::list<std::string>> response_headers_{};
112};
113
114class HttpRequestResponseTrigger : public Trigger<std::shared_ptr<HttpContainer>, std::string &> {
115 public:
116 void process(const std::shared_ptr<HttpContainer> &container, std::string &response_body) {
117 this->trigger(container, response_body);
118 }
119};
120
122 public:
123 void dump_config() override;
124 float get_setup_priority() const override { return setup_priority::AFTER_WIFI; }
125
126 void set_useragent(const char *useragent) { this->useragent_ = useragent; }
127 void set_timeout(uint32_t timeout) { this->timeout_ = timeout; }
128 void set_watchdog_timeout(uint32_t watchdog_timeout) { this->watchdog_timeout_ = watchdog_timeout; }
129 uint32_t get_watchdog_timeout() const { return this->watchdog_timeout_; }
130 void set_follow_redirects(bool follow_redirects) { this->follow_redirects_ = follow_redirects; }
131 void set_redirect_limit(uint16_t limit) { this->redirect_limit_ = limit; }
132
133 std::shared_ptr<HttpContainer> get(const std::string &url) { return this->start(url, "GET", "", {}); }
134 std::shared_ptr<HttpContainer> get(const std::string &url, const std::list<Header> &request_headers) {
135 return this->start(url, "GET", "", request_headers);
136 }
137 std::shared_ptr<HttpContainer> get(const std::string &url, const std::list<Header> &request_headers,
138 const std::set<std::string> &collect_headers) {
139 return this->start(url, "GET", "", request_headers, collect_headers);
140 }
141 std::shared_ptr<HttpContainer> post(const std::string &url, const std::string &body) {
142 return this->start(url, "POST", body, {});
143 }
144 std::shared_ptr<HttpContainer> post(const std::string &url, const std::string &body,
145 const std::list<Header> &request_headers) {
146 return this->start(url, "POST", body, request_headers);
147 }
148 std::shared_ptr<HttpContainer> post(const std::string &url, const std::string &body,
149 const std::list<Header> &request_headers,
150 const std::set<std::string> &collect_headers) {
151 return this->start(url, "POST", body, request_headers, collect_headers);
152 }
153
154 std::shared_ptr<HttpContainer> start(const std::string &url, const std::string &method, const std::string &body,
155 const std::list<Header> &request_headers) {
156 return this->start(url, method, body, request_headers, {});
157 }
158
159 std::shared_ptr<HttpContainer> start(const std::string &url, const std::string &method, const std::string &body,
160 const std::list<Header> &request_headers,
161 const std::set<std::string> &collect_headers) {
162 std::set<std::string> lower_case_collect_headers;
163 for (const std::string &collect_header : collect_headers) {
164 lower_case_collect_headers.insert(str_lower_case(collect_header));
165 }
166 return this->perform(url, method, body, request_headers, lower_case_collect_headers);
167 }
168
169 protected:
170 virtual std::shared_ptr<HttpContainer> perform(const std::string &url, const std::string &method,
171 const std::string &body, const std::list<Header> &request_headers,
172 const std::set<std::string> &collect_headers) = 0;
173 const char *useragent_{nullptr};
175 uint16_t redirect_limit_{};
176 uint32_t timeout_{4500};
177 uint32_t watchdog_timeout_{0};
178};
179
180template<typename... Ts> class HttpRequestSendAction : public Action<Ts...> {
181 public:
183 TEMPLATABLE_VALUE(std::string, url)
184 TEMPLATABLE_VALUE(const char *, method)
185 TEMPLATABLE_VALUE(std::string, body)
186#ifdef USE_HTTP_REQUEST_RESPONSE
187 TEMPLATABLE_VALUE(bool, capture_response)
188#endif
189
191 this->request_headers_.insert({key, value});
192 }
193
194 void add_collect_header(const char *value) { this->collect_headers_.insert(value); }
195
196 void add_json(const char *key, TemplatableValue<std::string, Ts...> value) { this->json_.insert({key, value}); }
197
198 void set_json(std::function<void(Ts..., JsonObject)> json_func) { this->json_func_ = json_func; }
199
200#ifdef USE_HTTP_REQUEST_RESPONSE
204#endif
206
207 Trigger<Ts...> *get_error_trigger() const { return this->error_trigger_; }
208
209 void set_max_response_buffer_size(size_t max_response_buffer_size) {
210 this->max_response_buffer_size_ = max_response_buffer_size;
211 }
212
213 void play(const Ts &...x) override {
214 std::string body;
215 if (this->body_.has_value()) {
216 body = this->body_.value(x...);
217 }
218 if (!this->json_.empty()) {
219 auto f = std::bind(&HttpRequestSendAction<Ts...>::encode_json_, this, x..., std::placeholders::_1);
220 body = json::build_json(f);
221 }
222 if (this->json_func_ != nullptr) {
223 auto f = std::bind(&HttpRequestSendAction<Ts...>::encode_json_func_, this, x..., std::placeholders::_1);
224 body = json::build_json(f);
225 }
226 std::list<Header> request_headers;
227 for (const auto &item : this->request_headers_) {
228 auto val = item.second;
229 Header header;
230 header.name = item.first;
231 header.value = val.value(x...);
232 request_headers.push_back(header);
233 }
234
235 auto container = this->parent_->start(this->url_.value(x...), this->method_.value(x...), body, request_headers,
236 this->collect_headers_);
237
238 auto captured_args = std::make_tuple(x...);
239
240 if (container == nullptr) {
241 std::apply([this](Ts... captured_args_inner) { this->error_trigger_->trigger(captured_args_inner...); },
242 captured_args);
243 return;
244 }
245
246 size_t content_length = container->content_length;
247 size_t max_length = std::min(content_length, this->max_response_buffer_size_);
248
249#ifdef USE_HTTP_REQUEST_RESPONSE
250 if (this->capture_response_.value(x...)) {
251 std::string response_body;
252 RAMAllocator<uint8_t> allocator;
253 uint8_t *buf = allocator.allocate(max_length);
254 if (buf != nullptr) {
255 size_t read_index = 0;
256 while (container->get_bytes_read() < max_length) {
257 int read = container->read(buf + read_index, std::min<size_t>(max_length - read_index, 512));
258 App.feed_wdt();
259 yield();
260 read_index += read;
261 }
262 response_body.reserve(read_index);
263 response_body.assign((char *) buf, read_index);
264 allocator.deallocate(buf, max_length);
265 }
266 std::apply(
267 [this, &container, &response_body](Ts... captured_args_inner) {
268 this->success_trigger_with_response_->trigger(container, response_body, captured_args_inner...);
269 },
270 captured_args);
271 } else
272#endif
273 {
274 std::apply([this, &container](
275 Ts... captured_args_inner) { this->success_trigger_->trigger(container, captured_args_inner...); },
276 captured_args);
277 }
278 container->end();
279 }
280
281 protected:
282 void encode_json_(Ts... x, JsonObject root) {
283 for (const auto &item : this->json_) {
284 auto val = item.second;
285 root[item.first] = val.value(x...);
286 }
287 }
288 void encode_json_func_(Ts... x, JsonObject root) { this->json_func_(x..., root); }
290 std::map<const char *, TemplatableValue<const char *, Ts...>> request_headers_{};
291 std::set<std::string> collect_headers_{"content-type", "content-length"};
292 std::map<const char *, TemplatableValue<std::string, Ts...>> json_{};
293 std::function<void(Ts..., JsonObject)> json_func_{nullptr};
294#ifdef USE_HTTP_REQUEST_RESPONSE
296 new Trigger<std::shared_ptr<HttpContainer>, std::string &, Ts...>();
297#endif
300 Trigger<Ts...> *error_trigger_ = new Trigger<Ts...>();
301
303};
304
305} // namespace http_request
306} // namespace esphome
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:918
An STL allocator that uses SPI or internal RAM.
Definition helpers.h:1084
void deallocate(T *p, size_t n)
Definition helpers.h:1142
T * allocate(size_t n)
Definition helpers.h:1104
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:66
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
std::string str_lower_case(const std::string &str)
Convert the string to lower case.
Definition helpers.cpp:189
void IRAM_ATTR HOT yield()
Definition core.cpp:29
Application App
Global storage of Application pointer - only one Application can exist.
uint16_t x
Definition tt21100.cpp:5