ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
http_request_host.cpp
Go to the documentation of this file.
1#ifdef USE_HOST
2
3#define USE_HTTP_REQUEST_HOST_H
4#define CPPHTTPLIB_NO_EXCEPTIONS
5#include "httplib.h"
6#include "http_request_host.h"
7
8#include <cstring>
9
10#include <regex>
13
15#include "esphome/core/log.h"
16
17namespace esphome::http_request {
18
19ESPHOME_LOG_TAG(TAG, "http_request");
20
21std::shared_ptr<HttpContainer> HttpRequestHost::perform(const char *url, const char *method, const std::string &body,
22 const std::vector<Header> &request_headers,
23 const std::vector<std::string> &lower_case_collect_headers,
24 uint32_t) {
25 if (!network::is_connected()) {
26 this->status_momentary_error(1000);
27 ESP_LOGW(TAG, "HTTP Request failed; Not connected to network");
28 return nullptr;
29 }
30
31 std::regex url_regex(R"(^(([^:\/?#]+):)?(//([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?)", std::regex::extended);
32 std::cmatch url_match_result;
33
34 if (!std::regex_match(url, url_match_result, url_regex) || url_match_result.length() < 7) {
35 ESP_LOGE(TAG, "HTTP Request failed; Malformed URL: %s", url);
36 return nullptr;
37 }
38 auto host = url_match_result[4].str();
39 auto scheme_host = url_match_result[1].str() + url_match_result[3].str();
40 auto path = url_match_result[5].str() + url_match_result[6].str();
41 if (path.empty())
42 path = "/";
43
44 std::shared_ptr<HttpContainerHost> container = std::make_shared<HttpContainerHost>();
45 container->set_parent(this);
46
47 const uint32_t start = millis();
48
50
51 httplib::Headers h_headers;
52 h_headers.emplace("Host", host.c_str());
53 h_headers.emplace("User-Agent", this->useragent_);
54 for (const auto &[name, value] : request_headers) {
55 h_headers.emplace(name, value);
56 }
57 httplib::Client client(scheme_host);
58 if (!client.is_valid()) {
59 ESP_LOGE(TAG, "HTTP Request failed; Invalid URL: %s", url);
60 return nullptr;
61 }
62 client.set_follow_location(this->follow_redirects_);
63#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
64 if (this->ca_path_ != nullptr)
65 client.set_ca_cert_path(this->ca_path_);
66#endif
67
68 httplib::Result result;
69 if (strcmp(method, "GET") == 0) {
70 result = client.Get(path, h_headers, [&](const char *data, size_t data_length) {
71 ESP_LOGV(TAG, "Got data length: %zu", data_length);
72 container->response_body_.insert(container->response_body_.end(), (const uint8_t *) data,
73 (const uint8_t *) data + data_length);
74 return true;
75 });
76 } else if (strcmp(method, "HEAD") == 0) {
77 result = client.Head(path, h_headers);
78 } else if (strcmp(method, "PUT") == 0) {
79 result = client.Put(path, h_headers, body, "");
80 if (result) {
81 auto data = std::vector<uint8_t>(result->body.begin(), result->body.end());
82 container->response_body_.insert(container->response_body_.end(), data.begin(), data.end());
83 }
84 } else if (strcmp(method, "PATCH") == 0) {
85 result = client.Patch(path, h_headers, body, "");
86 if (result) {
87 auto data = std::vector<uint8_t>(result->body.begin(), result->body.end());
88 container->response_body_.insert(container->response_body_.end(), data.begin(), data.end());
89 }
90 } else if (strcmp(method, "POST") == 0) {
91 result = client.Post(path, h_headers, body, "");
92 if (result) {
93 auto data = std::vector<uint8_t>(result->body.begin(), result->body.end());
94 container->response_body_.insert(container->response_body_.end(), data.begin(), data.end());
95 }
96 } else {
97 ESP_LOGW(TAG, "HTTP Request failed - unsupported method %s; URL: %s", method, url);
98 container->end();
99 return nullptr;
100 }
101 App.feed_wdt();
102 if (!result) {
103 ESP_LOGW(TAG, "HTTP Request failed; URL: %s, error code: %u", url, (unsigned) result.error());
104 container->end();
105 this->status_momentary_error(1000);
106 return nullptr;
107 }
108 App.feed_wdt();
109 auto response = *result;
110 container->status_code = response.status;
111 if (!is_success(response.status)) {
112 ESP_LOGE(TAG, "HTTP Request failed; URL: %s; Code: %d", url, response.status);
113 this->status_momentary_error(1000);
114 // Still return the container, so it can be used to get the status code and error message
115 }
116
117 container->content_length = container->response_body_.size();
118 for (const auto &header : response.headers) {
119 ESP_LOGD(TAG, "Header: %s: %s", header.first.c_str(), header.second.c_str());
120 auto lower_name = str_lower_case(header.first); // NOLINT
121 if (should_collect_header(lower_case_collect_headers, lower_name)) {
122 container->response_headers_.push_back({lower_name, header.second});
123 }
124 }
125 container->duration_ms = millis() - start;
126 return container;
127}
128
129int HttpContainerHost::read(uint8_t *buf, size_t max_len) {
130 auto bytes_remaining = this->response_body_.size() - this->bytes_read_;
131 auto read_len = std::min(max_len, bytes_remaining);
132 memcpy(buf, this->response_body_.data() + this->bytes_read_, read_len);
133 this->bytes_read_ += read_len;
134 return read_len;
135}
136
138 watchdog::WatchdogManager wdm(this->parent_->get_watchdog_timeout());
139 this->response_body_ = std::vector<uint8_t>();
140 this->bytes_read_ = 0;
141}
142
143} // namespace esphome::http_request
144
145#endif // USE_HOST
void feed_wdt()
Feed the task watchdog.
void status_momentary_error(uint32_t length=5000)
Set error status flag and clear it after length ms.
int read(uint8_t *buf, size_t max_len) override
std::shared_ptr< HttpContainer > start(const char *url, const char *method, const std::string &body, const std::vector< Header > &request_headers)
std::shared_ptr< HttpContainer > perform(const char *url, const char *method, const std::string &body, const std::vector< Header > &request_headers, const std::vector< std::string > &lower_case_collect_headers, uint32_t timeout_ms) override
bool should_collect_header(const std::vector< std::string > &lower_case_collect_headers, const std::string &lower_header_name)
Check if a header name should be collected (linear scan, fine for small lists)
bool is_success(int const status)
Checks if the given HTTP status code indicates a successful request.
ESPHOME_LOG_TAG(TAG, "http_request")
ESPHOME_ALWAYS_INLINE bool is_connected()
Return whether the node is connected to the network (through wifi, eth, ...)
Definition util.h:36
std::string str_lower_case(const std::string &str)
Convert the string to lower case.
uint32_t IRAM_ATTR HOT millis()
Definition hal.cpp:25
Application App
Global storage of Application pointer - only one Application can exist.
static void uint32_t