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