85 const std::string &lower_header_name) {
86 for (
const auto &
h : lower_case_collect_headers) {
87 if (
h == lower_header_name)
159static constexpr int HTTP_ERROR_CONNECTION_CLOSED = -1;
190 bool is_read_complete) {
191 if (bytes_read_or_error > 0) {
192 last_data_time =
millis();
195 if (bytes_read_or_error < 0) {
199 if (is_read_complete) {
202 if (
millis() - last_data_time >= timeout_ms) {
209class HttpRequestComponent;
245 virtual int read(uint8_t *buf,
size_t max_len) = 0;
290 size_t read_index = 0;
293 while (read_index < total_size) {
294 int read_bytes_or_error = container->
read(buffer + read_index, std::min(chunk_size, total_size - read_index));
309 read_index += read_bytes_or_error;
316 void process(
const std::shared_ptr<HttpContainer> &container, std::string &response_body) {
317 this->
trigger(container, response_body);
334 std::shared_ptr<HttpContainer>
get(
const std::string &url) {
335 return this->
start(url,
"GET",
"", std::vector<Header>{});
337 std::shared_ptr<HttpContainer>
get(
const std::string &url,
const std::vector<Header> &request_headers) {
338 return this->
start(url,
"GET",
"", request_headers);
340 std::shared_ptr<HttpContainer>
get(
const std::string &url,
const std::vector<Header> &request_headers,
341 const std::vector<std::string> &lower_case_collect_headers) {
342 return this->
start(url,
"GET",
"", request_headers, lower_case_collect_headers);
344 std::shared_ptr<HttpContainer>
post(
const std::string &url,
const std::string &body) {
345 return this->
start(url,
"POST", body, std::vector<Header>{});
347 std::shared_ptr<HttpContainer>
post(
const std::string &url,
const std::string &body,
348 const std::vector<Header> &request_headers) {
349 return this->
start(url,
"POST", body, request_headers);
351 std::shared_ptr<HttpContainer>
post(
const std::string &url,
const std::string &body,
352 const std::vector<Header> &request_headers,
353 const std::vector<std::string> &lower_case_collect_headers) {
354 return this->
start(url,
"POST", body, request_headers, lower_case_collect_headers);
358 ESPDEPRECATED(
"Pass request_headers as std::vector<Header> instead of std::list. Removed in 2027.1.0.",
"2026.7.0")
360 return this->
get(url, std::vector<Header>(request_headers.begin(), request_headers.end()));
363 ESPDEPRECATED(
"Pass request_headers as std::vector<Header> instead of std::list. Removed in 2027.1.0.",
"2026.7.0")
365 const std::vector<std::
string> &collect_headers) {
366 return this->
get(url, std::vector<Header>(request_headers.begin(), request_headers.end()), collect_headers);
369 ESPDEPRECATED(
"Pass request_headers as std::vector<Header> instead of std::list. Removed in 2027.1.0.",
"2026.7.0")
370 std::shared_ptr<
HttpContainer>
post(const std::
string &url, const std::
string &body,
371 const std::list<
Header> &request_headers) {
372 return this->
post(url, body, std::vector<Header>(request_headers.begin(), request_headers.end()));
375 ESPDEPRECATED(
"Pass request_headers as std::vector<Header> instead of std::list. Removed in 2027.1.0.",
"2026.7.0")
376 std::shared_ptr<
HttpContainer>
post(const std::
string &url, const std::
string &body,
377 const std::list<
Header> &request_headers,
378 const std::vector<std::
string> &collect_headers) {
379 return this->
post(url, body, std::vector<Header>(request_headers.begin(), request_headers.end()), collect_headers);
382 std::shared_ptr<HttpContainer>
start(
const std::string &url,
const std::string &method,
const std::string &body,
383 const std::vector<Header> &request_headers) {
385 return this->
perform(url, method, body, request_headers, {});
389 ESPDEPRECATED(
"Pass request_headers as std::vector<Header> instead of std::list. Removed in 2027.1.0.",
"2026.7.0")
390 std::shared_ptr<
HttpContainer>
start(const std::
string &url, const std::
string &method, const std::
string &body,
391 const std::list<
Header> &request_headers) {
392 return this->
start(url, method, body, std::vector<Header>(request_headers.begin(), request_headers.end()));
396 ESPDEPRECATED(
"Pass collect_headers as std::vector<std::string> instead of std::set. Removed in 2027.1.0.",
398 std::shared_ptr<
HttpContainer>
start(const std::
string &url, const std::
string &method, const std::
string &body,
399 const std::vector<
Header> &request_headers,
400 const std::set<std::
string> &collect_headers) {
401 std::vector<std::string> lower;
402 lower.reserve(collect_headers.size());
403 for (
const auto &
h : collect_headers) {
406 return this->
perform(url, method, body, request_headers, lower);
410 ESPDEPRECATED(
"Pass request_headers as std::vector<Header> instead of std::list, and collect_headers as "
411 "std::vector<std::string> instead of std::set. Removed in 2027.1.0.",
413 std::shared_ptr<
HttpContainer>
start(const std::
string &url, const std::
string &method, const std::
string &body,
414 const std::list<
Header> &request_headers,
415 const std::set<std::
string> &collect_headers) {
416 std::vector<std::string> lower;
417 lower.reserve(collect_headers.size());
418 for (
const auto &
h : collect_headers) {
421 return this->
perform(url, method, body, std::vector<Header>(request_headers.begin(), request_headers.end()), lower);
425 ESPDEPRECATED(
"Pass request_headers as std::vector<Header> instead of std::list. Removed in 2027.1.0.",
"2026.7.0")
426 std::shared_ptr<
HttpContainer>
start(const std::
string &url, const std::
string &method, const std::
string &body,
427 const std::list<
Header> &request_headers,
428 const std::vector<std::
string> &lower_case_collect_headers) {
429 return this->
perform(url, method, body, std::vector<Header>(request_headers.begin(), request_headers.end()),
430 lower_case_collect_headers);
433 std::shared_ptr<HttpContainer>
start(
const std::string &url,
const std::string &method,
const std::string &body,
434 const std::vector<Header> &request_headers,
435 const std::vector<std::string> &lower_case_collect_headers) {
436 return this->
perform(url, method, body, request_headers, lower_case_collect_headers);
440 virtual std::shared_ptr<HttpContainer>
perform(
const std::string &url,
const std::string &method,
441 const std::string &body,
const std::vector<Header> &request_headers,
442 const std::vector<std::string> &lower_case_collect_headers) = 0;
456#ifdef USE_HTTP_REQUEST_RESPONSE
472#ifdef USE_HTTP_REQUEST_RESPONSE
485 void play(
const Ts &...
x)
override {
487 if (this->body_.has_value()) {
488 body = this->body_.value(
x...);
490 if (!this->
json_.empty()) {
491 body =
json::build_json([
this,
x...](JsonObject root) { this->encode_json_(x..., root); });
494 body =
json::build_json([
this,
x...](JsonObject root) { this->json_func_(x..., root); });
496 std::vector<Header> request_headers;
499 request_headers.push_back({key,
val.value(
x...)});
502 auto container = this->
parent_->
start(this->url_.value(
x...), this->method_.value(
x...), body, request_headers,
503 this->lower_case_collect_headers_);
505 auto captured_args = std::make_tuple(
x...);
507 if (container ==
nullptr) {
508 std::apply([
this](Ts... captured_args_inner) { this->error_trigger_.trigger(captured_args_inner...); },
514#ifdef USE_HTTP_REQUEST_RESPONSE
515 if (this->capture_response_.value(
x...)) {
516 std::string response_body;
517 RAMAllocator<uint8_t> allocator;
518 uint8_t *buf = allocator.allocate(max_length);
519 if (buf != nullptr) {
522 size_t read_index = 0;
523 uint32_t last_data_time = millis();
524 const uint32_t read_timeout = this->parent_->get_timeout();
525 while (container->get_bytes_read() < max_length) {
526 int read_or_error = container->read(buf + read_index, std::min<size_t>(max_length - read_index, 512));
530 http_read_loop_result(read_or_error, last_data_time, read_timeout, container->is_read_complete());
531 if (result == HttpReadLoopResult::RETRY)
533 if (result != HttpReadLoopResult::DATA)
535 read_index += read_or_error;
537 response_body.reserve(read_index);
538 response_body.assign((char *) buf, read_index);
539 allocator.deallocate(buf, max_length);
542 [
this, &container, &response_body](Ts... captured_args_inner) {
543 this->success_trigger_with_response_.trigger(container, response_body, captured_args_inner...);
549 std::apply([
this, &container](
550 Ts... captured_args_inner) { this->success_trigger_.trigger(container, captured_args_inner...); },
558 for (
const auto &item : this->json_) {
559 auto val = item.second;
560 root[item.first] =
val.value(
x...);
565 std::vector<std::string> lower_case_collect_headers_{
"content-type",
"content-length"};
567 std::function<void(Ts..., JsonObject)> json_func_{
nullptr};
568#ifdef USE_HTTP_REQUEST_RESPONSE
574 size_t max_response_buffer_size_{SIZE_MAX};
Heap-allocating helper functions.
void feed_wdt()
Feed the task watchdog.
Fixed-capacity vector - allocates once at runtime, never reallocates This avoids std::vector template...
Helper class to easily give an object a parent of type T.
Function-pointer-only templatable storage (4 bytes on 32-bit).
Primary TemplatableValue: stores either a constant value or a function pointer.
void trigger(const Ts &...x) ESPHOME_ALWAYS_INLINE
std::string get_response_header(const std::string &header_name)
virtual int read(uint8_t *buf, size_t max_len)=0
Read data from the HTTP response body.
virtual bool is_read_complete() const
Check if all expected content has been read.
std::vector< Header > response_headers_
virtual ~HttpContainer()=default
int status_code
-1 indicates no response received yet
void set_chunked(bool chunked)
size_t get_bytes_read() const
void set_secure(bool secure)
bool is_chunked_
True if response uses chunked transfer encoding.
float get_setup_priority() const override
ESPDEPRECATED("Pass request_headers as std::vector<Header> instead of std::list. Removed in 2027.1.0.", "2026.7.0") std
uint32_t get_watchdog_timeout() const
std::shared_ptr< HttpContainer > post(const std::string &url, const std::string &body)
void set_useragent(const char *useragent)
ESPDEPRECATED("Pass request_headers as std::vector<Header> instead of std::list, and collect_headers as " "std::vector<std::string> instead of std::set. Removed in 2027.1.0.", "2026.7.0") std
std::shared_ptr< HttpContainer > start(const std::string &url, const std::string &method, const std::string &body, const std::vector< Header > &request_headers)
std::shared_ptr< HttpContainer > post(const std::string &url, const std::string &body, const std::vector< Header > &request_headers, const std::vector< std::string > &lower_case_collect_headers)
uint32_t watchdog_timeout_
void set_follow_redirects(bool follow_redirects)
uint32_t get_timeout() const
std::shared_ptr< HttpContainer > get(const std::string &url, const std::vector< Header > &request_headers, const std::vector< std::string > &lower_case_collect_headers)
void set_redirect_limit(uint16_t limit)
void set_watchdog_timeout(uint32_t watchdog_timeout)
ESPDEPRECATED("Pass collect_headers as std::vector<std::string> instead of std::set. Removed in 2027.1.0.", "2026.7.0") std
std::shared_ptr< HttpContainer > post(const std::string &url, const std::string &body, const std::vector< Header > &request_headers)
void set_timeout(uint32_t timeout)
std::shared_ptr< HttpContainer > start(const std::string &url, const std::string &method, const std::string &body, const std::vector< Header > &request_headers, const std::vector< std::string > &lower_case_collect_headers)
std::shared_ptr< HttpContainer > get(const std::string &url)
virtual std::shared_ptr< HttpContainer > perform(const std::string &url, const std::string &method, const std::string &body, const std::vector< Header > &request_headers, const std::vector< std::string > &lower_case_collect_headers)=0
std::shared_ptr< HttpContainer > get(const std::string &url, const std::vector< Header > &request_headers)
void dump_config() override
void process(const std::shared_ptr< HttpContainer > &container, std::string &response_body)
void set_max_response_buffer_size(size_t max_response_buffer_size)
void play(const Ts &...x) override
HttpRequestComponent * parent_
TEMPLATABLE_VALUE(std::string, url) TEMPLATABLE_VALUE(const char *
Trigger< std::shared_ptr< HttpContainer >, std::string &, Ts... > success_trigger_with_response_
void add_collect_header(const char *value)
Trigger< Ts... > error_trigger_
method capture_response void init_request_headers(size_t count)
FixedVector< std::pair< const char *, TemplatableFn< const char *, Ts... > > > request_headers_
Trigger< Ts... > * get_error_trigger()
std::function< void(Ts..., JsonObject)> json_func_
Trigger< std::shared_ptr< HttpContainer >, Ts... > success_trigger_
std::vector< std::string > lower_case_collect_headers_
void set_json(std::function< void(Ts..., JsonObject)> json_func)
void add_json(const char *key, TemplatableValue< std::string, Ts... > value)
Trigger< std::shared_ptr< HttpContainer >, std::string &, Ts... > * get_success_trigger_with_response()
void encode_json_(Ts... x, JsonObject root)
void add_request_header(const char *key, TemplatableFn< const char *, Ts... > value)
size_t max_response_buffer_size_
Trigger< std::shared_ptr< HttpContainer >, Ts... > * get_success_trigger()
HttpRequestSendAction(HttpRequestComponent *parent)
FixedVector< std::pair< const char *, TemplatableValue< std::string, Ts... > > > json_
void init_json(size_t count)
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)
@ HTTP_STATUS_PARTIAL_CONTENT
@ HTTP_STATUS_LENGTH_REQUIRED
@ HTTP_STATUS_PERMANENT_REDIRECT
@ HTTP_STATUS_RESET_CONTENT
@ HTTP_STATUS_METHOD_NOT_ALLOWED
@ HTTP_STATUS_NOT_MODIFIED
@ HTTP_STATUS_INTERNAL_ERROR
@ HTTP_STATUS_UNAUTHORIZED
@ HTTP_STATUS_MULTIPLE_CHOICES
@ HTTP_STATUS_NOT_ACCEPTABLE
@ HTTP_STATUS_TEMPORARY_REDIRECT
@ HTTP_STATUS_BAD_REQUEST
@ HTTP_STATUS_MOVED_PERMANENTLY
HttpReadLoopResult
Result of processing a non-blocking read with timeout (for manual loops)
@ TIMEOUT
Timeout waiting for data, caller should exit loop.
@ COMPLETE
All content has been read, caller should exit loop.
@ ERROR
Read error, caller should exit loop.
@ RETRY
No data yet, already delayed, caller should continue loop.
@ DATA
Data was read, process it.
bool is_success(int const status)
Checks if the given HTTP status code indicates a successful request.
HttpReadLoopResult http_read_loop_result(int bytes_read_or_error, uint32_t &last_data_time, uint32_t timeout_ms, bool is_read_complete)
Process a read result with timeout tracking and delay handling.
bool is_redirect(int const status)
Returns true if the HTTP status code is a redirect.
HttpReadResult http_read_fully(HttpContainer *container, uint8_t *buffer, size_t total_size, size_t chunk_size, uint32_t timeout_ms)
Read data from HTTP container into buffer with timeout handling Handles feed_wdt, yield,...
HttpReadStatus
Status of a read operation.
@ TIMEOUT
Timeout waiting for data.
@ ERROR
Read error occurred.
@ OK
Read completed successfully.
SerializationBuffer build_json(const json_build_t &f)
Build a JSON string with the provided json build function.
constexpr float AFTER_WIFI
For components that should be initialized after WiFi is connected.
std::string str_lower_case(const std::string &str)
Convert the string to lower case.
void HOT delay(uint32_t ms)
uint32_t IRAM_ATTR HOT millis()
Application App
Global storage of Application pointer - only one Application can exist.
for(uint32_t *p=scan_start;p< stack_top &&p< scan_start+64 &&bt_count< MAX_BACKTRACE;p++)
Result of an HTTP read operation.
HttpReadStatus status
Status of the read operation.
int error_code
Error code from read() on failure, 0 on success.