diff --git a/src/transport_layer/etcp.c b/src/transport_layer/etcp.c index 48be3ea3..cb1db23e 100644 --- a/src/transport_layer/etcp.c +++ b/src/transport_layer/etcp.c @@ -250,12 +250,22 @@ struct ETCP_CONN* etcp_connection_create(struct UTUN_INSTANCE* instance, char* n DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] connection initialized. ETCP=%p mtu=%d, next_tx_id=%u state=pending", etcp->log_name, etcp, etcp->mtu, etcp->next_tx_id); - // Вызываем callback для нового соединения если установлен - if (instance) { - struct etcp_inst_cbk_entry* cbe = instance->new_conn_cbks; - while (cbe) { struct etcp_inst_cbk_entry* n = cbe->next; cbe->fn(etcp, cbe->arg); cbe = n; } + /* Конструктор удерживает ресурсы до завершения уведомлений; отмена не возвращает закрытый объект. */ + etcp->callbacks_running++; + struct etcp_inst_cbk_entry* cbe = instance->new_conn_cbks; + while (cbe) { + struct etcp_inst_cbk_entry* next = cbe->next; + cbe->fn(etcp, cbe->arg); + if (etcp->close_requested) break; + cbe = next; + } + if (!etcp->close_requested) etcp_fire_conn_status(etcp, ETCP_CONN_STATUS_NEW); + etcp->callbacks_running--; + if (etcp->close_requested) { + DEBUG_DEBUG(DEBUG_CATEGORY_ETCP, "[%s] connection creation cancelled by callback", etcp->log_name); + etcp_connection_close(etcp); + return NULL; } - etcp_fire_conn_status(etcp, ETCP_CONN_STATUS_NEW); return etcp; } diff --git a/tests/test_etcp_lifecycle.c b/tests/test_etcp_lifecycle.c index f005c59a..7cd3865d 100644 --- a/tests/test_etcp_lifecycle.c +++ b/tests/test_etcp_lifecycle.c @@ -142,6 +142,35 @@ static void ncd_late_up_case(struct UTUN_INSTANCE* inst) { CHECK(!inst->ncd_registry && !inst->connections->count); puts("PASS NCD late UP after timeout and subsequent reconnect"); } +struct create_close_case { unsigned created, published, deleted, status; }; +static void cancel_created_connection(struct ETCP_CONN *conn, void *arg) { + struct create_close_case *test = arg; + test->created++; + etcp_connection_close(conn); + CHECK(conn->close_requested && !conn->delete_complete); +} +static void observe_created_connection(struct ETCP_CONN *conn, int status, void *arg) { + struct create_close_case *test = arg; + if (status == ETCP_CONN_STATUS_DELETE) test->deleted++; + if (status != ETCP_CONN_STATUS_NEW) return; + test->published++; + if (test->status) etcp_connection_close(conn); +} +static void constructor_close_cases(struct UTUN_INSTANCE *inst) { + for (unsigned status = 0; status < 2; status++) { + struct create_close_case test = {.status = status}; + if (!status) etcp_add_new_conn_cbk(inst, cancel_created_connection, &test); + etcp_add_conn_status_cbk(inst, observe_created_connection, &test); + CHECK(etcp_connection_create(inst, "cancel creation") == NULL); + CHECK(test.deleted == 1 && test.created == !status && test.published == status); + CHECK(!inst->connections->count); + if (!status) etcp_remove_new_conn_cbk(inst, cancel_created_connection, &test); + etcp_remove_conn_status_cbk(inst, observe_created_connection, &test); + uasync_drain_immediate(inst->ua); uasync_poll(inst->ua, 0); + printf("PASS connection creation cancelled from %s callback\n", status ? "NEW status" : "new connection"); + } +} + struct link_close_case { struct ETCP_LINK *neighbor; unsigned events, nested; }; static void close_neighbor(struct ETCP_CONN *conn, struct ETCP_LINK *link, int old_state, int old_status, void *arg) { (void)conn; (void)old_state; (void)old_status; @@ -202,7 +231,7 @@ int main(void) { uasync_poll(inst.ua,0); CHECK(inst.connections->count == 0); printf("PASS lifecycle scenario %d\n",scenario+1); } - ncd_cases(&inst); ncd_late_up_case(&inst); recovery_cases(&inst); neighbor_close_case(&inst); + ncd_cases(&inst); ncd_late_up_case(&inst); recovery_cases(&inst); neighbor_close_case(&inst); constructor_close_cases(&inst); queue_free(inst.connections); uasync_poll(inst.ua,0); CHECK(inst.ua->timer_alloc_count == inst.ua->timer_free_count); uasync_destroy(inst.ua,0); CHECK(u_get_allocated_count() == baseline);