Browse Source

tests: await proxy server readiness and retain worker failures

master
evgeny 2 days ago
parent
commit
aec9536522
  1. 79
      tests/test_socks_http_proxy.c

79
tests/test_socks_http_proxy.c

@ -13,6 +13,7 @@
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <poll.h>
#endif
#include <sys/stat.h>
#include <fcntl.h>
@ -46,6 +47,7 @@ static struct UTUN_INSTANCE* g_cli = NULL;
static struct UTUN_INSTANCE* g_srv = NULL;
static struct UASYNC* g_ua = NULL;
static int g_ok = 0, g_done = 0, g_phase = 0;
static int g_worker_failures;
static pid_t g_http_pid = 0, g_https_pid = 0;
static pid_t g_workers[WORKERS];
static pid_t g_workers_orig[WORKERS];
@ -80,6 +82,29 @@ static void gen_file(const char* path, int size, int seed) {
fclose(f);
}
// Сервер сообщает готовность после listen и настройки TLS; EOF обнаруживает ошибку запуска без паузы.
static int wait_server_ready(int fd, int port, const char* name) {
uint64_t start = get_time_us(), deadline = start + 5000000;
struct pollfd pfd = {.fd = fd, .events = POLLIN};
int result;
do {
uint64_t now = get_time_us();
result = poll(&pfd, 1, now < deadline ? (int)((deadline - now + 999) / 1000) : 0);
} while (result < 0 && errno == EINTR);
char marker = 0;
ssize_t bytes = result > 0 ? read(fd, &marker, 1) : -1;
int error = result < 0 || (result > 0 && bytes < 0) ? errno : 0;
close(fd);
if (result <= 0 || bytes != 1 || marker != 'R') {
DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "test %s server startup failed: port=%d poll=%d events=%x bytes=%ld marker=%d errno=%d",
name, port, result, pfd.revents, (long)bytes, marker, error);
return -1;
}
DEBUG_INFO(DEBUG_CATEGORY_DEBUG, "test %s server ready: port=%d elapsed=%lluus", name, port,
(unsigned long long)(get_time_us() - start));
return 0;
}
static int start_http_server(int port, const char* dir) {
char script[512]; snprintf(script, sizeof(script), "%s/_srv.py", dir);
FILE* sf = fopen(script, "w");
@ -104,20 +129,30 @@ static int start_http_server(int port, const char* dir) {
" self.end_headers()\n"
"port=int(sys.argv[1])\n"
"os.chdir(sys.argv[2])\n"
"HTTPServer(('127.0.0.1',port),H).serve_forever()\n"
"httpd=HTTPServer(('127.0.0.1',port),H)\n"
"ready=int(sys.argv[3]);os.write(ready,b'R');os.close(ready)\n"
"httpd.serve_forever()\n"
);
fclose(sf);
int ready[2];
if (pipe(ready) < 0) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "test HTTP ready pipe: %s", strerror(errno)); return -1; }
pid_t pid = fork();
if (pid < 0) return -1;
if (pid < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "test HTTP server fork: %s", strerror(errno));
close(ready[0]); close(ready[1]); return -1;
}
if (pid == 0) {
char port_str[16]; snprintf(port_str, sizeof(port_str), "%d", port);
execlp("python3", "python3", script, port_str, dir, (char*)NULL);
close(ready[0]);
char port_str[16], ready_str[16];
snprintf(port_str, sizeof(port_str), "%d", port); snprintf(ready_str, sizeof(ready_str), "%d", ready[1]);
execlp("python3", "python3", script, port_str, dir, ready_str, (char*)NULL);
DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "test HTTP python exec: %s", strerror(errno));
_exit(1);
}
close(ready[1]);
g_http_pid = pid;
usleep(300000);
return 0;
return wait_server_ready(ready[0], port, "HTTP");
}
static int start_https_server(int port, const char* dir, const char* cert, const char* key) {
@ -152,20 +187,29 @@ static int start_https_server(int port, const char* dir, const char* cert, const
"httpd=HTTPServer(('127.0.0.1',int(sys.argv[4])),H)\n"
"httpd.socket=ctx.wrap_socket(httpd.socket,server_side=True)\n"
"httpd.timeout=None\n"
"ready=int(sys.argv[5]);os.write(ready,b'R');os.close(ready)\n"
"httpd.serve_forever()\n"
);
fclose(sf);
int ready[2];
if (pipe(ready) < 0) { DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "test HTTPS ready pipe: %s", strerror(errno)); return -1; }
pid_t pid = fork();
if (pid < 0) return -1;
if (pid < 0) {
DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "test HTTPS server fork: %s", strerror(errno));
close(ready[0]); close(ready[1]); return -1;
}
if (pid == 0) {
char port_str[16]; snprintf(port_str, sizeof(port_str), "%d", port);
execlp("python3", "python3", script, cert, key, dir, port_str, (char*)NULL);
close(ready[0]);
char port_str[16], ready_str[16];
snprintf(port_str, sizeof(port_str), "%d", port); snprintf(ready_str, sizeof(ready_str), "%d", ready[1]);
execlp("python3", "python3", script, cert, key, dir, port_str, ready_str, (char*)NULL);
DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "test HTTPS python exec: %s", strerror(errno));
_exit(1);
}
close(ready[1]);
g_https_pid = pid;
usleep(300000);
return 0;
return wait_server_ready(ready[0], port, "HTTPS");
}
static int gen_self_signed_cert(const char* tmpdir, char* cert_out, size_t cert_sz, char* key_out, size_t key_sz) {
@ -329,6 +373,7 @@ static int worker_main(int wtype, const char* socks_proxy, const char* http_prox
static void monitor(void* arg) {
(void)arg;
g_mon_id = NULL;
if (g_done) return;
if (g_phase == 0) {
@ -373,16 +418,21 @@ static void monitor(void* arg) {
}
if (g_phase == 1) {
int done_count = 0, fail = 0;
int done_count = 0;
for (int i = 0; i < WORKERS; i++) {
if (g_workers[i] <= 0) { done_count++; continue; }
int status; pid_t r = waitpid(g_workers[i], &status, WNOHANG);
if (r == 0) continue;
if (r < 0 && errno == EINTR) continue;
if (r < 0 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
DEBUG_ERROR(DEBUG_CATEGORY_PROXY, "test worker=%d pid=%d failed: waitpid=%d status=%d errno=%d",
i, (int)g_workers[i], (int)r, r > 0 ? status : -1, r < 0 ? errno : 0);
g_worker_failures++;
}
g_workers[i] = 0; done_count++;
if (WIFEXITED(status) && WEXITSTATUS(status) != 0) fail++;
}
if (done_count >= WORKERS) {
if (fail) { printf("[FAIL] %d/%d workers failed\n", fail, WORKERS); g_done = -1; }
if (g_worker_failures) { printf("[FAIL] %d/%d workers failed\n", g_worker_failures, WORKERS); g_done = -1; }
else { g_ok = 1; g_done = 1; }
printf("\n=== PROFILING ===\n");
@ -433,6 +483,7 @@ int main(void) {
{ int fd = open("/dev/null", O_RDONLY); if (fd > 0 && fd != 0) { dup2(fd, 0); close(fd); } else if (fd < 0) { close(0); open("/dev/null", O_RDONLY); } }
debug_config_init(); debug_set_level(DEBUG_LEVEL_ERROR); debug_set_categories(DEBUG_CATEGORY_ALL);
debug_set_category_level(DEBUG_CATEGORY_DEBUG, DEBUG_LEVEL_INFO);
utun_instance_set_tun_init_enabled(0);
srand((unsigned)time(NULL));

Loading…
Cancel
Save