From 9f4547d9f74f12e2a6e56f14f0aa494d6c5ed0df Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Mon, 18 Aug 2025 14:13:54 -0400 Subject: [PATCH] Validate config --- src/config.cpp | 118 +++++++++++++++++++++++++++++++++++++++++++++++++ src/config.hpp | 13 ++++++ 2 files changed, 131 insertions(+) diff --git a/src/config.cpp b/src/config.cpp index 82f7b92..09e17f7 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -15,6 +15,10 @@ ConfigParser::load_from_file(const std::string &file_path) { parse_commit_config(toml_data, config.commit); parse_subscription_config(toml_data, config.subscription); + if (!validate_config(config)) { + return std::nullopt; + } + return config; } catch (const std::exception &e) { std::cerr << "Error parsing config file '" << file_path << "': " << e.what() @@ -33,6 +37,10 @@ ConfigParser::parse_toml_string(const std::string &toml_content) { parse_commit_config(toml_data, config.commit); parse_subscription_config(toml_data, config.subscription); + if (!validate_config(config)) { + return std::nullopt; + } + return config; } catch (const std::exception &e) { std::cerr << "Error parsing TOML content: " << e.what() << std::endl; @@ -100,4 +108,114 @@ void ConfigParser::parse_subscription_config(const auto &toml_data, }); } +bool ConfigParser::validate_config(const Config &config) { + bool valid = true; + + // Validate server configuration + if (config.server.port <= 0 || config.server.port > 65535) { + std::cerr + << "Configuration error: server.port must be between 1 and 65535, got " + << config.server.port << std::endl; + valid = false; + } + + if (config.server.max_request_size_bytes == 0) { + std::cerr << "Configuration error: server.max_request_size_bytes must be " + "greater than 0" + << std::endl; + valid = false; + } + + if (config.server.max_request_size_bytes > 100 * 1024 * 1024) { // 100MB limit + std::cerr << "Configuration error: server.max_request_size_bytes too large " + "(max 100MB), got " + << config.server.max_request_size_bytes << " bytes" << std::endl; + valid = false; + } + + if (config.server.accept_threads < 1 || config.server.accept_threads > 100) { + std::cerr << "Configuration error: server.accept_threads must be between 1 " + "and 100, got " + << config.server.accept_threads << std::endl; + valid = false; + } + + if (config.server.network_threads < 1 || + config.server.network_threads > 1000) { + std::cerr << "Configuration error: server.network_threads must be between " + "1 and 1000, got " + << config.server.network_threads << std::endl; + valid = false; + } + + if (config.server.event_batch_size < 1 || + config.server.event_batch_size > 10000) { + std::cerr << "Configuration error: server.event_batch_size must be between " + "1 and 10000, got " + << config.server.event_batch_size << std::endl; + valid = false; + } + + // Validate commit configuration + if (config.commit.min_request_id_length < 8 || + config.commit.min_request_id_length > 256) { + std::cerr << "Configuration error: commit.min_request_id_length must be " + "between 8 and 256, got " + << config.commit.min_request_id_length << std::endl; + valid = false; + } + + if (config.commit.request_id_retention_hours.count() < 1 || + config.commit.request_id_retention_hours.count() > 8760) { // 1 year max + std::cerr << "Configuration error: commit.request_id_retention_hours must " + "be between 1 and 8760, got " + << config.commit.request_id_retention_hours.count() << std::endl; + valid = false; + } + + if (config.commit.request_id_retention_versions == 0) { + std::cerr << "Configuration error: commit.request_id_retention_versions " + "must be greater than 0" + << std::endl; + valid = false; + } + + // Validate subscription configuration + if (config.subscription.max_buffer_size_bytes == 0) { + std::cerr << "Configuration error: subscription.max_buffer_size_bytes must " + "be greater than 0" + << std::endl; + valid = false; + } + + if (config.subscription.max_buffer_size_bytes > + 1024 * 1024 * 1024) { // 1GB limit + std::cerr << "Configuration error: subscription.max_buffer_size_bytes too " + "large (max 1GB), got " + << config.subscription.max_buffer_size_bytes << " bytes" + << std::endl; + valid = false; + } + + if (config.subscription.keepalive_interval.count() < 1 || + config.subscription.keepalive_interval.count() > 3600) { // 1 hour max + std::cerr << "Configuration error: subscription.keepalive_interval must be " + "between 1 and 3600 seconds, got " + << config.subscription.keepalive_interval.count() << std::endl; + valid = false; + } + + // Cross-validation checks + if (config.server.max_request_size_bytes > + config.subscription.max_buffer_size_bytes) { + std::cerr << "Configuration warning: server.max_request_size_bytes (" + << config.server.max_request_size_bytes + << ") is larger than subscription.max_buffer_size_bytes (" + << config.subscription.max_buffer_size_bytes << ")" << std::endl; + // This is just a warning, not a validation failure + } + + return valid; +} + } // namespace weaseldb diff --git a/src/config.hpp b/src/config.hpp index 3864224..7f19b84 100644 --- a/src/config.hpp +++ b/src/config.hpp @@ -93,6 +93,19 @@ public: static std::optional parse_toml_string(const std::string &toml_content); + /** + * @brief Validate configuration values are within reasonable bounds. + * + * This function performs comprehensive validation of all configuration + * parameters and writes detailed diagnostic messages to stderr for any + * validation failures or warnings encountered. + * + * @param config Configuration object to validate + * @return true if configuration is valid, false otherwise + * @note Validation errors and warnings are written to stderr + */ + static bool validate_config(const Config &config); + private: // Generic configuration parsing utilities template