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