ESPHome  2024.12.4
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 
17 namespace esphome {
18 namespace http_request {
19 
20 static const char *const TAG = "http_request.idf";
21 
24  ESP_LOGCONFIG(TAG, " Buffer Size RX: %u", this->buffer_size_rx_);
25  ESP_LOGCONFIG(TAG, " Buffer Size TX: %u", this->buffer_size_tx_);
26 }
27 
28 std::shared_ptr<HttpContainer> HttpRequestIDF::start(std::string url, std::string method, std::string body,
29  std::list<Header> headers) {
30  if (!network::is_connected()) {
31  this->status_momentary_error("failed", 1000);
32  ESP_LOGE(TAG, "HTTP Request failed; Not connected to network");
33  return nullptr;
34  }
35 
36  esp_http_client_method_t method_idf;
37  if (method == "GET") {
38  method_idf = HTTP_METHOD_GET;
39  } else if (method == "POST") {
40  method_idf = HTTP_METHOD_POST;
41  } else if (method == "PUT") {
42  method_idf = HTTP_METHOD_PUT;
43  } else if (method == "DELETE") {
44  method_idf = HTTP_METHOD_DELETE;
45  } else if (method == "PATCH") {
46  method_idf = HTTP_METHOD_PATCH;
47  } else {
48  this->status_momentary_error("failed", 1000);
49  ESP_LOGE(TAG, "HTTP Request failed; Unsupported method");
50  return nullptr;
51  }
52 
53  bool secure = url.find("https:") != std::string::npos;
54 
55  esp_http_client_config_t config = {};
56 
57  config.url = url.c_str();
58  config.method = method_idf;
59  config.timeout_ms = this->timeout_;
60  config.disable_auto_redirect = !this->follow_redirects_;
61  config.max_redirection_count = this->redirect_limit_;
62  config.auth_type = HTTP_AUTH_TYPE_BASIC;
63 #if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
64  if (secure) {
65  config.crt_bundle_attach = esp_crt_bundle_attach;
66  }
67 #endif
68 
69  if (this->useragent_ != nullptr) {
70  config.user_agent = this->useragent_;
71  }
72 
73  config.buffer_size = this->buffer_size_rx_;
74  config.buffer_size_tx = this->buffer_size_tx_;
75 
76  const uint32_t start = millis();
78 
79  esp_http_client_handle_t client = esp_http_client_init(&config);
80 
81  std::shared_ptr<HttpContainerIDF> container = std::make_shared<HttpContainerIDF>(client);
82  container->set_parent(this);
83 
84  container->set_secure(secure);
85 
86  for (const auto &header : headers) {
87  esp_http_client_set_header(client, header.name, header.value);
88  }
89 
90  const int body_len = body.length();
91 
92  esp_err_t err = esp_http_client_open(client, body_len);
93  if (err != ESP_OK) {
94  this->status_momentary_error("failed", 1000);
95  ESP_LOGE(TAG, "HTTP Request failed: %s", esp_err_to_name(err));
96  esp_http_client_cleanup(client);
97  return nullptr;
98  }
99 
100  if (body_len > 0) {
101  int write_left = body_len;
102  int write_index = 0;
103  const char *buf = body.c_str();
104  while (write_left > 0) {
105  int written = esp_http_client_write(client, buf + write_index, write_left);
106  if (written < 0) {
107  err = ESP_FAIL;
108  break;
109  }
110  write_left -= written;
111  write_index += written;
112  }
113  }
114 
115  if (err != ESP_OK) {
116  this->status_momentary_error("failed", 1000);
117  ESP_LOGE(TAG, "HTTP Request failed: %s", esp_err_to_name(err));
118  esp_http_client_cleanup(client);
119  return nullptr;
120  }
121 
122  container->feed_wdt();
123  container->content_length = esp_http_client_fetch_headers(client);
124  container->feed_wdt();
125  container->status_code = esp_http_client_get_status_code(client);
126  container->feed_wdt();
127  if (is_success(container->status_code)) {
128  container->duration_ms = millis() - start;
129  return container;
130  }
131 
132  if (this->follow_redirects_) {
133  auto num_redirects = this->redirect_limit_;
134  while (is_redirect(container->status_code) && num_redirects > 0) {
135  err = esp_http_client_set_redirection(client);
136  if (err != ESP_OK) {
137  ESP_LOGE(TAG, "esp_http_client_set_redirection failed: %s", esp_err_to_name(err));
138  this->status_momentary_error("failed", 1000);
139  esp_http_client_cleanup(client);
140  return nullptr;
141  }
142 #if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
143  char redirect_url[256]{};
144  if (esp_http_client_get_url(client, redirect_url, sizeof(redirect_url) - 1) == ESP_OK) {
145  ESP_LOGV(TAG, "redirecting to url: %s", redirect_url);
146  }
147 #endif
148  err = esp_http_client_open(client, 0);
149  if (err != ESP_OK) {
150  ESP_LOGE(TAG, "esp_http_client_open failed: %s", esp_err_to_name(err));
151  this->status_momentary_error("failed", 1000);
152  esp_http_client_cleanup(client);
153  return nullptr;
154  }
155 
156  container->feed_wdt();
157  container->content_length = esp_http_client_fetch_headers(client);
158  container->feed_wdt();
159  container->status_code = esp_http_client_get_status_code(client);
160  container->feed_wdt();
161  if (is_success(container->status_code)) {
162  container->duration_ms = millis() - start;
163  return container;
164  }
165 
166  num_redirects--;
167  }
168 
169  if (num_redirects == 0) {
170  ESP_LOGW(TAG, "Reach redirect limit count=%d", this->redirect_limit_);
171  }
172  }
173 
174  ESP_LOGE(TAG, "HTTP Request failed; URL: %s; Code: %d", url.c_str(), container->status_code);
175  this->status_momentary_error("failed", 1000);
176  return container;
177 }
178 
179 int HttpContainerIDF::read(uint8_t *buf, size_t max_len) {
180  const uint32_t start = millis();
181  watchdog::WatchdogManager wdm(this->parent_->get_watchdog_timeout());
182 
183  int bufsize = std::min(max_len, this->content_length - this->bytes_read_);
184 
185  if (bufsize == 0) {
186  this->duration_ms += (millis() - start);
187  return 0;
188  }
189 
190  this->feed_wdt();
191  int read_len = esp_http_client_read(this->client_, (char *) buf, bufsize);
192  this->feed_wdt();
193  this->bytes_read_ += read_len;
194 
195  this->duration_ms += (millis() - start);
196 
197  return read_len;
198 }
199 
201  watchdog::WatchdogManager wdm(this->parent_->get_watchdog_timeout());
202 
203  esp_http_client_close(this->client_);
204  esp_http_client_cleanup(this->client_);
205 }
206 
208  // Tests to see if the executing task has a watchdog timer attached
209  if (esp_task_wdt_status(nullptr) == ESP_OK) {
210  App.feed_wdt();
211  }
212 }
213 
214 } // namespace http_request
215 } // namespace esphome
216 
217 #endif // USE_ESP_IDF
bool is_redirect(int const status)
Returns true if the HTTP status code is a redirect.
Definition: http_request.h:59
std::shared_ptr< HttpContainer > start(std::string url, std::string method, std::string body, std::list< Header > headers) override
int read(uint8_t *buf, size_t max_len) override
bool is_connected()
Return whether the node is connected to the network (through wifi, eth, ...)
Definition: util.cpp:15
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
void status_momentary_error(const std::string &name, uint32_t length=5000)
Definition: component.cpp:182
void feed_wdt()
Feeds the watchdog timer if the executing task has one attached.
bool is_success(int const status)
Checks if the given HTTP status code indicates a successful request.
Definition: http_request.h:80
Application App
Global storage of Application pointer - only one Application can exist.
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7