Files
hygate-c/main.c
T

130 lines
3.8 KiB
C
Raw Normal View History

2026-10-12 07:43:34 +03:00
#include "hygate.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <stdint.h>
#include <errno.h>
#include <pthread.h>
#include <semaphore.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <curl/curl.h>
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) cfg.port = val;
else if (strcmp(a, "-endpoint") == 0) cfg.endpoint = val;
else if (strcmp(a, "-model") == 0) cfg.model = val;
else if (strcmp(a, "-user-agent") == 0) cfg.user_agent = val;
else if (strcmp(a, "-ua") == 0) 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;
}