#include "hygate.h" #include #include #include #include #include #include #include #include #include #include #include #include #include static config_t g_cfg; static sem_t g_conn_sem; static void *worker(void *arg) { int fd = (int)(intptr_t)arg; handle_connection(fd, &g_cfg); close(fd); sem_post(&g_conn_sem); return NULL; } int main(int argc, char **argv) { config_t cfg; cfg.port = DEFAULT_PORT; cfg.endpoint = DEFAULT_ENDPOINT; cfg.model = DEFAULT_MODEL; cfg.user_agent = ""; cfg.max_body_bytes = (long)MAX_BODY_BYTES; cfg.max_connections = MAX_CONCURRENT_THREADS; /* Simple flag parsing: -key value or -key=value */ for (int i = 1; i < argc; i++) { char *a = argv[i]; char *val = NULL; if (a[0] == '-') { char *eq = strchr(a, '='); if (eq) { *eq = 0; val = eq + 1; } else if (i + 1 < argc) { val = argv[i + 1]; i++; } if (strcmp(a, "-port") == 0) { if (val) cfg.port = val; } else if (strcmp(a, "-endpoint") == 0) { if (val) cfg.endpoint = val; } else if (strcmp(a, "-model") == 0) { if (val) cfg.model = val; } else if (strcmp(a, "-user-agent") == 0 || strcmp(a, "-ua") == 0) { if (val) cfg.user_agent = val; } else if (strcmp(a, "-max-body") == 0) { if (val) { long v = (long)strtoul(val, NULL, 10); if (v > 0) cfg.max_body_bytes = v; } } else if (strcmp(a, "-max-conn") == 0) { if (val) { int v = atoi(val); if (v > 0) cfg.max_connections = v; } } else { fprintf(stderr, "unknown flag: %s\n", a); return 2; } } } g_cfg = cfg; if (sem_init(&g_conn_sem, 0, (unsigned int)cfg.max_connections) != 0) { perror("sem_init"); return 1; } curl_global_init(CURL_GLOBAL_ALL); signal(SIGPIPE, SIG_IGN); int srv = socket(AF_INET, SOCK_STREAM, 0); if (srv < 0) { perror("socket"); return 1; } int one = 1; setsockopt(srv, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one); struct sockaddr_in addr; memset(&addr, 0, sizeof addr); addr.sin_family = AF_INET; addr.sin_port = htons((uint16_t)atoi(cfg.port)); addr.sin_addr.s_addr = INADDR_ANY; if (bind(srv, (struct sockaddr *)&addr, sizeof addr) < 0) { perror("bind"); return 1; } if (listen(srv, 128) < 0) { perror("listen"); return 1; } printf("hygate starting on port %s...\n", cfg.port); printf("Target Endpoint: %s\n", cfg.endpoint); printf("Model Name: %s\n", cfg.model); printf("User-Agent: %s\n", cfg.user_agent && cfg.user_agent[0] ? cfg.user_agent : DEFAULT_UA); printf("Max request body: %ld bytes\n", cfg.max_body_bytes); printf("Max concurrent connections: %d\n", cfg.max_connections); printf("Endpoints:\n"); printf(" GET http://localhost:%s/v1/models\n", cfg.port); printf(" POST http://localhost:%s/v1/chat/completions\n", cfg.port); for (;;) { /* Wait for a free worker slot before accepting, so the number of in-flight connections (and thus worker threads) is capped at cfg.max_connections. Excess connections queue in the kernel listen backlog. */ while (sem_wait(&g_conn_sem) != 0) { if (errno == EINTR) continue; perror("sem_wait"); break; } int cfd = accept(srv, NULL, NULL); if (cfd < 0) { if (errno == EINTR) { sem_post(&g_conn_sem); continue; } perror("accept"); sem_post(&g_conn_sem); continue; } pthread_t tid; if (pthread_create(&tid, NULL, worker, (void *)(intptr_t)cfd) == 0) { pthread_detach(tid); } else { handle_connection(cfd, &g_cfg); close(cfd); sem_post(&g_conn_sem); } } return 0; }