mirror of
https://github.com/esphome/esphome.git
synced 2025-10-07 20:33:47 +01:00
[api] Optimize frame helpers to eliminate double-move overhead
This commit is contained in:
@@ -132,26 +132,16 @@ APIError APINoiseFrameHelper::loop() {
|
|||||||
return APIFrameHelper::loop();
|
return APIFrameHelper::loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter
|
/** Read a packet into the rx_buf_.
|
||||||
*
|
*
|
||||||
* @param frame: The struct to hold the frame information in.
|
* @return APIError::OK if a full packet is in rx_buf_
|
||||||
* msg_start: points to the start of the payload - this pointer is only valid until the next
|
|
||||||
* try_receive_raw_ call
|
|
||||||
*
|
|
||||||
* @return 0 if a full packet is in rx_buf_
|
|
||||||
* @return -1 if error, check errno.
|
|
||||||
*
|
*
|
||||||
* errno EWOULDBLOCK: Packet could not be read without blocking. Try again later.
|
* errno EWOULDBLOCK: Packet could not be read without blocking. Try again later.
|
||||||
* errno ENOMEM: Not enough memory for reading packet.
|
* errno ENOMEM: Not enough memory for reading packet.
|
||||||
* errno API_ERROR_BAD_INDICATOR: Bad indicator byte at start of frame.
|
* errno API_ERROR_BAD_INDICATOR: Bad indicator byte at start of frame.
|
||||||
* errno API_ERROR_HANDSHAKE_PACKET_LEN: Packet too big for this phase.
|
* errno API_ERROR_HANDSHAKE_PACKET_LEN: Packet too big for this phase.
|
||||||
*/
|
*/
|
||||||
APIError APINoiseFrameHelper::try_read_frame_(std::vector<uint8_t> *frame) {
|
APIError APINoiseFrameHelper::try_read_frame_() {
|
||||||
if (frame == nullptr) {
|
|
||||||
HELPER_LOG("Bad argument for try_read_frame_");
|
|
||||||
return APIError::BAD_ARG;
|
|
||||||
}
|
|
||||||
|
|
||||||
// read header
|
// read header
|
||||||
if (rx_header_buf_len_ < 3) {
|
if (rx_header_buf_len_ < 3) {
|
||||||
// no header information yet
|
// no header information yet
|
||||||
@@ -205,12 +195,12 @@ APIError APINoiseFrameHelper::try_read_frame_(std::vector<uint8_t> *frame) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_PACKET_RECEIVED(rx_buf_);
|
LOG_PACKET_RECEIVED(this->rx_buf_);
|
||||||
*frame = std::move(rx_buf_);
|
|
||||||
// consume msg
|
// Clear state for next frame (rx_buf_ still contains data for caller)
|
||||||
rx_buf_ = {};
|
this->rx_buf_len_ = 0;
|
||||||
rx_buf_len_ = 0;
|
this->rx_header_buf_len_ = 0;
|
||||||
rx_header_buf_len_ = 0;
|
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -232,18 +222,18 @@ APIError APINoiseFrameHelper::state_action_() {
|
|||||||
}
|
}
|
||||||
if (state_ == State::CLIENT_HELLO) {
|
if (state_ == State::CLIENT_HELLO) {
|
||||||
// waiting for client hello
|
// waiting for client hello
|
||||||
std::vector<uint8_t> frame;
|
aerr = this->try_read_frame_();
|
||||||
aerr = try_read_frame_(&frame);
|
|
||||||
if (aerr != APIError::OK) {
|
if (aerr != APIError::OK) {
|
||||||
return handle_handshake_frame_error_(aerr);
|
return handle_handshake_frame_error_(aerr);
|
||||||
}
|
}
|
||||||
// ignore contents, may be used in future for flags
|
// ignore contents, may be used in future for flags
|
||||||
// Resize for: existing prologue + 2 size bytes + frame data
|
// Resize for: existing prologue + 2 size bytes + frame data
|
||||||
size_t old_size = prologue_.size();
|
size_t old_size = this->prologue_.size();
|
||||||
prologue_.resize(old_size + 2 + frame.size());
|
this->prologue_.resize(old_size + 2 + this->rx_buf_.size());
|
||||||
prologue_[old_size] = (uint8_t) (frame.size() >> 8);
|
this->prologue_[old_size] = (uint8_t) (this->rx_buf_.size() >> 8);
|
||||||
prologue_[old_size + 1] = (uint8_t) frame.size();
|
this->prologue_[old_size + 1] = (uint8_t) this->rx_buf_.size();
|
||||||
std::memcpy(prologue_.data() + old_size + 2, frame.data(), frame.size());
|
std::memcpy(this->prologue_.data() + old_size + 2, this->rx_buf_.data(), this->rx_buf_.size());
|
||||||
|
this->rx_buf_.clear();
|
||||||
|
|
||||||
state_ = State::SERVER_HELLO;
|
state_ = State::SERVER_HELLO;
|
||||||
}
|
}
|
||||||
@@ -285,24 +275,23 @@ APIError APINoiseFrameHelper::state_action_() {
|
|||||||
int action = noise_handshakestate_get_action(handshake_);
|
int action = noise_handshakestate_get_action(handshake_);
|
||||||
if (action == NOISE_ACTION_READ_MESSAGE) {
|
if (action == NOISE_ACTION_READ_MESSAGE) {
|
||||||
// waiting for handshake msg
|
// waiting for handshake msg
|
||||||
std::vector<uint8_t> frame;
|
aerr = this->try_read_frame_();
|
||||||
aerr = try_read_frame_(&frame);
|
|
||||||
if (aerr != APIError::OK) {
|
if (aerr != APIError::OK) {
|
||||||
return handle_handshake_frame_error_(aerr);
|
return handle_handshake_frame_error_(aerr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frame.empty()) {
|
if (this->rx_buf_.empty()) {
|
||||||
send_explicit_handshake_reject_(LOG_STR("Empty handshake message"));
|
send_explicit_handshake_reject_(LOG_STR("Empty handshake message"));
|
||||||
return APIError::BAD_HANDSHAKE_ERROR_BYTE;
|
return APIError::BAD_HANDSHAKE_ERROR_BYTE;
|
||||||
} else if (frame[0] != 0x00) {
|
} else if (this->rx_buf_[0] != 0x00) {
|
||||||
HELPER_LOG("Bad handshake error byte: %u", frame[0]);
|
HELPER_LOG("Bad handshake error byte: %u", this->rx_buf_[0]);
|
||||||
send_explicit_handshake_reject_(LOG_STR("Bad handshake error byte"));
|
send_explicit_handshake_reject_(LOG_STR("Bad handshake error byte"));
|
||||||
return APIError::BAD_HANDSHAKE_ERROR_BYTE;
|
return APIError::BAD_HANDSHAKE_ERROR_BYTE;
|
||||||
}
|
}
|
||||||
|
|
||||||
NoiseBuffer mbuf;
|
NoiseBuffer mbuf;
|
||||||
noise_buffer_init(mbuf);
|
noise_buffer_init(mbuf);
|
||||||
noise_buffer_set_input(mbuf, frame.data() + 1, frame.size() - 1);
|
noise_buffer_set_input(mbuf, this->rx_buf_.data() + 1, this->rx_buf_.size() - 1);
|
||||||
err = noise_handshakestate_read_message(handshake_, &mbuf, nullptr);
|
err = noise_handshakestate_read_message(handshake_, &mbuf, nullptr);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
// Special handling for MAC failure
|
// Special handling for MAC failure
|
||||||
@@ -311,6 +300,7 @@ APIError APINoiseFrameHelper::state_action_() {
|
|||||||
return handle_noise_error_(err, LOG_STR("noise_handshakestate_read_message"),
|
return handle_noise_error_(err, LOG_STR("noise_handshakestate_read_message"),
|
||||||
APIError::HANDSHAKESTATE_READ_FAILED);
|
APIError::HANDSHAKESTATE_READ_FAILED);
|
||||||
}
|
}
|
||||||
|
this->rx_buf_.clear();
|
||||||
|
|
||||||
aerr = check_handshake_finished_();
|
aerr = check_handshake_finished_();
|
||||||
if (aerr != APIError::OK)
|
if (aerr != APIError::OK)
|
||||||
@@ -379,35 +369,33 @@ void APINoiseFrameHelper::send_explicit_handshake_reject_(const LogString *reaso
|
|||||||
state_ = orig_state;
|
state_ = orig_state;
|
||||||
}
|
}
|
||||||
APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
||||||
int err;
|
APIError aerr = this->state_action_();
|
||||||
APIError aerr;
|
|
||||||
aerr = state_action_();
|
|
||||||
if (aerr != APIError::OK) {
|
if (aerr != APIError::OK) {
|
||||||
return aerr;
|
return aerr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state_ != State::DATA) {
|
if (this->state_ != State::DATA) {
|
||||||
return APIError::WOULD_BLOCK;
|
return APIError::WOULD_BLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> frame;
|
aerr = this->try_read_frame_();
|
||||||
aerr = try_read_frame_(&frame);
|
|
||||||
if (aerr != APIError::OK)
|
if (aerr != APIError::OK)
|
||||||
return aerr;
|
return aerr;
|
||||||
|
|
||||||
NoiseBuffer mbuf;
|
NoiseBuffer mbuf;
|
||||||
noise_buffer_init(mbuf);
|
noise_buffer_init(mbuf);
|
||||||
noise_buffer_set_inout(mbuf, frame.data(), frame.size(), frame.size());
|
noise_buffer_set_inout(mbuf, this->rx_buf_.data(), this->rx_buf_.size(), this->rx_buf_.size());
|
||||||
err = noise_cipherstate_decrypt(recv_cipher_, &mbuf);
|
int err = noise_cipherstate_decrypt(this->recv_cipher_, &mbuf);
|
||||||
APIError decrypt_err =
|
APIError decrypt_err =
|
||||||
handle_noise_error_(err, LOG_STR("noise_cipherstate_decrypt"), APIError::CIPHERSTATE_DECRYPT_FAILED);
|
handle_noise_error_(err, LOG_STR("noise_cipherstate_decrypt"), APIError::CIPHERSTATE_DECRYPT_FAILED);
|
||||||
if (decrypt_err != APIError::OK)
|
if (decrypt_err != APIError::OK) {
|
||||||
return decrypt_err;
|
return decrypt_err;
|
||||||
|
}
|
||||||
|
|
||||||
uint16_t msg_size = mbuf.size;
|
uint16_t msg_size = mbuf.size;
|
||||||
uint8_t *msg_data = frame.data();
|
uint8_t *msg_data = this->rx_buf_.data();
|
||||||
if (msg_size < 4) {
|
if (msg_size < 4) {
|
||||||
state_ = State::FAILED;
|
this->state_ = State::FAILED;
|
||||||
HELPER_LOG("Bad data packet: size %d too short", msg_size);
|
HELPER_LOG("Bad data packet: size %d too short", msg_size);
|
||||||
return APIError::BAD_DATA_PACKET;
|
return APIError::BAD_DATA_PACKET;
|
||||||
}
|
}
|
||||||
@@ -415,15 +403,16 @@ APIError APINoiseFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
|||||||
uint16_t type = (((uint16_t) msg_data[0]) << 8) | msg_data[1];
|
uint16_t type = (((uint16_t) msg_data[0]) << 8) | msg_data[1];
|
||||||
uint16_t data_len = (((uint16_t) msg_data[2]) << 8) | msg_data[3];
|
uint16_t data_len = (((uint16_t) msg_data[2]) << 8) | msg_data[3];
|
||||||
if (data_len > msg_size - 4) {
|
if (data_len > msg_size - 4) {
|
||||||
state_ = State::FAILED;
|
this->state_ = State::FAILED;
|
||||||
HELPER_LOG("Bad data packet: data_len %u greater than msg_size %u", data_len, msg_size);
|
HELPER_LOG("Bad data packet: data_len %u greater than msg_size %u", data_len, msg_size);
|
||||||
return APIError::BAD_DATA_PACKET;
|
return APIError::BAD_DATA_PACKET;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer->container = std::move(frame);
|
buffer->container = std::move(this->rx_buf_);
|
||||||
buffer->data_offset = 4;
|
buffer->data_offset = 4;
|
||||||
buffer->data_len = data_len;
|
buffer->data_len = data_len;
|
||||||
buffer->type = type;
|
buffer->type = type;
|
||||||
|
this->rx_buf_.clear();
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
}
|
}
|
||||||
APIError APINoiseFrameHelper::write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) {
|
APIError APINoiseFrameHelper::write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) {
|
||||||
|
@@ -28,7 +28,7 @@ class APINoiseFrameHelper final : public APIFrameHelper {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
APIError state_action_();
|
APIError state_action_();
|
||||||
APIError try_read_frame_(std::vector<uint8_t> *frame);
|
APIError try_read_frame_();
|
||||||
APIError write_frame_(const uint8_t *data, uint16_t len);
|
APIError write_frame_(const uint8_t *data, uint16_t len);
|
||||||
APIError init_handshake_();
|
APIError init_handshake_();
|
||||||
APIError check_handshake_finished_();
|
APIError check_handshake_finished_();
|
||||||
|
@@ -47,21 +47,13 @@ APIError APIPlaintextFrameHelper::loop() {
|
|||||||
return APIFrameHelper::loop();
|
return APIFrameHelper::loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Read a packet into the rx_buf_. If successful, stores frame data in the frame parameter
|
/** Read a packet into the rx_buf_.
|
||||||
*
|
|
||||||
* @param frame: The struct to hold the frame information in.
|
|
||||||
* msg: store the parsed frame in that struct
|
|
||||||
*
|
*
|
||||||
* @return See APIError
|
* @return See APIError
|
||||||
*
|
*
|
||||||
* error API_ERROR_BAD_INDICATOR: Bad indicator byte at start of frame.
|
* error API_ERROR_BAD_INDICATOR: Bad indicator byte at start of frame.
|
||||||
*/
|
*/
|
||||||
APIError APIPlaintextFrameHelper::try_read_frame_(std::vector<uint8_t> *frame) {
|
APIError APIPlaintextFrameHelper::try_read_frame_() {
|
||||||
if (frame == nullptr) {
|
|
||||||
HELPER_LOG("Bad argument for try_read_frame_");
|
|
||||||
return APIError::BAD_ARG;
|
|
||||||
}
|
|
||||||
|
|
||||||
// read header
|
// read header
|
||||||
while (!rx_header_parsed_) {
|
while (!rx_header_parsed_) {
|
||||||
// Now that we know when the socket is ready, we can read up to 3 bytes
|
// Now that we know when the socket is ready, we can read up to 3 bytes
|
||||||
@@ -170,24 +162,22 @@ APIError APIPlaintextFrameHelper::try_read_frame_(std::vector<uint8_t> *frame) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_PACKET_RECEIVED(rx_buf_);
|
LOG_PACKET_RECEIVED(this->rx_buf_);
|
||||||
*frame = std::move(rx_buf_);
|
|
||||||
// consume msg
|
// Clear state for next frame (rx_buf_ still contains data for caller)
|
||||||
rx_buf_ = {};
|
this->rx_buf_len_ = 0;
|
||||||
rx_buf_len_ = 0;
|
this->rx_header_buf_pos_ = 0;
|
||||||
rx_header_buf_pos_ = 0;
|
this->rx_header_parsed_ = false;
|
||||||
rx_header_parsed_ = false;
|
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
}
|
}
|
||||||
APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
|
||||||
APIError aerr;
|
|
||||||
|
|
||||||
if (state_ != State::DATA) {
|
APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
||||||
|
if (this->state_ != State::DATA) {
|
||||||
return APIError::WOULD_BLOCK;
|
return APIError::WOULD_BLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> frame;
|
APIError aerr = this->try_read_frame_();
|
||||||
aerr = try_read_frame_(&frame);
|
|
||||||
if (aerr != APIError::OK) {
|
if (aerr != APIError::OK) {
|
||||||
if (aerr == APIError::BAD_INDICATOR) {
|
if (aerr == APIError::BAD_INDICATOR) {
|
||||||
// Make sure to tell the remote that we don't
|
// Make sure to tell the remote that we don't
|
||||||
@@ -220,10 +210,11 @@ APIError APIPlaintextFrameHelper::read_packet(ReadPacketBuffer *buffer) {
|
|||||||
return aerr;
|
return aerr;
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer->container = std::move(frame);
|
buffer->container = std::move(this->rx_buf_);
|
||||||
buffer->data_offset = 0;
|
buffer->data_offset = 0;
|
||||||
buffer->data_len = rx_header_parsed_len_;
|
buffer->data_len = this->rx_header_parsed_len_;
|
||||||
buffer->type = rx_header_parsed_type_;
|
buffer->type = this->rx_header_parsed_type_;
|
||||||
|
this->rx_buf_.clear();
|
||||||
return APIError::OK;
|
return APIError::OK;
|
||||||
}
|
}
|
||||||
APIError APIPlaintextFrameHelper::write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) {
|
APIError APIPlaintextFrameHelper::write_protobuf_packet(uint8_t type, ProtoWriteBuffer buffer) {
|
||||||
|
@@ -24,7 +24,7 @@ class APIPlaintextFrameHelper final : public APIFrameHelper {
|
|||||||
APIError write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) override;
|
APIError write_protobuf_packets(ProtoWriteBuffer buffer, std::span<const PacketInfo> packets) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
APIError try_read_frame_(std::vector<uint8_t> *frame);
|
APIError try_read_frame_();
|
||||||
|
|
||||||
// Group 2-byte aligned types
|
// Group 2-byte aligned types
|
||||||
uint16_t rx_header_parsed_type_ = 0;
|
uint16_t rx_header_parsed_type_ = 0;
|
||||||
|
Reference in New Issue
Block a user