mirror of
https://github.com/esphome/esphome.git
synced 2025-10-02 10:02:23 +01:00
251 lines
7.5 KiB
C++
251 lines
7.5 KiB
C++
#include "http_request_idf.h"
|
|
|
|
#ifdef USE_ESP_IDF
|
|
|
|
#include "esphome/components/network/util.h"
|
|
#include "esphome/components/watchdog/watchdog.h"
|
|
|
|
#include "esphome/core/application.h"
|
|
#include "esphome/core/log.h"
|
|
|
|
#if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
|
|
#include "esp_crt_bundle.h"
|
|
#endif
|
|
|
|
#include "esp_task_wdt.h"
|
|
|
|
namespace esphome {
|
|
namespace http_request {
|
|
|
|
static const char *const TAG = "http_request.idf";
|
|
|
|
struct UserData {
|
|
const std::set<std::string> &collect_headers;
|
|
std::map<std::string, std::list<std::string>> response_headers;
|
|
};
|
|
|
|
void HttpRequestIDF::dump_config() {
|
|
HttpRequestComponent::dump_config();
|
|
ESP_LOGCONFIG(TAG,
|
|
" Buffer Size RX: %u\n"
|
|
" Buffer Size TX: %u",
|
|
this->buffer_size_rx_, this->buffer_size_tx_);
|
|
}
|
|
|
|
esp_err_t HttpRequestIDF::http_event_handler(esp_http_client_event_t *evt) {
|
|
UserData *user_data = (UserData *) evt->user_data;
|
|
|
|
switch (evt->event_id) {
|
|
case HTTP_EVENT_ON_HEADER: {
|
|
const std::string header_name = str_lower_case(evt->header_key);
|
|
if (user_data->collect_headers.count(header_name)) {
|
|
const std::string header_value = evt->header_value;
|
|
ESP_LOGD(TAG, "Received response header, name: %s, value: %s", header_name.c_str(), header_value.c_str());
|
|
user_data->response_headers[header_name].push_back(header_value);
|
|
}
|
|
break;
|
|
}
|
|
default: {
|
|
break;
|
|
}
|
|
}
|
|
return ESP_OK;
|
|
}
|
|
|
|
std::shared_ptr<HttpContainer> HttpRequestIDF::perform(std::string url, std::string method, std::string body,
|
|
std::list<Header> request_headers,
|
|
std::set<std::string> collect_headers) {
|
|
if (!network::is_connected()) {
|
|
this->status_momentary_error("failed", 1000);
|
|
ESP_LOGE(TAG, "HTTP Request failed; Not connected to network");
|
|
return nullptr;
|
|
}
|
|
|
|
esp_http_client_method_t method_idf;
|
|
if (method == "GET") {
|
|
method_idf = HTTP_METHOD_GET;
|
|
} else if (method == "POST") {
|
|
method_idf = HTTP_METHOD_POST;
|
|
} else if (method == "PUT") {
|
|
method_idf = HTTP_METHOD_PUT;
|
|
} else if (method == "DELETE") {
|
|
method_idf = HTTP_METHOD_DELETE;
|
|
} else if (method == "PATCH") {
|
|
method_idf = HTTP_METHOD_PATCH;
|
|
} else {
|
|
this->status_momentary_error("failed", 1000);
|
|
ESP_LOGE(TAG, "HTTP Request failed; Unsupported method");
|
|
return nullptr;
|
|
}
|
|
|
|
bool secure = url.find("https:") != std::string::npos;
|
|
|
|
esp_http_client_config_t config = {};
|
|
|
|
config.url = url.c_str();
|
|
config.method = method_idf;
|
|
config.timeout_ms = this->timeout_;
|
|
config.disable_auto_redirect = !this->follow_redirects_;
|
|
config.max_redirection_count = this->redirect_limit_;
|
|
config.auth_type = HTTP_AUTH_TYPE_BASIC;
|
|
#if CONFIG_MBEDTLS_CERTIFICATE_BUNDLE
|
|
if (secure) {
|
|
config.crt_bundle_attach = esp_crt_bundle_attach;
|
|
}
|
|
#endif
|
|
|
|
if (this->useragent_ != nullptr) {
|
|
config.user_agent = this->useragent_;
|
|
}
|
|
|
|
config.buffer_size = this->buffer_size_rx_;
|
|
config.buffer_size_tx = this->buffer_size_tx_;
|
|
|
|
const uint32_t start = millis();
|
|
watchdog::WatchdogManager wdm(this->get_watchdog_timeout());
|
|
|
|
config.event_handler = http_event_handler;
|
|
auto user_data = UserData{collect_headers, {}};
|
|
config.user_data = static_cast<void *>(&user_data);
|
|
|
|
esp_http_client_handle_t client = esp_http_client_init(&config);
|
|
|
|
std::shared_ptr<HttpContainerIDF> container = std::make_shared<HttpContainerIDF>(client);
|
|
container->set_parent(this);
|
|
|
|
container->set_secure(secure);
|
|
|
|
for (const auto &header : request_headers) {
|
|
esp_http_client_set_header(client, header.name.c_str(), header.value.c_str());
|
|
}
|
|
|
|
const int body_len = body.length();
|
|
|
|
esp_err_t err = esp_http_client_open(client, body_len);
|
|
if (err != ESP_OK) {
|
|
this->status_momentary_error("failed", 1000);
|
|
ESP_LOGE(TAG, "HTTP Request failed: %s", esp_err_to_name(err));
|
|
esp_http_client_cleanup(client);
|
|
return nullptr;
|
|
}
|
|
|
|
if (body_len > 0) {
|
|
int write_left = body_len;
|
|
int write_index = 0;
|
|
const char *buf = body.c_str();
|
|
while (write_left > 0) {
|
|
int written = esp_http_client_write(client, buf + write_index, write_left);
|
|
if (written < 0) {
|
|
err = ESP_FAIL;
|
|
break;
|
|
}
|
|
write_left -= written;
|
|
write_index += written;
|
|
}
|
|
}
|
|
|
|
if (err != ESP_OK) {
|
|
this->status_momentary_error("failed", 1000);
|
|
ESP_LOGE(TAG, "HTTP Request failed: %s", esp_err_to_name(err));
|
|
esp_http_client_cleanup(client);
|
|
return nullptr;
|
|
}
|
|
|
|
container->feed_wdt();
|
|
container->content_length = esp_http_client_fetch_headers(client);
|
|
container->feed_wdt();
|
|
container->status_code = esp_http_client_get_status_code(client);
|
|
container->feed_wdt();
|
|
container->set_response_headers(user_data.response_headers);
|
|
if (is_success(container->status_code)) {
|
|
container->duration_ms = millis() - start;
|
|
return container;
|
|
}
|
|
|
|
if (this->follow_redirects_) {
|
|
auto num_redirects = this->redirect_limit_;
|
|
while (is_redirect(container->status_code) && num_redirects > 0) {
|
|
err = esp_http_client_set_redirection(client);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGE(TAG, "esp_http_client_set_redirection failed: %s", esp_err_to_name(err));
|
|
this->status_momentary_error("failed", 1000);
|
|
esp_http_client_cleanup(client);
|
|
return nullptr;
|
|
}
|
|
#if ESPHOME_LOG_LEVEL >= ESPHOME_LOG_LEVEL_VERBOSE
|
|
char redirect_url[256]{};
|
|
if (esp_http_client_get_url(client, redirect_url, sizeof(redirect_url) - 1) == ESP_OK) {
|
|
ESP_LOGV(TAG, "redirecting to url: %s", redirect_url);
|
|
}
|
|
#endif
|
|
err = esp_http_client_open(client, 0);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGE(TAG, "esp_http_client_open failed: %s", esp_err_to_name(err));
|
|
this->status_momentary_error("failed", 1000);
|
|
esp_http_client_cleanup(client);
|
|
return nullptr;
|
|
}
|
|
|
|
container->feed_wdt();
|
|
container->content_length = esp_http_client_fetch_headers(client);
|
|
container->feed_wdt();
|
|
container->status_code = esp_http_client_get_status_code(client);
|
|
container->feed_wdt();
|
|
if (is_success(container->status_code)) {
|
|
container->duration_ms = millis() - start;
|
|
return container;
|
|
}
|
|
|
|
num_redirects--;
|
|
}
|
|
|
|
if (num_redirects == 0) {
|
|
ESP_LOGW(TAG, "Reach redirect limit count=%d", this->redirect_limit_);
|
|
}
|
|
}
|
|
|
|
ESP_LOGE(TAG, "HTTP Request failed; URL: %s; Code: %d", url.c_str(), container->status_code);
|
|
this->status_momentary_error("failed", 1000);
|
|
return container;
|
|
}
|
|
|
|
int HttpContainerIDF::read(uint8_t *buf, size_t max_len) {
|
|
const uint32_t start = millis();
|
|
watchdog::WatchdogManager wdm(this->parent_->get_watchdog_timeout());
|
|
|
|
int bufsize = std::min(max_len, this->content_length - this->bytes_read_);
|
|
|
|
if (bufsize == 0) {
|
|
this->duration_ms += (millis() - start);
|
|
return 0;
|
|
}
|
|
|
|
this->feed_wdt();
|
|
int read_len = esp_http_client_read(this->client_, (char *) buf, bufsize);
|
|
this->feed_wdt();
|
|
this->bytes_read_ += read_len;
|
|
|
|
this->duration_ms += (millis() - start);
|
|
|
|
return read_len;
|
|
}
|
|
|
|
void HttpContainerIDF::end() {
|
|
watchdog::WatchdogManager wdm(this->parent_->get_watchdog_timeout());
|
|
|
|
esp_http_client_close(this->client_);
|
|
esp_http_client_cleanup(this->client_);
|
|
}
|
|
|
|
void HttpContainerIDF::feed_wdt() {
|
|
// Tests to see if the executing task has a watchdog timer attached
|
|
if (esp_task_wdt_status(nullptr) == ESP_OK) {
|
|
App.feed_wdt();
|
|
}
|
|
}
|
|
|
|
} // namespace http_request
|
|
} // namespace esphome
|
|
|
|
#endif // USE_ESP_IDF
|