diff --git a/source/server/http_filter_config_base.cc b/source/server/http_filter_config_base.cc new file mode 100644 index 000000000..cd1d52e14 --- /dev/null +++ b/source/server/http_filter_config_base.cc @@ -0,0 +1,42 @@ +#include "server/http_filter_config_base.h" + +namespace Nighthawk { +namespace Server { + +FilterConfigurationBase::FilterConfigurationBase( + const nighthawk::server::ResponseOptions& proto_config, absl::string_view filter_name) + : filter_name_(filter_name), + server_config_(std::make_shared(std::move(proto_config))), + effective_config_(server_config_) {} + +void FilterConfigurationBase::computeEffectiveConfiguration( + const Envoy::Http::RequestHeaderMap& headers) { + const auto* request_config_header = headers.get(TestServer::HeaderNames::get().TestServerConfig); + if (request_config_header) { + nighthawk::server::ResponseOptions response_options = *server_config_; + std::string error_message; + if (Configuration::mergeJsonConfig(request_config_header->value().getStringView(), + response_options, error_message)) { + effective_config_ = + std::make_shared(std::move(response_options)); + } else { + effective_config_ = absl::InvalidArgumentError(error_message); + } + } +} + +bool FilterConfigurationBase::maybeSendErrorReply( + Envoy::Http::StreamDecoderFilterCallbacks& decoder_callbacks) const { + if (!effective_config_.ok()) { + decoder_callbacks.sendLocalReply(static_cast(500), + fmt::format("{} didn't understand the request: {}", + filter_name_, + effective_config_.status().message()), + nullptr, absl::nullopt, ""); + return true; + } + return false; +} + +} // namespace Server +} // namespace Nighthawk diff --git a/source/server/http_filter_config_base.h b/source/server/http_filter_config_base.h new file mode 100644 index 000000000..88d20405e --- /dev/null +++ b/source/server/http_filter_config_base.h @@ -0,0 +1,81 @@ +#pragma once + +#include + +#include "envoy/server/filter_config.h" + +#include "external/envoy/source/common/common/statusor.h" + +#include "api/server/response_options.pb.h" + +#include "server/configuration.h" +#include "server/well_known_headers.h" + +#include "absl/status/status.h" + +namespace Nighthawk { +namespace Server { + +/** + * Canonical representation of the effective filter configuration. + * We use a shared pointer to avoid copying in the static configuration flow. + */ +using EffectiveFilterConfigurationPtr = std::shared_ptr; + +/** + * Provides functionality for parsing and merging request-header based configuration, as well as + * generating a common error response accross all extensions. + */ +class FilterConfigurationBase { +public: + /** + * @brief Construct a new Filter Configuration Base object + * + * @param proto_config the static disk-based response options configuration + * @param filter_name name of the extension that is consuming this. Used during error response + * generation. + */ + FilterConfigurationBase(const nighthawk::server::ResponseOptions& proto_config, + absl::string_view filter_name); + + /** + * Copmute the effective configuration, based on considering the static configuration as well as + * any configuration provided via request headers. + * + * @param request_headers Full set of request headers to be inspected for configuration. + */ + void computeEffectiveConfiguration(const Envoy::Http::RequestHeaderMap& request_headers); + + /** + * Send an error reply based on status of the effective configuration. For example, when dynamic + * configuration delivered via request headers could not be parsed or was out of spec. + * + * @param decoder_callbacks Decoder used to generate the reply. + * @return true iff an error reply was generated. + */ + bool maybeSendErrorReply(Envoy::Http::StreamDecoderFilterCallbacks& decoder_callbacks) const; + + /** + * @brief Get the effective configuration. Depending on state ,this could be one of static + * configuration, dynamic configuration, or an error status. + * + * @return const absl::StatusOr The effective configuration, or + * an error status. + */ + const absl::StatusOr getEffectiveConfiguration() const { + return effective_config_; + } + + /** + * @return absl::string_view Name of the filter that constructed this instance. + */ + absl::string_view filter_name() const { return filter_name_; } + +private: + const std::string filter_name_; + const std::shared_ptr server_config_; + absl::StatusOr effective_config_; +}; + +} // namespace Server +} // namespace Nighthawk