mirror of
https://github.com/rstrouse/ESPSomfy-RTS.git
synced 2026-10-31 13:52:15 +01:00
From the review of 7723711:
- The assembler is locked: a client closed for a full queue is closed
by the task that was sending, loop(), whose disconnect dropped the
buffer while async_tcp could be copying into it. A whole message is
taken out of it, so a disconnect can't free it while it is read.
- loop() expires a message stalled for 10 s, freeing its buffer and
telling its client, instead of holding it until another client came.
A binary message is refused before it can touch another's buffer.
- Each edit takes only the fields its route reads; one it would ignore
is refused by name. linkRemote's rolling code goes in under the
route's name, so applyModelEdit is unchanged again.
- An edit refused for an id that doesn't exist replies not found
(edit_result_t::missing). A reply that doesn't fit its buffer goes
as an internal error, so the op still gets one.
- The TYPE table says what becomes of each op (proto_op_t), which the
socket switches on. The reply address is one proto_reply_to_t. The
routes and the ops start and queue an edit the same way
(newModelEdit, submitModelEdit). The full-list texts and the offered
remote address rule live in ModelEditor.
- e2e: the sort order test skips a list that it would renumber.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
49 lines
1.5 KiB
C++
49 lines
1.5 KiB
C++
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "ProtoAssembly.h"
|
|
|
|
void MessageAssembler::release() {
|
|
free(this->buf);
|
|
this->buf = nullptr;
|
|
this->len = 0;
|
|
this->owner = 0;
|
|
}
|
|
void MessageAssembler::drop(uint32_t clientId) {
|
|
if(this->owner == clientId) this->release();
|
|
}
|
|
uint32_t MessageAssembler::expire(uint32_t now) {
|
|
if(!this->owner || now - this->startedAt < PROTO_ASSEMBLY_TIMEOUT_MS) return 0;
|
|
const uint32_t stalled = this->owner;
|
|
this->release();
|
|
return stalled;
|
|
}
|
|
char *MessageAssembler::take(size_t &len) {
|
|
char *message = this->buf;
|
|
len = this->len;
|
|
this->buf = nullptr;
|
|
this->release();
|
|
return message;
|
|
}
|
|
assembly_t MessageAssembler::add(const proto_piece_t &piece, uint32_t now) {
|
|
if(piece.first) {
|
|
// Refused without taking the buffer from anyone.
|
|
if(!piece.text) return assembly_t::notText;
|
|
if(this->owner && this->owner != piece.clientId) return assembly_t::busy;
|
|
this->release();
|
|
this->buf = static_cast<char *>(malloc(this->limit + 1));
|
|
if(!this->buf) return assembly_t::noMemory;
|
|
this->owner = piece.clientId;
|
|
this->startedAt = now;
|
|
}
|
|
// The rest of a message that was already answered.
|
|
else if(this->owner != piece.clientId) return assembly_t::ignored;
|
|
if(this->len + piece.len > this->limit) {
|
|
this->release();
|
|
return assembly_t::tooLarge;
|
|
}
|
|
memcpy(this->buf + this->len, piece.data, piece.len);
|
|
this->len += piece.len;
|
|
if(!piece.last) return assembly_t::more;
|
|
this->buf[this->len] = '\0';
|
|
return assembly_t::complete;
|
|
}
|