diff --git a/esphome/components/api/api_connection.cpp b/esphome/components/api/api_connection.cpp index 37986672d6..9bafe7cb3e 100644 --- a/esphome/components/api/api_connection.cpp +++ b/esphome/components/api/api_connection.cpp @@ -134,7 +134,7 @@ void APIConnection::start() { // Initialize client name with peername (IP address) until Hello message provides actual name char peername[socket::PEERNAME_MAX_LEN]; this->helper_->getpeername_to(peername); - this->client_info_.name = peername; + strncpy(this->client_info_.name, peername, sizeof(this->client_info_.name) - 1); } APIConnection::~APIConnection() { @@ -1523,13 +1523,16 @@ void APIConnection::complete_authentication_() { } bool APIConnection::send_hello_response(const HelloRequest &msg) { - this->client_info_.name.assign(msg.client_info.c_str(), msg.client_info.size()); + // Copy client name with truncation if needed + size_t copy_len = std::min(msg.client_info.size(), sizeof(this->client_info_.name) - 1); + memcpy(this->client_info_.name, msg.client_info.c_str(), copy_len); + this->client_info_.name[copy_len] = '\0'; this->client_api_version_major_ = msg.api_version_major; this->client_api_version_minor_ = msg.api_version_minor; char peername[socket::PEERNAME_MAX_LEN]; this->helper_->getpeername_to(peername); - ESP_LOGV(TAG, "Hello from client: '%s' | %s | API Version %" PRIu32 ".%" PRIu32, this->client_info_.name.c_str(), - peername, this->client_api_version_major_, this->client_api_version_minor_); + ESP_LOGV(TAG, "Hello from client: '%s' | %s | API Version %" PRIu32 ".%" PRIu32, this->client_info_.name, peername, + this->client_api_version_major_, this->client_api_version_minor_); HelloResponse resp; resp.api_version_major = 1; @@ -2107,14 +2110,14 @@ void APIConnection::process_state_subscriptions_() { void APIConnection::log_client_(int level, const LogString *message) { char peername[socket::PEERNAME_MAX_LEN]; this->helper_->getpeername_to(peername); - esp_log_printf_(level, TAG, __LINE__, ESPHOME_LOG_FORMAT("%s (%s): %s"), this->client_info_.name.c_str(), peername, + esp_log_printf_(level, TAG, __LINE__, ESPHOME_LOG_FORMAT("%s (%s): %s"), this->client_info_.name, peername, LOG_STR_ARG(message)); } void APIConnection::log_warning_(const LogString *message, APIError err) { char peername[socket::PEERNAME_MAX_LEN]; this->helper_->getpeername_to(peername); - ESP_LOGW(TAG, "%s (%s): %s %s errno=%d", this->client_info_.name.c_str(), peername, LOG_STR_ARG(message), + ESP_LOGW(TAG, "%s (%s): %s %s errno=%d", this->client_info_.name, peername, LOG_STR_ARG(message), LOG_STR_ARG(api_error_to_logstr(err)), errno); } diff --git a/esphome/components/api/api_connection.h b/esphome/components/api/api_connection.h index 8c1a91addd..b5700b61aa 100644 --- a/esphome/components/api/api_connection.h +++ b/esphome/components/api/api_connection.h @@ -9,17 +9,21 @@ #include "esphome/core/application.h" #include "esphome/core/component.h" #include "esphome/core/entity_base.h" +#include "esphome/core/string_ref.h" #include #include namespace esphome::api { -// Client information structure +// Client information structure - uses fixed buffer to avoid std::string heap allocation +// Max client name length (e.g., "Home Assistant 2026.1.0.dev0" = 28 chars) +static constexpr size_t CLIENT_INFO_NAME_MAX_LEN = 32; + struct ClientInfo { - std::string name; // Client name from Hello message + char name[CLIENT_INFO_NAME_MAX_LEN]{}; // Client name from Hello message // Note: peername (IP address) is not stored here to save memory. - // Use helper_->getpeername_to() or helper_->getpeername() when needed. + // Use helper_->getpeername_to() when needed. }; // Keepalive timeout in milliseconds @@ -290,7 +294,7 @@ class APIConnection final : public APIServerConnection { bool try_to_clear_buffer(bool log_out_of_space); bool send_buffer(ProtoWriteBuffer buffer, uint8_t message_type) override; - const std::string &get_name() const { return this->client_info_.name; } + StringRef get_name() const { return StringRef(this->client_info_.name); } /// Get peer name (IP address) into a stack buffer - avoids heap allocation size_t get_peername_to(std::span buf) const { return this->helper_->getpeername_to(buf); diff --git a/esphome/components/api/api_frame_helper.cpp b/esphome/components/api/api_frame_helper.cpp index d4801fb63a..1263e23ffb 100644 --- a/esphome/components/api/api_frame_helper.cpp +++ b/esphome/components/api/api_frame_helper.cpp @@ -18,7 +18,7 @@ static const char *const TAG = "api.frame_helper"; do { \ char peername__[socket::PEERNAME_MAX_LEN]; \ this->socket_->getpeername_to(peername__); \ - ESP_LOGVV(TAG, "%s (%s): " msg, this->client_info_->name.c_str(), peername__, ##__VA_ARGS__); \ + ESP_LOGVV(TAG, "%s (%s): " msg, this->client_info_->name, peername__, ##__VA_ARGS__); \ } while (0) #else #define HELPER_LOG(msg, ...) ((void) 0) diff --git a/esphome/components/api/api_frame_helper_noise.cpp b/esphome/components/api/api_frame_helper_noise.cpp index 698b0d2128..8a3c14b432 100644 --- a/esphome/components/api/api_frame_helper_noise.cpp +++ b/esphome/components/api/api_frame_helper_noise.cpp @@ -29,7 +29,7 @@ static constexpr size_t PROLOGUE_INIT_LEN = 12; // strlen("NoiseAPIInit") do { \ char peername__[socket::PEERNAME_MAX_LEN]; \ this->socket_->getpeername_to(peername__); \ - ESP_LOGVV(TAG, "%s (%s): " msg, this->client_info_->name.c_str(), peername__, ##__VA_ARGS__); \ + ESP_LOGVV(TAG, "%s (%s): " msg, this->client_info_->name, peername__, ##__VA_ARGS__); \ } while (0) #else #define HELPER_LOG(msg, ...) ((void) 0) diff --git a/esphome/components/api/api_frame_helper_plaintext.cpp b/esphome/components/api/api_frame_helper_plaintext.cpp index 21fa2f5ef6..ddcc661d40 100644 --- a/esphome/components/api/api_frame_helper_plaintext.cpp +++ b/esphome/components/api/api_frame_helper_plaintext.cpp @@ -23,7 +23,7 @@ static const char *const TAG = "api.plaintext"; do { \ char peername__[socket::PEERNAME_MAX_LEN]; \ this->socket_->getpeername_to(peername__); \ - ESP_LOGVV(TAG, "%s (%s): " msg, this->client_info_->name.c_str(), peername__, ##__VA_ARGS__); \ + ESP_LOGVV(TAG, "%s (%s): " msg, this->client_info_->name, peername__, ##__VA_ARGS__); \ } while (0) #else #define HELPER_LOG(msg, ...) ((void) 0) diff --git a/esphome/components/api/api_server.cpp b/esphome/components/api/api_server.cpp index 979fd3420a..b2915442fb 100644 --- a/esphome/components/api/api_server.cpp +++ b/esphome/components/api/api_server.cpp @@ -193,7 +193,7 @@ void APIServer::loop() { #ifdef USE_API_USER_DEFINED_ACTION_RESPONSES this->unregister_active_action_calls_for_connection(client.get()); #endif - ESP_LOGV(TAG, "Remove connection %s", client->client_info_.name.c_str()); + ESP_LOGV(TAG, "Remove connection %s", client->client_info_.name); // Swap with the last element and pop (avoids expensive vector shifts) if (client_index < this->clients_.size() - 1) {