Update.
Some checks failed
Build and Test / build (push) Failing after 19s

This commit is contained in:
retoor 2025-11-29 13:18:07 +01:00
parent 14ebbbec3f
commit 378f64fc75
5 changed files with 136 additions and 64 deletions

View File

@ -29,7 +29,8 @@ static void compute_password_hash(const char *password, char *output, size_t out
}
}
app_config_t config;
app_config_t *config = NULL;
static app_config_t *stale_configs_head = NULL;
static int is_valid_hostname(const char *hostname) {
if (!hostname || strlen(hostname) == 0 || strlen(hostname) > 253) return 0;
@ -118,26 +119,37 @@ int config_load(const char *filename) {
return 0;
}
cJSON *port_item = cJSON_GetObjectItem(root, "port");
config.port = cJSON_IsNumber(port_item) ? port_item->valueint : 8080;
app_config_t *new_config = calloc(1, sizeof(app_config_t));
if (!new_config) {
log_error("Failed to allocate memory for new config");
cJSON_Delete(root);
return 0;
}
new_config->ref_count = 1; // Start with one reference for the global 'config' pointer
if (config.port < 1 || config.port > 65535) {
fprintf(stderr, "Invalid port number: %d\n", config.port);
cJSON *port_item = cJSON_GetObjectItem(root, "port");
new_config->port = cJSON_IsNumber(port_item) ? port_item->valueint : 8080;
if (new_config->port < 1 || new_config->port > 65535) {
fprintf(stderr, "Invalid port number: %d\n", new_config->port);
free(new_config);
cJSON_Delete(root);
return 0;
}
cJSON *proxy_array = cJSON_GetObjectItem(root, "reverse_proxy");
if (cJSON_IsArray(proxy_array)) {
config.route_count = cJSON_GetArraySize(proxy_array);
if (config.route_count <= 0) {
new_config->route_count = cJSON_GetArraySize(proxy_array);
if (new_config->route_count <= 0) {
free(new_config);
cJSON_Delete(root);
return 0;
}
config.routes = calloc(config.route_count, sizeof(route_config_t));
if (!config.routes) {
new_config->routes = calloc(new_config->route_count, sizeof(route_config_t));
if (!new_config->routes) {
log_error("Failed to allocate memory for routes");
free(new_config);
cJSON_Delete(root);
return 0;
}
@ -145,7 +157,7 @@ int config_load(const char *filename) {
int i = 0;
cJSON *route_item;
cJSON_ArrayForEach(route_item, proxy_array) {
route_config_t *route = &config.routes[i];
route_config_t *route = &new_config->routes[i];
cJSON *hostname = cJSON_GetObjectItem(route_item, "hostname");
cJSON *upstream_host = cJSON_GetObjectItem(route_item, "upstream_host");
@ -232,7 +244,7 @@ int config_load(const char *filename) {
route->patches.rule_count++;
}
if (route->patches.rule_count > 0) {
log_info("Loaded %d patch rules for %s", route->patches.rule_count, route->hostname);
log_info("Loaded %d patch rules for %s", route->hostname);
}
}
@ -244,16 +256,47 @@ int config_load(const char *filename) {
}
}
cJSON_Delete(root);
log_info("Loaded %d routes from %s", config.route_count, filename);
if (config) {
config_ref_dec(config);
}
config = new_config;
log_info("Loaded %d routes from %s", config->route_count, filename);
return 1;
}
void config_free(void) {
if (config.routes) {
free(config.routes);
config.routes = NULL;
void config_ref_inc(app_config_t *conf) {
if (conf) {
__sync_fetch_and_add(&conf->ref_count, 1);
}
config.route_count = 0;
}
void config_ref_dec(app_config_t *conf) {
if (!conf) return;
if (__sync_sub_and_fetch(&conf->ref_count, 1) == 0) {
log_debug("Freeing configuration with port %d", conf->port);
if (conf->routes) {
free(conf->routes);
}
free(conf);
}
}
void config_free(void) {
if (config) {
config_ref_dec(config);
config = NULL;
}
app_config_t *current = stale_configs_head;
while (current) {
app_config_t *next = current->next;
config_ref_dec(current);
current = next;
}
stale_configs_head = NULL;
}
void config_create_default(const char *filename) {
@ -294,11 +337,18 @@ void config_create_default(const char *filename) {
route_config_t *config_find_route(const char *hostname) {
if (!hostname) return NULL;
pthread_rwlock_rdlock(&config_lock);
app_config_t *current_config = config;
if (!current_config) {
pthread_rwlock_unlock(&config_lock);
return NULL;
}
route_config_t *result = NULL;
for (int i = 0; i < config.route_count; i++) {
if (strcasecmp(hostname, config.routes[i].hostname) == 0) {
result = &config.routes[i];
for (int i = 0; i < current_config->route_count; i++) {
if (strcasecmp(hostname, current_config->routes[i].hostname) == 0) {
result = &current_config->routes[i];
break;
}
}
@ -325,12 +375,17 @@ int config_check_file_changed(const char *filename) {
int config_hot_reload(const char *filename) {
log_info("Hot-reloading configuration from %s", filename);
app_config_t new_config;
memset(&new_config, 0, sizeof(app_config_t));
app_config_t *new_config = calloc(1, sizeof(app_config_t));
if (!new_config) {
log_error("Hot-reload: Failed to allocate memory for new config");
return 0;
}
new_config->ref_count = 1;
char *json_string = read_file_to_string(filename);
if (!json_string) {
log_error("Hot-reload: Could not read config file");
free(new_config);
return 0;
}
@ -338,37 +393,41 @@ int config_hot_reload(const char *filename) {
free(json_string);
if (!root) {
log_error("Hot-reload: JSON parse error: %s", cJSON_GetErrorPtr());
free(new_config);
return 0;
}
cJSON *port_item = cJSON_GetObjectItem(root, "port");
new_config.port = cJSON_IsNumber(port_item) ? port_item->valueint : 8080;
new_config->port = cJSON_IsNumber(port_item) ? port_item->valueint : 8080;
if (new_config.port < 1 || new_config.port > 65535) {
log_error("Hot-reload: Invalid port number: %d", new_config.port);
if (new_config->port < 1 || new_config->port > 65535) {
log_error("Hot-reload: Invalid port number: %d", new_config->port);
cJSON_Delete(root);
free(new_config);
return 0;
}
cJSON *proxy_array = cJSON_GetObjectItem(root, "reverse_proxy");
if (cJSON_IsArray(proxy_array)) {
new_config.route_count = cJSON_GetArraySize(proxy_array);
if (new_config.route_count <= 0) {
new_config->route_count = cJSON_GetArraySize(proxy_array);
if (new_config->route_count <= 0) {
cJSON_Delete(root);
free(new_config);
return 0;
}
new_config.routes = calloc(new_config.route_count, sizeof(route_config_t));
if (!new_config.routes) {
new_config->routes = calloc(new_config->route_count, sizeof(route_config_t));
if (!new_config->routes) {
log_error("Hot-reload: Failed to allocate memory for routes");
cJSON_Delete(root);
free(new_config);
return 0;
}
int i = 0;
cJSON *route_item;
cJSON_ArrayForEach(route_item, proxy_array) {
route_config_t *route = &new_config.routes[i];
route_config_t *route = &new_config->routes[i];
cJSON *hostname = cJSON_GetObjectItem(route_item, "hostname");
cJSON *upstream_host = cJSON_GetObjectItem(route_item, "upstream_host");
@ -451,21 +510,22 @@ int config_hot_reload(const char *filename) {
route->patches.rule_count);
i++;
}
new_config.route_count = i;
new_config->route_count = i;
}
cJSON_Delete(root);
pthread_rwlock_wrlock(&config_lock);
route_config_t *old_routes = config.routes;
config.routes = new_config.routes;
config.route_count = new_config.route_count;
app_config_t *old_config = config;
config = new_config;
pthread_rwlock_unlock(&config_lock);
if (old_routes) {
free(old_routes);
if (old_config) {
old_config->next = stale_configs_head;
stale_configs_head = old_config;
config_ref_dec(old_config);
}
log_info("Hot-reload complete: %d routes loaded", new_config.route_count);
log_info("Hot-reload complete: %d routes loaded", new_config->route_count);
return 1;
}

View File

@ -5,7 +5,7 @@
#define CONFIG_RELOAD_INTERVAL_SECONDS 3
extern app_config_t config;
extern app_config_t *config;
int config_load(const char *filename);
void config_free(void);
@ -14,4 +14,7 @@ route_config_t *config_find_route(const char *hostname);
int config_check_file_changed(const char *filename);
int config_hot_reload(const char *filename);
void config_ref_inc(app_config_t *conf);
void config_ref_dec(app_config_t *conf);
#endif

View File

@ -199,6 +199,10 @@ void connection_close(int fd) {
buffer_free(&conn->read_buf);
buffer_free(&conn->write_buf);
if (conn->config) {
config_ref_dec(conn->config);
}
memset(conn, 0, sizeof(connection_t));
conn->type = CONN_TYPE_UNUSED;
conn->fd = -1;
@ -375,12 +379,21 @@ static int try_upstream_connect(struct sockaddr_in *addr, int *out_fd) {
void connection_connect_to_upstream(connection_t *client, const char *data, size_t data_len) {
if (!client || !data) return;
app_config_t *current_config = config;
if (!current_config) {
connection_send_error_response(client, 503, "Service Unavailable", "Configuration not loaded");
return;
}
route_config_t *route = config_find_route(client->request.host);
if (!route) {
connection_send_error_response(client, 502, "Bad Gateway", "No route configured for this host");
return;
}
client->config = current_config;
config_ref_inc(client->config);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
@ -475,11 +488,19 @@ void connection_connect_to_upstream(connection_t *client, const char *data, size
while(current < end) {
const char* line_end = memchr(current, '\n', end - current);
if (!line_end) break;
if (strncasecmp(current, "Host:", 5) == 0) {
if (strncasecmp(current, "Host", 4) == 0) {
const char *colon = current + 4;
while (colon < line_end && (*colon == ' ' || *colon == '\t')) {
colon++;
}
if (colon < line_end && *colon == ':') {
old_host_header_start = current;
old_host_header_end = line_end + 1;
break;
}
}
current = line_end + 1;
}

View File

@ -32,23 +32,7 @@ static void signal_handler(int sig) {
static void reload_configuration(void) {
if (!g_config_file) return;
log_info("Reloading configuration from %s", g_config_file);
app_config_t old_config = config;
memset(&config, 0, sizeof(app_config_t));
if (!config_load(g_config_file)) {
log_error("Failed to reload configuration, keeping old config");
config = old_config;
return;
}
if (old_config.routes) {
free(old_config.routes);
}
log_info("Configuration reloaded successfully");
config_hot_reload(g_config_file);
}
static void graceful_shutdown(void) {
@ -185,11 +169,11 @@ int main(int argc, char *argv[]) {
}
connection_init_all();
connection_setup_listener(config.port);
connection_setup_listener(config->port);
log_info("Port %d", config.port);
log_info("Dashboard: http://localhost:%d/rproxy/dashboard", config.port);
log_info("Stats: http://localhost:%d/rproxy/api/stats", config.port);
log_info("Port %d", config->port);
log_info("Dashboard: http://localhost:%d/rproxy/dashboard", config->port);
log_info("Stats: http://localhost:%d/rproxy/api/stats", config->port);
log_info("Send SIGHUP to reload configuration");
atexit(cleanup);

View File

@ -70,6 +70,7 @@ struct connection_s;
struct vhost_stats_s;
struct route_config_s;
struct app_config_s;
typedef struct connection_s {
conn_type_t type;
@ -87,6 +88,7 @@ typedef struct connection_s {
int half_closed;
int write_shutdown;
struct route_config_s *route;
struct app_config_s *config;
int is_textual_content;
int content_type_checked;
int patch_blocked;
@ -120,10 +122,12 @@ typedef struct route_config_s {
patch_config_t patches;
} route_config_t;
typedef struct {
typedef struct app_config_s {
int port;
route_config_t *routes;
int route_count;
_Atomic int ref_count;
struct app_config_s *next;
} app_config_t;
typedef struct {