proxy: rename + simplify + backpressure

- tcp_proxy/remote_proxy → tcp_proxy_client/tcp_proxy_server
- Все proxy файлы перенесены в src/proxy/
- Конфиг: [tcp_proxy]→[tcp_proxy_client], [remote_proxy]→[tcp_proxy_server]
- Убран subcmd CONNECTED (0x02) — клиент шлёт DATA сразу после CONNECT
- Backpressure: etcp_router_waiter_register/cancel через ll_queue threshold waiter
- Клиент: recv_cb send fail → tx_buf+waiter, не tcp_recved() (окно lwIP=0)
- Сервер: read_cb send fail → pause_buf+waiter, пауза read_id
- CLOSE/ERROR отправляется только после drain всех очередей
- Fail: conn не найден/closed → send ERROR вместо CLOSE/silent drop
- PROTOCOL.md: полная архитектура с диаграммами состояний и потоков
This commit is contained in:
Evgeny committed 2026-06-02 18:02:37 +03:00
1 parent e7a9163011
commit 1f4fe37fe5
41 files changed
+1659 -659

No files matched your search

+158 -5
View File
@@ -73,6 +73,15 @@ static void waiter_cb(struct ll_queue *q, void *arg) {
(*(int*)arg)++; stats.cb_waiter++;
}
static int waiter_order[10];
static int waiter_order_idx;
static void waiter_cb_order(struct ll_queue *q, void *arg) {
(void)q;
waiter_order[waiter_order_idx++] = *(int*)arg;
stats.cb_waiter++;
}
/* ------------------------------------------------------------------ */
static void test_basic(void) {
TEST("basic creation / free");
@@ -165,10 +174,13 @@ static void test_waiter(void) {
TEST("wait_threshold + cancel");
struct UASYNC *ua = uasync_create();
struct ll_queue *q = queue_new(ua, 0, 0, 0, "q6");
queue_set_threshold(q, 2, 0);
int called = 0;
struct queue_waiter *w = queue_wait_threshold(q, 2, 0, waiter_cb, &called);
ASSERT(w == NULL && called == 1, "immediate when condition met"); // empty queue
struct queue_waiter_handle h;
queue_waiter_handle_init(&h, waiter_cb, &called);
int ret = queue_waiter_wait(q, &h);
ASSERT(ret == 1 && called == 1, "immediate when condition met"); // empty queue
for (int i = 0; i < 5; i++) {
test_data_t *d = (test_data_t*)queue_entry_new(sizeof(*d)); d->id = i;
@@ -176,15 +188,151 @@ static void test_waiter(void) {
}
called = 0;
w = queue_wait_threshold(q, 2, 0, waiter_cb, &called);
ASSERT(w != NULL && called == 0, "");
ret = queue_waiter_wait(q, &h);
ASSERT(ret == 0 && called == 0, "");
for (int i = 0; i < 3; i++) queue_entry_free((struct ll_entry*)queue_data_get(q));
for (int i = 0; i < 15; i++) uasync_poll(ua, 1);
ASSERT_EQ(called, 1, "");
queue_cancel_wait(q, w);
queue_waiter_cancel(q, &h);
queue_free(q); uasync_destroy(ua, 0);
PASS();
}
static void test_waiter_multiple(void) {
TEST("waiter multiple FIFO (round-robin)");
struct UASYNC *ua = uasync_create();
struct ll_queue *q = queue_new(ua, 0, 0, 0, "wm");
queue_set_threshold(q, 2, 0);
for (int i = 0; i < 5; i++) {
test_data_t *d = (test_data_t*)queue_entry_new(sizeof(*d)); d->id = i;
queue_data_put(q, (struct ll_entry*)d);
}
waiter_order_idx = 0;
int ids[4] = {10, 20, 30, 40};
struct queue_waiter_handle handles[4];
for (int i = 0; i < 4; i++) {
queue_waiter_handle_init(&handles[i], waiter_cb_order, &ids[i]);
ASSERT(queue_waiter_wait(q, &handles[i]) == 0, "should queue");
}
for (int i = 0; i < 5; i++) queue_entry_free((struct ll_entry*)queue_data_get(q));
ASSERT_EQ(waiter_order_idx, 3, "3 waiters fired");
ASSERT(waiter_order[0] == 10 && waiter_order[1] == 20 && waiter_order[2] == 30, "FIFO order");
for (int i = 0; i < 4; i++) queue_waiter_cancel(q, &handles[i]);
queue_free(q); uasync_destroy(ua, 0);
PASS();
}
static void test_waiter_cancel(void) {
TEST("waiter cancel middle");
struct UASYNC *ua = uasync_create();
struct ll_queue *q = queue_new(ua, 0, 0, 0, "wc");
queue_set_threshold(q, 3, 0);
for (int i = 0; i < 10; i++) {
test_data_t *d = (test_data_t*)queue_entry_new(sizeof(*d)); d->id = i;
queue_data_put(q, (struct ll_entry*)d);
}
waiter_order_idx = 0;
int ids[3] = {1, 2, 3};
struct queue_waiter_handle handles[3];
for (int i = 0; i < 3; i++) {
queue_waiter_handle_init(&handles[i], waiter_cb_order, &ids[i]);
queue_waiter_wait(q, &handles[i]); // count=10 > 3, all queued
}
queue_waiter_cancel(q, &handles[1]); // cancel middle (ID 2)
for (int i = 0; i < 10; i++) queue_entry_free((struct ll_entry*)queue_data_get(q));
ASSERT_EQ(waiter_order_idx, 2, "2 fired (middle cancelled)");
ASSERT(waiter_order[0] == 1 && waiter_order[1] == 3, "skip cancelled");
for (int i = 0; i < 3; i++) queue_waiter_cancel(q, &handles[i]);
queue_free(q); uasync_destroy(ua, 0);
PASS();
}
static void test_waiter_queue_free(void) {
TEST("queue_free clears waiters");
struct UASYNC *ua = uasync_create();
struct ll_queue *q = queue_new(ua, 0, 0, 0, "wf");
for (int i = 0; i < 3; i++) {
test_data_t *d = (test_data_t*)queue_entry_new(sizeof(*d)); d->id = i;
queue_data_put(q, (struct ll_entry*)d);
}
int called = 0;
struct queue_waiter_handle h;
queue_waiter_handle_init(&h, waiter_cb, &called);
queue_waiter_wait(q, &h);
ASSERT(h.internal != NULL, "waiter is queued");
queue_free(q);
ASSERT(h.internal == NULL, "internal cleared by queue_free");
uasync_destroy(ua, 0);
PASS();
}
static void test_waiter_threshold(void) {
TEST("waiter dynamic threshold change");
struct UASYNC *ua = uasync_create();
struct ll_queue *q = queue_new(ua, 0, 0, 0, "wt");
queue_set_threshold(q, 0, 0); // default, but explicit
for (int i = 0; i < 10; i++) {
test_data_t *d = (test_data_t*)queue_entry_new(sizeof(*d)); d->id = i;
queue_data_put(q, (struct ll_entry*)d);
}
int called = 0;
struct queue_waiter_handle h;
queue_waiter_handle_init(&h, waiter_cb, &called);
int ret = queue_waiter_wait(q, &h);
ASSERT(ret == 0 && called == 0, "queued (10 > 0)");
queue_set_threshold(q, 10, 0); // raise threshold
queue_entry_free((struct ll_entry*)queue_data_get(q)); // count=9, check: 9 <= 10? YES
ASSERT_EQ(called, 1, "fired after threshold raised");
while (queue_entry_count(q)) queue_entry_free((struct ll_entry*)queue_data_get(q));
queue_free(q); uasync_destroy(ua, 0);
PASS();
}
static void test_waiter_reuse(void) {
TEST("waiter handle reuse");
struct UASYNC *ua = uasync_create();
struct ll_queue *q = queue_new(ua, 0, 0, 0, "wr");
int called = 0;
struct queue_waiter_handle h;
queue_waiter_handle_init(&h, waiter_cb, &called);
int ret = queue_waiter_wait(q, &h);
ASSERT(ret == 1 && called == 1, "immediate (empty queue)");
for (int i = 0; i < 3; i++) {
test_data_t *d = (test_data_t*)queue_entry_new(sizeof(*d)); d->id = i;
queue_data_put(q, (struct ll_entry*)d);
}
called = 0;
ret = queue_waiter_wait(q, &h);
ASSERT(ret == 0 && called == 0, "queued");
for (int i = 0; i < 3; i++) queue_entry_free((struct ll_entry*)queue_data_get(q));
ASSERT_EQ(called, 1, "fired again on same handle");
queue_waiter_cancel(q, &h);
ASSERT(h.internal == NULL, "internal cleared");
queue_free(q); uasync_destroy(ua, 0);
PASS();
}
@@ -284,6 +432,11 @@ int main(void) {
test_lifo_priority();
test_callback();
test_waiter();
test_waiter_multiple();
test_waiter_cancel();
test_waiter_queue_free();
test_waiter_threshold();
test_waiter_reuse();
test_limits_hash();
test_pool();
test_stress();