ESPHome 2025.5.0
Loading...
Searching...
No Matches
http_request_idf.cpp
Go to the documentation of this file.
1#include "http_request_idf.h"
2
3#ifdef USE_ESP_IDF
4
7
9#include "esphome/core/log.h"
10
11#if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
12#include "esp_crt_bundle.h"
13#endif
14
15#include "esp_task_wdt.h"
16
17namespace esphome {
18namespace http_request {
19
20static const char *const TAG = "http_request.idf";
21
22struct UserData {
23 const std::set<std::string> &collect_headers;
24 std::map<std::string, std::list<std::string>> response_headers;
25};
26
29 ESP_LOGCONFIG(TAG, " Buffer Size RX: %u", this->buffer_size_rx_);
30 ESP_LOGCONFIG(TAG, " Buffer Size TX: %u", this->buffer_size_tx_);
31}
32
33esp_err_t HttpRequestIDF::http_event_handler(esp_http_client_event_t *evt) {
34 UserData *user_data = (UserData *) evt->user_data;
35
36 switch (evt->event_id) {
37 case HTTP_EVENT_ON_HEADER: {
38 const std::string header_name = str_lower_case(evt->header_key);
39 if (user_data->collect_headers.count(header_name)) {
40 const std::string header_value = evt->header_value;
41 ESP_LOGD(TAG, "Received response header, name: %s, value: %s", header_name.c_str(), header_value.c_str());
42 user_data->response_headers[header_name].push_back(header_value);
43 break;
44 }
45 break;
46 }
47 default: {
48 break;
49 }
50 }
51 return ESP_OK;
52}
53
54std::shared_ptr<HttpContainer> HttpRequestIDF::perform(std::string url, std::string method, std::string body,
55 std::list<Header> request_headers,
56 std::set<std::string> collect_headers) {
57 if (!network::is_connected()) {
58 this->status_momentary_error("failed", 1000);
59 ESP_LOGE(TAG, "HTTP Request failed; Not connected to network");
60 return nullptr;
61 }
62
63 esp_http_client_method_t method_idf;
64 if (method == "GET") {
65 method_idf = HTTP_METHOD_GET;
66 } else if (method == "POST") {
67 method_idf = HTTP_METHOD_POST;
68 } else if (method == "PUT") {
69 method_idf = HTTP_METHOD_PUT;
70 } else if (method == "DELETE") {
71 method_idf = HTTP_METHOD_DELETE;
72 } else if (method == "PATCH") {
73 method_idf = HTTP_METHOD_PATCH;
74 } else {
75 this->status_momentary_error("failed", 1000);
76 ESP_LOGE(TAG, "HTTP Request failed; Unsupported method");
77 return nullptr;
78 }
79
80 bool secure = url.find("https:") != std::string::npos;
81
82 esp_http_client_config_t config = {};
83
84 config.url = url.c_str();
85 config.method = method_idf;
86 config.timeout_ms = this->timeout_;
87 config.disable_auto_redirect = !this->follow_redirects_;
88 config.max_redirection_count = this->redirect_limit_;
89 config.auth_type = HTTP_AUTH_TYPE_BASIC;
90#if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
91 if (secure) {
92 config.crt_bundle_attach = esp_crt_bundle_attach;
93 }
94#endif
95
96 if (this->useragent_ != nullptr) {
97 config.user_agent = this->useragent_;
98 }
99
100 config.buffer_size = this->buffer_size_rx_;
101 config.buffer_size_tx = this->buffer_size_tx_;
102
103 const uint32_t start = millis();
105
106 config.event_handler = http_event_handler;
107 auto user_data = UserData{collect_headers, {}};
108 config.user_data = static_cast<void *>(&user_data);
109
110 esp_http_client_handle_t client = esp_http_client_init(&config);
111
112 std::shared_ptr<HttpContainerIDF> container = std::make_shared<HttpContainerIDF>(client);
113 container->set_parent(this);
114
115 container->set_secure(secure);
116
117 for (const auto &header : request_headers) {
118 esp_http_client_set_header(client, header.name.c_str(), header.value.c_str());
119 }
120
121 const int body_len = body.length();
122
123 esp_err_t err = esp_http_client_open(client, body_len);
124 if (err != ESP_OK) {
125 this->status_momentary_error("failed", 1000);
126 ESP_LOGE(TAG, "HTTP Request failed: %s", esp_err_to_name(err));
127 esp_http_client_cleanup(client);
128 return nullptr;
129 }
130
131 if (body_len > 0) {
132 int write_left = body_len;
133 int write_index = 0;
134 const char *buf = body.c_str();
135 while (write_left > 0) {
136 int written = esp_http_client_write(client, buf + write_index, write_left);
137 if (written < 0) {
138 err = ESP_FAIL;
139 break;
140 }
141 write_left -= written;
142 write_index += written;
143 }
144 }
145
146 if (err != ESP_OK) {
147 this->status_momentary_error("failed", 1000);
148 ESP_LOGE(TAG, "HTTP Request failed: %s", esp_err_to_name(err));
149 esp_http_client_cleanup(client);
150 return nullptr;
151 }
152
153 container->feed_wdt();
154 container->content_length = esp_http_client_fetch_headers(client);
155 container->feed_wdt();
156 container->status_code = esp_http_client_get_status_code(client);
157 container->feed_wdt();
158 container->set_response_headers(user_data.response_headers);
159 if (is_success(container->status_code)) {
160 container->duration_ms = millis() - start;
161 return container;
162 }
163
164 if (this->follow_redirects_) {
165 auto num_redirects = this->redirect_limit_;
166 while (is_redirect(container->status_code) && num_redirects > 0) {
167 err = esp_http_client_set_redirection(client);
168 if (err != ESP_OK) {
169 ESP_LOGE(TAG, "esp_http_client_set_redirection failed: %s", esp_err_to_name(err));
170 this->status_momentary_error("failed", 1000);
171 esp_http_client_cleanup(client);
172 return nullptr;
173 }
174#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
175 char redirect_url[256]{};
176 if (esp_http_client_get_url(client, redirect_url, sizeof(redirect_url) - 1) == ESP_OK) {
177 ESP_LOGV(TAG, "redirecting to url: %s", redirect_url);
178 }
179#endif
180 err = esp_http_client_open(client, 0);
181 if (err != ESP_OK) {
182 ESP_LOGE(TAG, "esp_http_client_open failed: %s", esp_err_to_name(err));
183 this->status_momentary_error("failed", 1000);
184 esp_http_client_cleanup(client);
185 return nullptr;
186 }
187
188 container->feed_wdt();
189 container->content_length = esp_http_client_fetch_headers(client);
190 container->feed_wdt();
191 container->status_code = esp_http_client_get_status_code(client);
192 container->feed_wdt();
193 if (is_success(container->status_code)) {
194 container->duration_ms = millis() - start;
195 return container;
196 }
197
198 num_redirects--;
199 }
200
201 if (num_redirects == 0) {
202 ESP_LOGW(TAG, "Reach redirect limit count=%d", this->redirect_limit_);
203 }
204 }
205
206 ESP_LOGE(TAG, "HTTP Request failed; URL: %s; Code: %d", url.c_str(), container->status_code);
207 this->status_momentary_error("failed", 1000);
208 return container;
209}
210
211int HttpContainerIDF::read(uint8_t *buf, size_t max_len) {
212 const uint32_t start = millis();
213 watchdog::WatchdogManager wdm(this->parent_->get_watchdog_timeout());
214
215 int bufsize = std::min(max_len, this->content_length - this->bytes_read_);
216
217 if (bufsize == 0) {
218 this->duration_ms += (millis() - start);
219 return 0;
220 }
221
222 this->feed_wdt();
223 int read_len = esp_http_client_read(this->client_, (char *) buf, bufsize);
224 this->feed_wdt();
225 this->bytes_read_ += read_len;
226
227 this->duration_ms += (millis() - start);
228
229 return read_len;
230}
231
233 watchdog::WatchdogManager wdm(this->parent_->get_watchdog_timeout());
234
235 esp_http_client_close(this->client_);
236 esp_http_client_cleanup(this->client_);
237}
238
240 // Tests to see if the executing task has a watchdog timer attached
241 if (esp_task_wdt_status(nullptr) == ESP_OK) {
242 App.feed_wdt();
243 }
244}
245
246} // namespace http_request
247} // namespace esphome
248
249#endif // USE_ESP_IDF
void feed_wdt(uint32_t time=0)
void status_momentary_error(const std::string &name, uint32_t length=5000)
int read(uint8_t *buf, size_t max_len) override
void feed_wdt()
Feeds the watchdog timer if the executing task has one attached.
std::shared_ptr< HttpContainer > start(const std::string &url, const std::string &method, const std::string &body, const std::list< Header > &request_headers)
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) override
static esp_err_t http_event_handler(esp_http_client_event_t *evt)
Monitors the http client events to gather response headers.
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.
bool is_connected()
Return whether the node is connected to the network (through wifi, eth, ...)
Definition util.cpp:15
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
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:27
Application App
Global storage of Application pointer - only one Application can exist.