29 const char *(*value)(Ts...);
92 const std::string &lower_header_name) {
93 for (
const auto &
h : lower_case_collect_headers) {
94 if (
h == lower_header_name)
166static constexpr int HTTP_ERROR_CONNECTION_CLOSED = -1;
204 bool is_read_complete) {
205 if (bytes_read_or_error > 0) {
206 last_data_time =
millis();
209 if (bytes_read_or_error < 0) {
213 if (is_read_complete) {
216 if (
millis() - last_data_time >= timeout_ms) {
223class HttpRequestComponent;
259 virtual int read(uint8_t *buf,
size_t max_len) = 0;
319 size_t read_index = 0;
322 while (read_index < total_size) {
323 int read_bytes_or_error = container->
read(
buffer + read_index, std::min(chunk_size, total_size - read_index));
338 read_index += read_bytes_or_error;
345 void process(
const std::shared_ptr<HttpContainer> &container, std::string &response_body) {
346 this->
trigger(container, response_body);
390 void setup()
override;
391 void loop()
override;
415 std::shared_ptr<HttpContainer>
get(
const char *url) {
return this->
start(url,
"GET",
"", std::vector<Header>{}); }
416 std::shared_ptr<HttpContainer>
get(
const char *url,
const std::vector<Header> &request_headers) {
417 return this->
start(url,
"GET",
"", request_headers);
419 std::shared_ptr<HttpContainer>
get(
const char *url,
const std::vector<Header> &request_headers,
420 const std::vector<std::string> &lower_case_collect_headers) {
421 return this->
start(url,
"GET",
"", request_headers, lower_case_collect_headers);
423 std::shared_ptr<HttpContainer>
post(
const char *url,
const std::string &body) {
424 return this->
start(url,
"POST", body, std::vector<Header>{});
426 std::shared_ptr<HttpContainer>
post(
const char *url,
const std::string &body,
427 const std::vector<Header> &request_headers) {
428 return this->
start(url,
"POST", body, request_headers);
430 std::shared_ptr<HttpContainer>
post(
const char *url,
const std::string &body,
431 const std::vector<Header> &request_headers,
432 const std::vector<std::string> &lower_case_collect_headers) {
433 return this->
start(url,
"POST", body, request_headers, lower_case_collect_headers);
436 std::shared_ptr<HttpContainer>
get(
const std::string &url) {
return this->
get(url.c_str()); }
437 std::shared_ptr<HttpContainer>
get(
const std::string &url,
const std::vector<Header> &request_headers) {
438 return this->
get(url.c_str(), request_headers);
440 std::shared_ptr<HttpContainer>
get(
const std::string &url,
const std::vector<Header> &request_headers,
441 const std::vector<std::string> &lower_case_collect_headers) {
442 return this->
get(url.c_str(), request_headers, lower_case_collect_headers);
444 std::shared_ptr<HttpContainer>
post(
const std::string &url,
const std::string &body) {
445 return this->
post(url.c_str(), body);
447 std::shared_ptr<HttpContainer>
post(
const std::string &url,
const std::string &body,
448 const std::vector<Header> &request_headers) {
449 return this->
post(url.c_str(), body, request_headers);
451 std::shared_ptr<HttpContainer>
post(
const std::string &url,
const std::string &body,
452 const std::vector<Header> &request_headers,
453 const std::vector<std::string> &lower_case_collect_headers) {
454 return this->
post(url.c_str(), body, request_headers, lower_case_collect_headers);
458 ESPDEPRECATED(
"Pass request_headers as std::vector<Header> instead of std::list. Removed in 2027.1.0.",
"2026.7.0")
460 return this->
get(url, std::vector<Header>(request_headers.begin(), request_headers.end()));
463 ESPDEPRECATED(
"Pass request_headers as std::vector<Header> instead of std::list. Removed in 2027.1.0.",
"2026.7.0")
465 const
std::vector<
std::
string> &collect_headers) {
466 return this->
get(url, std::vector<Header>(request_headers.begin(), request_headers.end()), collect_headers);
469 ESPDEPRECATED(
"Pass request_headers as std::vector<Header> instead of std::list. Removed in 2027.1.0.",
"2026.7.0")
471 const
std::list<
Header> &request_headers) {
472 return this->
post(url, body, std::vector<Header>(request_headers.begin(), request_headers.end()));
475 ESPDEPRECATED(
"Pass request_headers as std::vector<Header> instead of std::list. Removed in 2027.1.0.",
"2026.7.0")
477 const
std::list<
Header> &request_headers,
478 const
std::vector<
std::
string> &collect_headers) {
479 return this->
post(url, body, std::vector<Header>(request_headers.begin(), request_headers.end()), collect_headers);
482 std::shared_ptr<HttpContainer>
start(
const char *url,
const char *method,
const std::string &body,
483 const std::vector<Header> &request_headers) {
485 return this->
perform(url, method, body, request_headers, {}, this->
timeout_);
487 std::shared_ptr<HttpContainer>
start(
const std::string &url,
const std::string &method,
const std::string &body,
488 const std::vector<Header> &request_headers) {
489 return this->
start(url.c_str(), method.c_str(), body, request_headers);
493 ESPDEPRECATED(
"Pass request_headers as std::vector<Header> instead of std::list. Removed in 2027.1.0.",
"2026.7.0")
495 const
std::list<
Header> &request_headers) {
496 return this->
start(url, method, body, std::vector<Header>(request_headers.begin(), request_headers.end()));
500 ESPDEPRECATED(
"Pass collect_headers as std::vector<std::string> instead of std::set. Removed in 2027.1.0.",
503 const
std::vector<
Header> &request_headers,
504 const
std::set<
std::
string> &collect_headers) {
505 std::vector<std::string> lower;
506 lower.reserve(collect_headers.size());
507 for (
const auto &
h : collect_headers) {
510 return this->
perform(url.c_str(), method.c_str(), body, request_headers, lower, this->timeout_);
514 ESPDEPRECATED(
"Pass request_headers as std::vector<Header> instead of std::list, and collect_headers as "
515 "std::vector<std::string> instead of std::set. Removed in 2027.1.0.",
518 const
std::list<
Header> &request_headers,
519 const
std::set<
std::
string> &collect_headers) {
520 std::vector<std::string> lower;
521 lower.reserve(collect_headers.size());
522 for (
const auto &
h : collect_headers) {
525 return this->
perform(url.c_str(), method.c_str(), body,
526 std::vector<Header>(request_headers.begin(), request_headers.end()), lower, this->timeout_);
530 ESPDEPRECATED(
"Pass request_headers as std::vector<Header> instead of std::list. Removed in 2027.1.0.",
"2026.7.0")
532 const
std::list<
Header> &request_headers,
533 const
std::vector<
std::
string> &lower_case_collect_headers) {
534 return this->
perform(url.c_str(), method.c_str(), body,
535 std::vector<Header>(request_headers.begin(), request_headers.end()),
536 lower_case_collect_headers, this->timeout_);
539 std::shared_ptr<HttpContainer>
start(
const char *url,
const char *method,
const std::string &body,
540 const std::vector<Header> &request_headers,
541 const std::vector<std::string> &lower_case_collect_headers) {
542 return this->
perform(url, method, body, request_headers, lower_case_collect_headers, this->
timeout_);
544 std::shared_ptr<HttpContainer>
start(
const char *url,
const char *method,
const std::string &body,
545 const std::vector<Header> &request_headers,
546 const std::vector<std::string> &lower_case_collect_headers,
548 return this->
perform(url, method, body, request_headers, lower_case_collect_headers, timeout_ms);
550 std::shared_ptr<HttpContainer>
start(
const std::string &url,
const std::string &method,
const std::string &body,
551 const std::vector<Header> &request_headers,
552 const std::vector<std::string> &lower_case_collect_headers) {
553 return this->
start(url.c_str(), method.c_str(), body, request_headers, lower_case_collect_headers);
559 virtual std::shared_ptr<HttpContainer>
begin(
const char *url,
const char *method, std::string &&body,
560 const std::vector<Header> &request_headers,
561 const std::vector<std::string> &lower_case_collect_headers,
563 return this->
perform(url, method, body, request_headers, lower_case_collect_headers, timeout_ms);
567 virtual std::shared_ptr<HttpContainer>
perform(
const char *url,
const char *method,
const std::string &body,
568 const std::vector<Header> &request_headers,
569 const std::vector<std::string> &lower_case_collect_headers,
603#ifdef USE_HTTP_REQUEST_RESPONSE
607 static_assert(std::is_trivially_copyable_v<
RequestHeader<Ts...>> &&
609 "ESP8266 reads request headers from flash in whole words");
622#ifdef USE_HTTP_REQUEST_RESPONSE
640 void play(
const Ts &...
x)
override { }
646 std::tuple<stored_trigger_arg_t<Ts>...>
args;
650 auto *self =
static_cast<Request *
>(request);
654 std::apply([action, self](
auto &...
x) { action->start_request_(*self,
x...); }, self->args);
657 action->finish_(*self);
663 action->stop_complex();
672 if (this->body_.has_value()) {
673 body = this->body_.value(
x...);
675 if (!this->
json_.empty()) {
676 body =
json::build_json([
this,
x...](JsonObject root)
mutable { this->encode_json_(x..., root); });
679 body =
json::build_json([
this,
x...](JsonObject root)
mutable { this->json_func_(x..., root); });
681 std::vector<Header> request_headers;
684 request_headers.push_back({header.name, header.value(
x...)});
687 request.
timeout = this->timeout_.has_value() ? this->timeout_.value(
x...) : this->parent_->get_timeout();
688#ifdef USE_HTTP_REQUEST_RESPONSE
689 request.
capture = this->capture_response_.value(
x...);
691 request.
buf_size = this->max_response_buffer_size_;
692 request.
container = this->parent_->begin(this->url_.value(
x...).c_str(), this->method_.value(
x...), std::move(body),
693 request_headers, this->lower_case_collect_headers_, request.
timeout);
701#ifdef USE_HTTP_REQUEST_RESPONSE
703 std::string response_body;
704 if (request.
buf !=
nullptr)
705 response_body.assign(
reinterpret_cast<char *
>(request.
buf), request.
read_index);
708 [
this, &container, &response_body](
auto &...args) {
715 std::apply([
this, &container](
auto &...args) { this->
success_trigger_.trigger(container, args...); },
719 if (container !=
nullptr) {
725 std::apply([
this](
auto &...args) { this->
play_next_(args...); }, request.
args);
729 for (
const auto &item : this->
json_) {
730 auto val = item.second;
731 root[item.first] =
val.value(
x...);
739#ifdef USE_HTTP_REQUEST_RESPONSE
Heap-allocating helper functions.
void play_next_(const Ts &...x)
void feed_wdt()
Feed the task watchdog.
Lightweight read-only view over a const array stored in RODATA (will typically be in flash memory).
Fixed-capacity vector - sized once through init() or try_init(); push_back never reallocates This avo...
Helper class to easily give an object a parent of type T.
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)
Get the value of a response header by 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.
uint32_t watchdog_timeout_
std::vector< Header > response_headers_
virtual ~HttpContainer()=default
int status_code
-1 indicates no response received yet
virtual HttpRequestProgress poll()
Advance a request started with HttpRequestComponent::begin(); call from the main loop until it is no ...
virtual int read_available(uint8_t *buf, size_t max_len)
Like read(), but returns 0 at once when no data has arrived yet, where read() may wait for it.
uint32_t get_watchdog_timeout() const
void set_chunked(bool chunked)
size_t get_bytes_read() const
void set_watchdog_timeout(uint32_t watchdog_timeout)
void set_secure(bool secure)
bool is_chunked_
True if response uses chunked transfer encoding.
bool advance_(PendingRequest *request)
void cancel(void *owner)
For HttpRequestSendAction: drop the requests of a stopped action without firing their triggers.
void set_max_concurrent_requests(uint8_t max_concurrent)
static constexpr uint32_t READ_BUDGET_MS
Longest time one loop() pass spends reading a response body.
void on_shutdown() override
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)
std::shared_ptr< HttpContainer > get(const char *url)
void finish_(PendingRequest *request)
std::shared_ptr< HttpContainer > start(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)
virtual 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)=0
std::shared_ptr< HttpContainer > post(const char *url, const std::string &body, const std::vector< Header > &request_headers, const std::vector< std::string > &lower_case_collect_headers)
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)
void on_safe_shutdown() override
uint32_t watchdog_timeout_
PendingRequest * requests_
void set_follow_redirects(bool follow_redirects)
uint32_t get_timeout() const
std::shared_ptr< HttpContainer > post(const char *url, const std::string &body, const std::vector< Header > &request_headers)
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 begin_request_(PendingRequest *request)
void set_redirect_limit(uint16_t limit)
uint32_t get_watchdog_timeout(uint32_t timeout_ms) const
void submit(PendingRequest *request)
For HttpRequestSendAction: queue a request and take ownership of it.
void set_watchdog_timeout(uint32_t watchdog_timeout)
std::shared_ptr< HttpContainer > start(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)
std::shared_ptr< HttpContainer > get(const char *url, const std::vector< Header > &request_headers)
std::shared_ptr< HttpContainer > post(const char *url, const std::string &body)
std::shared_ptr< HttpContainer > start(const char *url, const char *method, const std::string &body, const std::vector< Header > &request_headers)
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)
static constexpr uint8_t MAX_QUEUED
Requests that wait for a free slot; more are dropped with on_error.
virtual std::shared_ptr< HttpContainer > begin(const char *url, const char *method, std::string &&body, const std::vector< Header > &request_headers, const std::vector< std::string > &lower_case_collect_headers, uint32_t timeout_ms)
Start a request without waiting for the response, then poll() the container until it is not pending.
std::shared_ptr< HttpContainer > get(const std::string &url, const std::vector< Header > &request_headers)
void dump_config() override
std::shared_ptr< HttpContainer > get(const char *url, const std::vector< Header > &request_headers, const std::vector< std::string > &lower_case_collect_headers)
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 start_request_(Request &request, const Ts &...x)
void add_collect_header(const char *value)
void play_complex(const Ts &...x) override
Trigger< Ts... > error_trigger_
void finish_(Request &request)
Trigger< Ts... > * get_error_trigger()
std::function< void(Ts..., JsonObject)> json_func_
ConstVector< RequestHeader< Ts... > > request_headers_
Trigger< std::shared_ptr< HttpContainer >, Ts... > success_trigger_
method timeout TEMPLATABLE_VALUE(bool, capture_response) static_assert(std void set_request_headers(const RequestHeader< Ts... > *headers, size_t count)
Codegen only: the table must outlive the action.
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)
size_t max_response_buffer_size_
static void handle_event(PendingRequest *request, RequestEvent event)
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
HttpRequestProgress
Progress of a request started with HttpRequestComponent::begin()
@ HTTP_REQUEST_PROGRESS_FAILED
No response; the container must still be ended.
@ HTTP_REQUEST_PROGRESS_DONE
Response headers received; status_code is set and the body can be read.
@ HTTP_REQUEST_PROGRESS_PENDING
Still connecting, sending, or waiting for the response headers.
RequestEvent
What HttpRequestComponent asks of the action that owns a request.
@ REQUEST_EVENT_FINISH
Fire the triggers and continue the action chain.
@ REQUEST_EVENT_START
Build the request and call begin()
@ REQUEST_EVENT_DESTROY
Free the request.
@ REQUEST_EVENT_ABANDON
Dropped at shutdown: stop the action, so that it can run again.
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.
std::tuple< stored_trigger_arg_t< Ts >... > args
Request(HttpRequestSendAction *action, const Ts &...x)
A request from an HttpRequestSendAction, run from HttpRequestComponent::loop().
std::shared_ptr< HttpContainer > container
PendingRequest(Handler handler, void *owner)
PendingRequest & operator=(const PendingRequest &)=delete
void(*)(PendingRequest *request, RequestEvent event) Handler
PendingRequest(const PendingRequest &)=delete