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