From 378f64fc756384cbcf04181bc6f7cebb0b591b69 Mon Sep 17 00:00:00 2001 From: retoor Date: Sat, 29 Nov 2025 13:18:07 +0100 Subject: [PATCH] Update. --- src/config.c | 134 ++++++++++++++++++++++++++++++++++------------- src/config.h | 5 +- src/connection.c | 29 ++++++++-- src/main.c | 26 ++------- src/types.h | 6 ++- 5 files changed, 136 insertions(+), 64 deletions(-) diff --git a/src/config.c b/src/config.c index 81111c1..2986942 100644 --- a/src/config.c +++ b/src/config.c @@ -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 = ¤t_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; } diff --git a/src/config.h b/src/config.h index 8b3925a..82e5aff 100644 --- a/src/config.h +++ b/src/config.h @@ -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 diff --git a/src/connection.c b/src/connection.c index 8aa85e8..080465a 100644 --- a/src/connection.c +++ b/src/connection.c @@ -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) { - old_host_header_start = current; - old_host_header_end = line_end + 1; - break; + + 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; } diff --git a/src/main.c b/src/main.c index ce42e9f..c15a179 100644 --- a/src/main.c +++ b/src/main.c @@ -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); diff --git a/src/types.h b/src/types.h index df64eee..f2e53f4 100644 --- a/src/types.h +++ b/src/types.h @@ -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 {