- u_async.c: process immediate_queue BEFORE epoll_wait in uasync_poll - stcp_link.c: defensive NULL etcp_conn/etcp_link in stcp_link_close - utun_instance.c: move stcp_server_list_destroy_all to Phase J (before ETCP), add deferred drain in both Phase J and L - stcp.h/c: STCP_HS_ENC_CLIENT 6→38, STCP_HS_ENC_SERVER 7→39, exchange ed25519_pubkey in both handshake directions - etcp_connections.c: copy peer_ed25519 from STCP to ETCP_CONN in etcp_link_enter_ready_tcp; guard link->etcp in tcp_link_close_cb - chat_sync.c: fix lk->conn NULL dereference for TCP links - test_stcp.c: update stcp_server_create/stcp_client_connect callsites - tools/chat_tcp_test: change transport udp→tcp
136 lines
4.4 KiB
C
136 lines
4.4 KiB
C
// test_tcp_proxy_server.c — Test tcp_conn с реальным TCP соединением (echo сервер)
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <errno.h>
|
||
#ifndef _WIN32
|
||
#include <unistd.h>
|
||
#include <signal.h>
|
||
#include <sys/socket.h>
|
||
#include <netinet/in.h>
|
||
#include <arpa/inet.h>
|
||
#include <sys/wait.h>
|
||
#endif
|
||
#include "../lib/platform_compat.h"
|
||
#include "../lib/tcp_io.h"
|
||
#include "../lib/u_async.h"
|
||
#include "../lib/ll_queue.h"
|
||
#include "../lib/debug_config.h"
|
||
#include "../lib/mem.h"
|
||
|
||
#define ECHO_PORT 19991
|
||
#define TIMEOUT_MS 8000
|
||
#define PAYLOAD_SIZE 64
|
||
|
||
static int g_ok = 0, g_done = 0;
|
||
#ifndef _WIN32
|
||
static pid_t echo_pid = 0;
|
||
#endif
|
||
static uint8_t send_buf[PAYLOAD_SIZE];
|
||
static void* timeout_id = NULL;
|
||
|
||
#ifndef _WIN32
|
||
static void echo_server(void) {
|
||
int srv = socket(AF_INET, SOCK_STREAM, 0);
|
||
if (srv < 0) _exit(1);
|
||
int opt = 1; setsockopt(srv, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
|
||
struct sockaddr_in a = {.sin_family = AF_INET, .sin_port = htons(ECHO_PORT), .sin_addr.s_addr = inet_addr("127.0.0.1")};
|
||
if (bind(srv, (struct sockaddr*)&a, sizeof(a)) < 0 || listen(srv, 1) < 0) { close(srv); _exit(1); }
|
||
int cli = accept(srv, NULL, NULL);
|
||
if (cli < 0) { close(srv); _exit(1); }
|
||
uint8_t buf[8192]; ssize_t n;
|
||
while ((n = recv(cli, buf, sizeof(buf), 0)) > 0) {
|
||
ssize_t s = 0;
|
||
while (s < n) { ssize_t r = send(cli, buf + s, n - s, 0); if (r < 0) goto done; s += r; }
|
||
}
|
||
done: close(cli); close(srv);
|
||
}
|
||
#endif
|
||
|
||
static void on_fin_cb(struct tcp_conn* tc, void* arg) { (void)tc; (void)arg; }
|
||
static void on_error_cb(struct tcp_conn* tc, int err, void* arg) { (void)tc; (void)arg; (void)err; }
|
||
|
||
static int push_write(struct tcp_conn* tc, const uint8_t* data, size_t len) {
|
||
struct ll_entry* e = queue_entry_new_from_pool(tc->entry_pool);
|
||
uint8_t* buf = memory_pool_alloc(tc->data_pool);
|
||
if (!e || !buf) { if (e) queue_entry_free(e); if (buf) memory_pool_free(tc->data_pool, buf); return -1; }
|
||
memcpy(buf, data, len);
|
||
e->dgram = buf; e->len = (uint16_t)len;
|
||
queue_data_put(tc->write_queue, e);
|
||
return 0;
|
||
}
|
||
|
||
static void timeout_cb(void* arg) {
|
||
(void)arg;
|
||
printf("[FAIL] timeout\n");
|
||
g_done = -1;
|
||
}
|
||
|
||
int main(void) {
|
||
debug_config_init();
|
||
debug_set_level(DEBUG_LEVEL_WARN);
|
||
|
||
#ifdef _WIN32
|
||
printf("[SKIP] test_tcp_proxy_server — fork() not available on Windows\n");
|
||
return 0;
|
||
#else
|
||
echo_pid = fork();
|
||
if (echo_pid == 0) { echo_server(); _exit(0); }
|
||
usleep(50000); // даём серверу запуститься
|
||
#endif
|
||
|
||
struct UASYNC* ua = uasync_create();
|
||
if (!ua) return 1;
|
||
|
||
timeout_id = uasync_set_timeout(ua, TIMEOUT_MS * 10, NULL, timeout_cb, "timeout");
|
||
|
||
socket_t sock = socket(AF_INET, SOCK_STREAM, 0);
|
||
if (sock == SOCKET_INVALID) return 1;
|
||
socket_set_nonblocking(sock);
|
||
|
||
struct tcp_conn* tc = tcp_conn_create(ua, sock, 1500, 8192, 32, 8, 0, on_fin_cb, on_error_cb, NULL);
|
||
if (!tc) return 1;
|
||
|
||
struct sockaddr_in addr;
|
||
memset(&addr, 0, sizeof(addr));
|
||
addr.sin_family = AF_INET;
|
||
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
|
||
addr.sin_port = htons(ECHO_PORT);
|
||
int ret = connect(sock, (struct sockaddr*)&addr, sizeof(addr));
|
||
if (ret < 0 && errno != EINPROGRESS) { printf("[FAIL] connect: %s\n", strerror(errno)); g_done = -1; }
|
||
|
||
int phase = 0;
|
||
while (!g_done) {
|
||
uasync_poll(ua, 10);
|
||
|
||
if (phase == 0 && tc->connected) {
|
||
phase = 1;
|
||
for (int i = 0; i < PAYLOAD_SIZE; i++) send_buf[i] = (uint8_t)(rand() & 0xFF);
|
||
push_write(tc, send_buf, PAYLOAD_SIZE);
|
||
}
|
||
|
||
if (phase == 1 && !tc->error) {
|
||
struct ll_entry* e = queue_data_get(tc->read_queue);
|
||
if (e) {
|
||
if (e->len == (uint16_t)PAYLOAD_SIZE && memcmp(send_buf, e->dgram, e->len) == 0) {
|
||
printf("[PASS] test_tcp_proxy_server — %u bytes echoed via tcp_conn\n", e->len); g_ok = 1;
|
||
} else {
|
||
printf("[FAIL] echo mismatch: got %u expected %d\n", e->len, PAYLOAD_SIZE);
|
||
}
|
||
memory_pool_free(tc->data_pool, e->dgram);
|
||
queue_entry_free(e);
|
||
queue_resume_callback(tc->read_queue);
|
||
g_done = 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
tcp_conn_destroy(tc);
|
||
uasync_destroy(ua, 0);
|
||
#ifndef _WIN32
|
||
kill(echo_pid, SIGKILL);
|
||
waitpid(echo_pid, NULL, 0);
|
||
#endif
|
||
return g_ok ? 0 : 1;
|
||
}
|