feat(structural rules)
This commit is contained in:
parent
010ee3e8b6
commit
4f89a2918c
@ -4,6 +4,7 @@
|
||||
#include <webserv/config/validation/directive_rules/AValidationRule.hpp> // for AValidationRule
|
||||
#include <webserv/config/validation/directive_rules/AllowedValuesRule.hpp> // for AllowedValuesRule
|
||||
#include <webserv/config/validation/directive_rules/PortValidationRule.hpp> // for PortValidationRule
|
||||
#include <webserv/config/validation/structural_rules/StructuralRules.hpp> // for structural rules
|
||||
#include <webserv/log/Log.hpp> // for LOCATION, Log
|
||||
|
||||
#include <string> // for basic_string, string
|
||||
@ -12,6 +13,11 @@ ConfigValidator::ConfigValidator(const GlobalConfig *config) : engine_(std::make
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
|
||||
/*Structural Rules*/
|
||||
engine_->addStructuralRule(std::make_unique<MinimumServerBlocksRule>(1));
|
||||
engine_->addStructuralRule(std::make_unique<RequiredLocationBlocksRule>(1));
|
||||
engine_->addStructuralRule(std::make_unique<UniqueServerNamesRule>());
|
||||
|
||||
/*Global Directive Rules*/
|
||||
|
||||
/*Server Directive Rules*/
|
||||
|
||||
@ -28,6 +28,14 @@ void ValidationEngine::addLocationRule(const std::string &directiveName, std::un
|
||||
addRule(locationRules_, directiveName, std::move(rule));
|
||||
}
|
||||
|
||||
void ValidationEngine::addStructuralRule(std::unique_ptr<AStructuralValidationRule> rule)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
if (rule != nullptr) {
|
||||
structuralRules_.push_back(std::move(rule));
|
||||
}
|
||||
}
|
||||
|
||||
void ValidationEngine::addRule(RuleMap &ruleMap, const std::string &directiveName,
|
||||
std::unique_ptr<AValidationRule> rule)
|
||||
{
|
||||
@ -68,7 +76,7 @@ std::vector<ValidationResult> ValidationEngine::getWarnings() const
|
||||
|
||||
bool ValidationEngine::hasErrors() const
|
||||
{
|
||||
for (const auto &result : results_)
|
||||
for (const auto &result : results_) //NOLINT(readability-use-anyofallof)
|
||||
{
|
||||
if (!result.isValidResult())
|
||||
{
|
||||
@ -114,13 +122,44 @@ void ValidationEngine::validateConfig(RuleMap const &rulesMap, const AConfig *co
|
||||
void ValidationEngine::validateLocationConfig(const std::string &path, const LocationConfig *config)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
|
||||
// Run location structural validation rules
|
||||
for (const auto &rule : structuralRules_) {
|
||||
try {
|
||||
ValidationResult result = rule->validateLocation(config);
|
||||
if (!result.isValidResult()) {
|
||||
results_.push_back(result);
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
results_.push_back(ValidationResult::error(
|
||||
"Structural rule '" + rule->getRuleName() + "' threw exception for location '" + path + "': " + e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
// Run location directive validation rules
|
||||
validateConfig(locationRules_, config);
|
||||
}
|
||||
|
||||
void ValidationEngine::validateServerConfig(const ServerConfig *config)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
|
||||
// Run server structural validation rules
|
||||
for (const auto &rule : structuralRules_) {
|
||||
try {
|
||||
ValidationResult result = rule->validateServer(config);
|
||||
if (!result.isValidResult()) {
|
||||
results_.push_back(result);
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
results_.push_back(ValidationResult::error(
|
||||
"Structural rule '" + rule->getRuleName() + "' threw exception: " + e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
// Run server directive validation rules
|
||||
validateConfig(serverRules_, config);
|
||||
|
||||
for (const auto &path : config->getLocationPaths())
|
||||
{
|
||||
const LocationConfig *locationConfig = config->getLocation(path);
|
||||
@ -134,7 +173,23 @@ void ValidationEngine::validateServerConfig(const ServerConfig *config)
|
||||
void ValidationEngine::validateGlobalConfig(const GlobalConfig *config)
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
|
||||
// Run global structural validation rules
|
||||
for (const auto &rule : structuralRules_) {
|
||||
try {
|
||||
ValidationResult result = rule->validateGlobal(config);
|
||||
if (!result.isValidResult()) {
|
||||
results_.push_back(result);
|
||||
}
|
||||
} catch (const std::exception &e) {
|
||||
results_.push_back(ValidationResult::error(
|
||||
"Structural rule '" + rule->getRuleName() + "' threw exception: " + e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
// Run global directive validation rules
|
||||
validateConfig(globalRules_, config);
|
||||
|
||||
for (const auto *serverConfig : config->getServerConfigs())
|
||||
{
|
||||
validateServerConfig(serverConfig);
|
||||
@ -149,6 +204,8 @@ ValidationEngine::ValidationEngine(const GlobalConfig *globalConfig) : globalCon
|
||||
void ValidationEngine::validate()
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
results_.clear(); // Clear previous results
|
||||
|
||||
if (globalConfig_ != nullptr)
|
||||
{
|
||||
validateGlobalConfig(globalConfig_);
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#include <webserv/config/ServerConfig.hpp>
|
||||
#include <webserv/config/validation/ValidationResult.hpp> // for ValidationResult
|
||||
#include <webserv/config/validation/directive_rules/AValidationRule.hpp> // for AValidationRule
|
||||
#include <webserv/config/validation/structural_rules/AStructuralValidationRule.hpp> // for AStructuralValidationRule
|
||||
|
||||
#include <map> // for map
|
||||
#include <memory> // for unique_ptr
|
||||
@ -33,6 +34,9 @@ class ValidationEngine
|
||||
void addServerRule(const std::string &directiveName, std::unique_ptr<AValidationRule> rule);
|
||||
void addLocationRule(const std::string &directiveName, std::unique_ptr<AValidationRule> rule);
|
||||
|
||||
// Structural validation rules
|
||||
void addStructuralRule(std::unique_ptr<AStructuralValidationRule> rule);
|
||||
|
||||
void validate();
|
||||
|
||||
[[nodiscard]] std::vector<ValidationResult> getValidationResults() const;
|
||||
@ -47,9 +51,11 @@ class ValidationEngine
|
||||
void validateGlobalConfig(const GlobalConfig *config);
|
||||
void validateServerConfig(const ServerConfig *config);
|
||||
void validateLocationConfig(const std::string &path, const LocationConfig *config);
|
||||
|
||||
RuleMap globalRules_;
|
||||
RuleMap serverRules_;
|
||||
RuleMap locationRules_;
|
||||
std::vector<std::unique_ptr<AStructuralValidationRule>> structuralRules_;
|
||||
const GlobalConfig *globalConfig_;
|
||||
|
||||
std::vector<ValidationResult> results_;
|
||||
|
||||
@ -0,0 +1,58 @@
|
||||
#pragma once
|
||||
|
||||
#include "webserv/config/validation/ValidationResult.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
class GlobalConfig;
|
||||
class ServerConfig;
|
||||
class LocationConfig;
|
||||
|
||||
class AStructuralValidationRule
|
||||
{
|
||||
private:
|
||||
std::string ruleName_;
|
||||
std::string description_;
|
||||
|
||||
protected:
|
||||
AStructuralValidationRule(const std::string &ruleName, const std::string &description)
|
||||
: ruleName_(ruleName), description_(description) {}
|
||||
|
||||
public:
|
||||
virtual ~AStructuralValidationRule() = default;
|
||||
|
||||
AStructuralValidationRule(const AStructuralValidationRule &other) = delete;
|
||||
AStructuralValidationRule &operator=(const AStructuralValidationRule &other) = delete;
|
||||
AStructuralValidationRule(AStructuralValidationRule &&other) noexcept = delete;
|
||||
AStructuralValidationRule &operator=(AStructuralValidationRule &&other) noexcept = delete;
|
||||
|
||||
// Virtual validation methods - override as needed
|
||||
[[nodiscard]] virtual ValidationResult validateGlobal(const GlobalConfig *config) const
|
||||
{
|
||||
static_cast<void>(config); // Suppress unused parameter warning
|
||||
return ValidationResult::success(); // Default: no global validation
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual ValidationResult validateServer(const ServerConfig *config) const
|
||||
{
|
||||
static_cast<void>(config); // Suppress unused parameter warning
|
||||
return ValidationResult::success(); // Default: no server validation
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual ValidationResult validateLocation(const LocationConfig *config) const
|
||||
{
|
||||
static_cast<void>(config); // Suppress unused parameter warning
|
||||
return ValidationResult::success(); // Default: no location validation
|
||||
}
|
||||
|
||||
// Non-virtual getters - set in constructor
|
||||
[[nodiscard]] std::string getRuleName() const
|
||||
{
|
||||
return ruleName_;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string getDescription() const
|
||||
{
|
||||
return description_;
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,32 @@
|
||||
#include "webserv/config/validation/structural_rules/MinimumServerBlocksRule.hpp"
|
||||
|
||||
#include "webserv/config/GlobalConfig.hpp"
|
||||
#include "webserv/log/Log.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
MinimumServerBlocksRule::MinimumServerBlocksRule(size_t minimumServers)
|
||||
: AStructuralValidationRule("MinimumServerBlocksRule",
|
||||
"Ensures global config has at least " + std::to_string(minimumServers) + " server block(s)"),
|
||||
minimumServers_(minimumServers)
|
||||
{
|
||||
}
|
||||
|
||||
ValidationResult MinimumServerBlocksRule::validateGlobal(const GlobalConfig *config) const
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
|
||||
if (config == nullptr) {
|
||||
return ValidationResult::error("Global config is null");
|
||||
}
|
||||
|
||||
size_t serverCount = config->getServerConfigs().size();
|
||||
|
||||
if (serverCount < minimumServers_) {
|
||||
return ValidationResult::error(
|
||||
"Global configuration must have at least " + std::to_string(minimumServers_) +
|
||||
" server block(s), but found " + std::to_string(serverCount));
|
||||
}
|
||||
|
||||
return ValidationResult::success();
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "webserv/config/validation/structural_rules/AStructuralValidationRule.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
class GlobalConfig;
|
||||
|
||||
class MinimumServerBlocksRule : public AStructuralValidationRule
|
||||
{
|
||||
private:
|
||||
size_t minimumServers_;
|
||||
|
||||
public:
|
||||
explicit MinimumServerBlocksRule(size_t minimumServers = 1);
|
||||
|
||||
~MinimumServerBlocksRule() override = default;
|
||||
|
||||
MinimumServerBlocksRule(const MinimumServerBlocksRule &other) = delete;
|
||||
MinimumServerBlocksRule &operator=(const MinimumServerBlocksRule &other) = delete;
|
||||
MinimumServerBlocksRule(MinimumServerBlocksRule &&other) noexcept = delete;
|
||||
MinimumServerBlocksRule &operator=(MinimumServerBlocksRule &&other) noexcept = delete;
|
||||
|
||||
[[nodiscard]] ValidationResult validateGlobal(const GlobalConfig *config) const override;
|
||||
};
|
||||
@ -0,0 +1,32 @@
|
||||
#include "webserv/config/validation/structural_rules/RequiredLocationBlocksRule.hpp"
|
||||
|
||||
#include "webserv/config/ServerConfig.hpp"
|
||||
#include "webserv/log/Log.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
RequiredLocationBlocksRule::RequiredLocationBlocksRule(size_t minimumLocations)
|
||||
: AStructuralValidationRule("RequiredLocationBlocksRule",
|
||||
"Ensures server has at least " + std::to_string(minimumLocations) + " location block(s)"),
|
||||
minimumLocations_(minimumLocations)
|
||||
{
|
||||
}
|
||||
|
||||
ValidationResult RequiredLocationBlocksRule::validateServer(const ServerConfig *config) const
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
|
||||
if (config == nullptr) {
|
||||
return ValidationResult::error("Server config is null");
|
||||
}
|
||||
|
||||
size_t locationCount = config->getLocationPaths().size();
|
||||
|
||||
if (locationCount < minimumLocations_) {
|
||||
return ValidationResult::error(
|
||||
"Server block must have at least " + std::to_string(minimumLocations_) +
|
||||
" location block(s), but found " + std::to_string(locationCount));
|
||||
}
|
||||
|
||||
return ValidationResult::success();
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include "webserv/config/validation/structural_rules/AStructuralValidationRule.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
class ServerConfig;
|
||||
|
||||
class RequiredLocationBlocksRule : public AStructuralValidationRule
|
||||
{
|
||||
private:
|
||||
size_t minimumLocations_;
|
||||
|
||||
public:
|
||||
explicit RequiredLocationBlocksRule(size_t minimumLocations = 1);
|
||||
|
||||
~RequiredLocationBlocksRule() override = default;
|
||||
|
||||
RequiredLocationBlocksRule(const RequiredLocationBlocksRule &other) = delete;
|
||||
RequiredLocationBlocksRule &operator=(const RequiredLocationBlocksRule &other) = delete;
|
||||
RequiredLocationBlocksRule(RequiredLocationBlocksRule &&other) noexcept = delete;
|
||||
RequiredLocationBlocksRule &operator=(RequiredLocationBlocksRule &&other) noexcept = delete;
|
||||
|
||||
[[nodiscard]] ValidationResult validateServer(const ServerConfig *config) const override;
|
||||
};
|
||||
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
// Base class
|
||||
#include "webserv/config/validation/structural_rules/AStructuralValidationRule.hpp"
|
||||
|
||||
// Concrete structural validation rules
|
||||
#include "webserv/config/validation/structural_rules/MinimumServerBlocksRule.hpp"
|
||||
#include "webserv/config/validation/structural_rules/RequiredLocationBlocksRule.hpp"
|
||||
#include "webserv/config/validation/structural_rules/UniqueServerNamesRule.hpp"
|
||||
|
||||
/**
|
||||
* Structural Validation Rules for WebServ Configuration
|
||||
*
|
||||
* These rules validate the overall structure and relationships
|
||||
* between configuration blocks, rather than individual directive values.
|
||||
*
|
||||
* Available Rules:
|
||||
*
|
||||
* 1. MinimumServerBlocksRule - Ensures global config has minimum number of server blocks
|
||||
* 2. RequiredLocationBlocksRule - Ensures each server has minimum number of location blocks
|
||||
* 3. UniqueServerNamesRule - Ensures all server names are unique across configuration
|
||||
*
|
||||
* Usage:
|
||||
*
|
||||
* // In ValidationEngine setup
|
||||
* engine.addStructuralRule(std::make_unique<MinimumServerBlocksRule>(1));
|
||||
* engine.addStructuralRule(std::make_unique<RequiredLocationBlocksRule>(1));
|
||||
* engine.addStructuralRule(std::make_unique<UniqueServerNamesRule>());
|
||||
*
|
||||
* Each rule inherits from AStructuralValidationRule and can validate at:
|
||||
* - Global level (validateGlobal)
|
||||
* - Server level (validateServer)
|
||||
* - Location level (validateLocation)
|
||||
*
|
||||
* Rules have descriptive names and descriptions set in their constructors.
|
||||
*/
|
||||
@ -0,0 +1,49 @@
|
||||
#include "webserv/config/validation/structural_rules/UniqueServerNamesRule.hpp"
|
||||
|
||||
#include "webserv/config/GlobalConfig.hpp"
|
||||
#include "webserv/config/ServerConfig.hpp"
|
||||
#include "webserv/log/Log.hpp"
|
||||
|
||||
#include <optional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
UniqueServerNamesRule::UniqueServerNamesRule()
|
||||
: AStructuralValidationRule("UniqueServerNamesRule",
|
||||
"Ensures all server blocks have unique server names")
|
||||
{
|
||||
}
|
||||
|
||||
ValidationResult UniqueServerNamesRule::validateGlobal(const GlobalConfig *config) const
|
||||
{
|
||||
Log::trace(LOCATION);
|
||||
|
||||
if (config == nullptr) {
|
||||
return ValidationResult::error("Global config is null");
|
||||
}
|
||||
|
||||
std::set<std::string> serverNames;
|
||||
auto servers = config->getServerConfigs();
|
||||
|
||||
for (const auto *server : servers) {
|
||||
if (server == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto serverNameOpt = server->get<std::string>("server_name");
|
||||
auto listenOpt = server->get<int>("listen");
|
||||
if (serverNameOpt.has_value() && listenOpt.has_value()) {
|
||||
const std::string &serverName = serverNameOpt.value();
|
||||
int listenPort = listenOpt.value();
|
||||
|
||||
if (serverNames.contains(serverName + ":" + std::to_string(listenPort))) {
|
||||
return ValidationResult::error(
|
||||
"Duplicate server name '" + serverName + "' found in configuration");
|
||||
}
|
||||
|
||||
serverNames.insert(serverName + ":" + std::to_string(listenPort));
|
||||
}
|
||||
}
|
||||
|
||||
return ValidationResult::success();
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include "webserv/config/validation/structural_rules/AStructuralValidationRule.hpp"
|
||||
|
||||
class GlobalConfig;
|
||||
|
||||
class UniqueServerNamesRule : public AStructuralValidationRule
|
||||
{
|
||||
public:
|
||||
UniqueServerNamesRule();
|
||||
|
||||
~UniqueServerNamesRule() override = default;
|
||||
|
||||
UniqueServerNamesRule(const UniqueServerNamesRule &other) = delete;
|
||||
UniqueServerNamesRule &operator=(const UniqueServerNamesRule &other) = delete;
|
||||
UniqueServerNamesRule(UniqueServerNamesRule &&other) noexcept = delete;
|
||||
UniqueServerNamesRule &operator=(UniqueServerNamesRule &&other) noexcept = delete;
|
||||
|
||||
[[nodiscard]] ValidationResult validateGlobal(const GlobalConfig *config) const override;
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user