|
|
|
|
@ -230,6 +230,60 @@ static void check_wake_deadline(void) {
|
|
|
|
|
teardown(); DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "PASS wake replaces long sleep deadline with normal timeout"); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void peer_phase_changed(struct UTUN_INSTANCE* owner, uint64_t peer, int sleeping, void* arg) { |
|
|
|
|
unsigned* events = arg; |
|
|
|
|
CHECK(owner == &inst && peer == 42); |
|
|
|
|
events[sleeping ? 1 : 0]++; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void receive_keepalive(struct ETCP_LINK* link, uint8_t flags, uint16_t active) { |
|
|
|
|
struct ETCP_DGRAM* pkt = memory_pool_alloc(inst.pkt_pool); CHECK(pkt); |
|
|
|
|
memset(pkt, 0, sizeof(*pkt) + 8); pkt->link = link; |
|
|
|
|
pkt->data_len = flags & KA_FLAG_SLEEP ? 8 : 4; |
|
|
|
|
pkt->data[0] = ETCP_KEEPALIVE; pkt->data[1] = flags; |
|
|
|
|
pkt->data[2] = 200; pkt->data[4] = active & 0xff; pkt->data[5] = active >> 8; |
|
|
|
|
pkt->data[6] = 200; // 20 секунд сна
|
|
|
|
|
link->last_recv_local_time = get_time_tb(); |
|
|
|
|
CHECK(etcp_keepalive_on_recv(pkt) == 1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Новый линк снимает сон всего соединения, включая ещё не наступившую SLEEP-фазу. */ |
|
|
|
|
static void check_peer_wake_new_link(int tcp, int active_window) { |
|
|
|
|
setup(); struct ETCP_CONN* conn = ready_conn(!tcp); struct ETCP_LINK* old = conn->links; |
|
|
|
|
unsigned events[2] = {0}; utun_add_peer_sleep_cbk(&inst, peer_phase_changed, events); |
|
|
|
|
receive_keepalive(old, KA_FLAG_SLEEP, active_window ? 5 : 0); |
|
|
|
|
CHECK(conn->peer_phase_timer && conn->peer_sleep_phase == !active_window); |
|
|
|
|
CHECK(events[1] == (unsigned)!active_window && !events[0]); |
|
|
|
|
/* REINIT потока сам по себе не означает пробуждение приложения. */ |
|
|
|
|
etcp_conn_reinit_id(conn, "peer restart before wake", conn->reset_id); |
|
|
|
|
CHECK(conn->peer_phase_timer && conn->peer_sleep_phase == !active_window); |
|
|
|
|
struct ETCP_LINK* fresh = add_link(conn, tcp); start_keepalive_timer(fresh); |
|
|
|
|
CHECK(!fresh->ka_peer_sleeping && fresh->keepalive_timer); |
|
|
|
|
receive_keepalive(fresh, KA_FLAG_SLEEP_ACK, 0); |
|
|
|
|
CHECK(conn->peer_phase_timer && conn->peer_sleep_phase == !active_window); |
|
|
|
|
void* timer = fresh->keepalive_timer; |
|
|
|
|
receive_keepalive(fresh, 0, 0); |
|
|
|
|
CHECK(!conn->peer_sleep_phase && !conn->peer_phase_timer && fresh->keepalive_timer == timer); |
|
|
|
|
CHECK(events[0] == (unsigned)!active_window && old->ka_peer_sleeping); |
|
|
|
|
uint64_t allocations = inst.ua->timer_alloc_count; |
|
|
|
|
receive_keepalive(fresh, 0, 0); |
|
|
|
|
CHECK(inst.ua->timer_alloc_count == allocations && events[0] == (unsigned)!active_window); |
|
|
|
|
/* Частый RX не отодвигает TX; отменённый фазовый таймер не усыпляет снова. */ |
|
|
|
|
unsigned packets = fresh->keepalive_sent_count; |
|
|
|
|
for (unsigned i = 0; i < 6; i++) { |
|
|
|
|
test_clock_run_for(inst.ua, 1000); receive_keepalive(fresh, 0, 0); |
|
|
|
|
CHECK(!conn->peer_sleep_phase && !conn->peer_phase_timer); |
|
|
|
|
} |
|
|
|
|
CHECK(fresh->keepalive_sent_count > packets && events[0] == (unsigned)!active_window); |
|
|
|
|
/* Пробуждение пира не выключает собственный standby на прежнем линке. */ |
|
|
|
|
old->ka_my_sleeping = 1; receive_keepalive(old, 0, 0); |
|
|
|
|
CHECK(old->ka_my_sleeping && !old->ka_peer_sleeping && !old->keepalive_timer); |
|
|
|
|
CHECK(!conn->peer_phase_timer && events[0] == (unsigned)!active_window); |
|
|
|
|
utun_remove_peer_sleep_cbk(&inst, peer_phase_changed, events); |
|
|
|
|
teardown(); |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "PASS peer wake via new link tcp=%d active_window=%d: phase cancelled, TX preserved", tcp, active_window); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Мягкое закрытие работает без NCD entry; KEEP_ALIVE сохраняет транспорт дольше прежних 5 секунд. */ |
|
|
|
|
static void check_graceful(void) { |
|
|
|
|
setup(); struct ETCP_CONN* conn = ready_conn(0); |
|
|
|
|
@ -251,6 +305,7 @@ int main(void) {
|
|
|
|
|
debug_config_init(); debug_set_level(DEBUG_LEVEL_WARN); test_clock_init(10000); |
|
|
|
|
debug_set_category_level(DEBUG_CATEGORY_CONNECTION, DEBUG_LEVEL_DEBUG); |
|
|
|
|
debug_set_category_level(DEBUG_CATEGORY_KEEPALIVE, DEBUG_LEVEL_DEBUG); |
|
|
|
|
debug_set_category_level(DEBUG_CATEGORY_DEBUG, DEBUG_LEVEL_INFO); |
|
|
|
|
size_t baseline = u_get_allocated_count(); |
|
|
|
|
check_pending(); CHECK(u_get_allocated_count() == baseline); |
|
|
|
|
check_reconnect(); CHECK(u_get_allocated_count() == baseline); |
|
|
|
|
@ -262,6 +317,9 @@ int main(void) {
|
|
|
|
|
check_sockless_tcp_keepalive(); CHECK(u_get_allocated_count() == baseline); |
|
|
|
|
check_sleep(); CHECK(u_get_allocated_count() == baseline); |
|
|
|
|
check_wake_deadline(); CHECK(u_get_allocated_count() == baseline); |
|
|
|
|
for (int tcp = 0; tcp <= 1; tcp++) for (int active = 0; active <= 1; active++) { |
|
|
|
|
check_peer_wake_new_link(tcp, active); CHECK(u_get_allocated_count() == baseline); |
|
|
|
|
} |
|
|
|
|
check_remote_down(); CHECK(u_get_allocated_count() == baseline); |
|
|
|
|
check_graceful(); CHECK(u_get_allocated_count() == baseline); |
|
|
|
|
test_clock_finish(); return 0; |
|
|
|
|
|