ESPHome 2025.5.0
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(std::shared_ptr<HttpContainer> container, std::string &response_body) {
117 this->trigger(std::move(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(uint16_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(std::string url, std::string method, std::string body,
171 std::list<Header> request_headers,
172 std::set<std::string> collect_headers) = 0;
173 const char *useragent_{nullptr};
175 uint16_t redirect_limit_{};
176 uint16_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 TEMPLATABLE_VALUE(bool, capture_response)
187
188 void add_request_header(const char *key, TemplatableValue<const char *, Ts...> value) {
189 this->request_headers_.insert({key, value});
190 }
191
192 void add_collect_header(const char *value) { this->collect_headers_.insert(value); }
193
194 void add_json(const char *key, TemplatableValue<std::string, Ts...> value) { this->json_.insert({key, value}); }
195
196 void set_json(std::function<void(Ts..., JsonObject)> json_func) { this->json_func_ = json_func; }
197
199
200 void register_error_trigger(Trigger<> *trigger) { this->error_triggers_.push_back(trigger); }
201
202 void set_max_response_buffer_size(size_t max_response_buffer_size) {
203 this->max_response_buffer_size_ = max_response_buffer_size;
204 }
205
206 void play(Ts... x) override {
207 std::string body;
208 if (this->body_.has_value()) {
209 body = this->body_.value(x...);
210 }
211 if (!this->json_.empty()) {
212 auto f = std::bind(&HttpRequestSendAction<Ts...>::encode_json_, this, x..., std::placeholders::_1);
213 body = json::build_json(f);
214 }
215 if (this->json_func_ != nullptr) {
216 auto f = std::bind(&HttpRequestSendAction<Ts...>::encode_json_func_, this, x..., std::placeholders::_1);
217 body = json::build_json(f);
218 }
219 std::list<Header> request_headers;
220 for (const auto &item : this->request_headers_) {
221 auto val = item.second;
222 Header header;
223 header.name = item.first;
224 header.value = val.value(x...);
225 request_headers.push_back(header);
226 }
227
228 auto container = this->parent_->start(this->url_.value(x...), this->method_.value(x...), body, request_headers,
229 this->collect_headers_);
230
231 if (container == nullptr) {
232 for (auto *trigger : this->error_triggers_)
233 trigger->trigger();
234 return;
235 }
236
237 size_t content_length = container->content_length;
238 size_t max_length = std::min(content_length, this->max_response_buffer_size_);
239
240 std::string response_body;
241 if (this->capture_response_.value(x...)) {
243 uint8_t *buf = allocator.allocate(max_length);
244 if (buf != nullptr) {
245 size_t read_index = 0;
246 while (container->get_bytes_read() < max_length) {
247 int read = container->read(buf + read_index, std::min<size_t>(max_length - read_index, 512));
248 App.feed_wdt();
249 yield();
250 read_index += read;
251 }
252 response_body.reserve(read_index);
253 response_body.assign((char *) buf, read_index);
254 allocator.deallocate(buf, max_length);
255 }
256 }
257
258 if (this->response_triggers_.size() == 1) {
259 // if there is only one trigger, no need to copy the response body
260 this->response_triggers_[0]->process(container, response_body);
261 } else {
262 for (auto *trigger : this->response_triggers_) {
263 // with multiple triggers, pass a copy of the response body to each
264 // one so that modifications made in one trigger are not visible to
265 // the others
266 auto response_body_copy = std::string(response_body);
267 trigger->process(container, response_body_copy);
268 }
269 }
270 container->end();
271 }
272
273 protected:
274 void encode_json_(Ts... x, JsonObject root) {
275 for (const auto &item : this->json_) {
276 auto val = item.second;
277 root[item.first] = val.value(x...);
278 }
279 }
280 void encode_json_func_(Ts... x, JsonObject root) { this->json_func_(x..., root); }
282 std::map<const char *, TemplatableValue<const char *, Ts...>> request_headers_{};
283 std::set<std::string> collect_headers_{"content-type", "content-length"};
284 std::map<const char *, TemplatableValue<std::string, Ts...>> json_{};
285 std::function<void(Ts..., JsonObject)> json_func_{nullptr};
286 std::vector<HttpRequestResponseTrigger *> response_triggers_{};
287 std::vector<Trigger<> *> error_triggers_{};
288
290};
291
292} // namespace http_request
293} // 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:538
An STL allocator that uses SPI or internal RAM.
Definition helpers.h:683
void deallocate(T *p, size_t n)
Definition helpers.h:741
T * allocate(size_t n)
Definition helpers.h:703
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_
virtual std::shared_ptr< HttpContainer > perform(std::string url, std::string method, std::string body, std::list< Header > request_headers, std::set< std::string > collect_headers)=0
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)
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(std::shared_ptr< HttpContainer > container, std::string &response_body)
void set_max_response_buffer_size(size_t max_response_buffer_size)
void register_error_trigger(Trigger<> *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 *
std::vector< HttpRequestResponseTrigger * > response_triggers_
void register_response_trigger(HttpRequestResponseTrigger *trigger)
std::function< void(Ts..., JsonObject)> json_func_
void encode_json_func_(Ts... x, JsonObject root)
std::vector< Trigger<> * > error_triggers_
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)
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:12
const float AFTER_WIFI
For components that should be initialized after WiFi is connected.
Definition component.cpp:26
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:290
void IRAM_ATTR HOT yield()
Definition core.cpp:26
Application App
Global storage of Application pointer - only one Application can exist.
uint16_t x
Definition tt21100.cpp:5