namespace agent {
CtrlAgentCfgContext::CtrlAgentCfgContext()
- : http_host_(""), http_port_(0),
+ : http_host_(""), http_port_(0), http_headers_(),
trust_anchor_(""), cert_file_(""), key_file_(""), cert_required_(true) {
}
CtrlAgentCfgContext::CtrlAgentCfgContext(const CtrlAgentCfgContext& orig)
: ConfigBase(), ctrl_sockets_(orig.ctrl_sockets_),
http_host_(orig.http_host_), http_port_(orig.http_port_),
+ http_headers_(orig.http_headers_),
trust_anchor_(orig.trust_anchor_), cert_file_(orig.cert_file_),
key_file_(orig.key_file_), cert_required_(orig.cert_required_),
hooks_config_(orig.hooks_config_), auth_config_(orig.auth_config_) {
ca->set("http-host", Element::create(http_host_));
// Set http-port
ca->set("http-port", Element::create(static_cast<int64_t>(http_port_)));
+ // Set http-headers
+ if (!http_headers_.empty()) {
+ ca->set("http-headers", toElement(http_headers_));
+ }
// Set TLS setup when enabled
if (!trust_anchor_.empty()) {
ca->set("trust-anchor", Element::create(trust_anchor_));
#include <cc/data.h>
#include <hooks/hooks_config.h>
#include <http/auth_config.h>
+#include <http/cfg_http_header.h>
#include <process/d_cfg_mgr.h>
#include <boost/pointer_cast.hpp>
#include <map>
return (http_port_);
}
+ /// @brief Sets http-headers parameter
+ ///
+ /// @param headers Collection of config HTTP headers.
+ void setHttpHeaders(const isc::http::CfgHttpHeaders& headers) {
+ http_headers_ = headers;
+ }
+
+ /// @brief Returns http-headers parameter
+ ///
+ /// @return Collection of config HTTP headers.
+ const isc::http::CfgHttpHeaders& getHttpHeaders() const {
+ return (http_headers_);
+ }
+
/// @brief Sets HTTP authentication configuration.
///
/// @note Only the basic HTTP authentication is supported.
/// TCP port the CA should listen on.
uint16_t http_port_;
+ /// Config HTTP headers.
+ isc::http::CfgHttpHeaders http_headers_;
+
/// Trust anchor aka Certificate Authority (can be a file name or
/// a directory path).
std::string trust_anchor_;
ctx->setAuthConfig(auth);
}
+ // HTTP headers are fifth.
+ ConstElementPtr headers_config = config->get("http-headers");
+ if (headers_config) {
+ using namespace isc::http;
+ ctx->setHttpHeaders(parseCfgHttpHeaders(headers_config));
+ }
+
// User context can be done at anytime.
ConstElementPtr user_context = config->get("user-context");
if (user_context) {
libkea_http_la_SOURCES += auth_messages.cc auth_messages.h
libkea_http_la_SOURCES += basic_auth_config.cc basic_auth_config.h
libkea_http_la_SOURCES += basic_auth.cc basic_auth.h
+libkea_http_la_SOURCES += cfg_http_header.h cfg_http_header.cc
libkea_http_la_CXXFLAGS = $(AM_CXXFLAGS)
libkea_http_la_CPPFLAGS = $(AM_CPPFLAGS)
auth_messages.h \
basic_auth.h \
basic_auth_config.h \
+ cfg_http_header.h \
client.h \
connection.h \
connection_pool.h \
--- /dev/null
+// Copyright (C) 2024 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <cc/simple_parser.h>
+#include <http/cfg_http_header.h>
+
+using namespace isc;
+using namespace isc::data;
+using namespace isc::dhcp;
+using namespace std;
+
+namespace isc {
+namespace http {
+
+ElementPtr
+CfgHttpHeader::toElement() const {
+ ElementPtr map = isc::data::Element::createMap();
+ contextToElement(map);
+ map->set("name", Element::create(name_));
+ map->set("value", Element::create(value_));
+ return (map);
+}
+
+ElementPtr
+toElement(const CfgHttpHeaders& headers) {
+ ElementPtr list = Element::createList();
+ for (auto const& header : headers) {
+ list->add(header.toElement());
+ }
+ return (list);
+}
+
+namespace {
+
+const SimpleKeywords HTTP_HEADER_KEYWORDS = {
+ { "name", Element::string },
+ { "value", Element::string },
+ { "user-context", Element::map }
+};
+
+const SimpleRequiredKeywords HTTP_HEADER_REQUIRED = { "name", "value" };
+
+CfgHttpHeader
+parseCfgHttpHeader(const ConstElementPtr& config) {
+ if (!config) {
+ // Should not happen.
+ isc_throw(DhcpConfigError, "null 'http-headers' item");
+ }
+ if (config->getType() != Element::map) {
+ isc_throw(DhcpConfigError, "invalid type specified for 'http-headers' "
+ "item (" << config->getPosition() << ")");
+ }
+ SimpleParser::checkKeywords(HTTP_HEADER_KEYWORDS, config);
+ SimpleParser::checkRequired(HTTP_HEADER_REQUIRED, config);
+ string name = config->get("name")->stringValue();
+ if (name.empty()) {
+ isc_throw(DhcpConfigError, "empty 'name' ("
+ << config->get("name")->getPosition() << ")");
+ }
+ string value = config->get("value")->stringValue();
+ if (value.empty()) {
+ isc_throw(DhcpConfigError, "empty 'value' ("
+ << config->get("value")->getPosition() << ")");
+ }
+ CfgHttpHeader header(name, value);
+ ConstElementPtr user_context = config->get("user-context");
+ if (user_context) {
+ header.setContext(user_context);
+ }
+ return (header);
+}
+
+}
+
+CfgHttpHeaders
+parseCfgHttpHeaders(const ConstElementPtr& config) {
+ CfgHttpHeaders headers;
+ if (!config) {
+ return (headers);
+ }
+ if (config->getType() != Element::list) {
+ isc_throw(DhcpConfigError, "invalid type specified for parameter "
+ "'http-headers' (" << config->getPosition() << ")");
+ }
+ for (auto const& item : config->listValue()) {
+ headers.push_back(parseCfgHttpHeader(item));
+ }
+ return (headers);
+}
+
+} // namespace http
+} // namespace isc
--- /dev/null
+// Copyright (C) 2024 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#ifndef CFG_HTTP_HEADER_H
+#define CFG_HTTP_HEADER_H
+
+#include <cc/cfg_to_element.h>
+#include <cc/data.h>
+#include <cc/user_context.h>
+#include <http/request.h>
+#include <http/response.h>
+
+namespace isc {
+namespace http {
+
+/// @brief Config HTTP header.
+class CfgHttpHeader : public isc::data::UserContext, public isc::data::CfgToElement {
+public:
+ std::string name_;
+ std::string value_;
+
+ /// @brief Constructor.
+ ///
+ /// @param name Header name.
+ /// @param value Header value.
+ CfgHttpHeader(const std::string& name, const std::string& value)
+ : name_(name), value_(value) {
+ }
+
+ /// @brief Unparses config HTTP header.
+ ///
+ /// @return A pointer to unparsed header configuration.
+ virtual isc::data::ElementPtr toElement() const;
+};
+
+/// @brief Collection of config HTTP headers.
+typedef std::vector<CfgHttpHeader> CfgHttpHeaders;
+
+/// @brief Copy config HTTP headers to message.
+///
+/// @tparam HTTP_MSG Either HttpRequest or HttpResponse.
+/// @param headers Config HTTP headers.
+/// @param message HTTP_MSG target object.
+template<typename HTTP_MSG>
+void copyHttpHeaders(const CfgHttpHeaders& headers, const HTTP_MSG& message) {
+ for (auto const& header : headers) {
+ message.context()->headers_.
+ push_back(HttpHeaderContext(header.name_, header.value_));
+ }
+}
+
+/// @brief Unparse config HTTP headers.
+///
+/// @param headers Config HTTP headers.
+/// @return A pointer to unparsed headers configuration.
+isc::data::ElementPtr toElement(const CfgHttpHeaders& headers);
+
+/// @brief Parse config HTTP headers.
+///
+/// @param config Element holding the HTTP headers configuration.
+/// @return The HTTP headers.
+/// @throw DhcpConfigError when the configuration is invalid.
+CfgHttpHeaders parseCfgHttpHeaders(const isc::data::ConstElementPtr& config);
+
+} // namespace http
+} // namespace isc
+
+#endif
libhttp_unittests_SOURCES = basic_auth_unittests.cc
libhttp_unittests_SOURCES += basic_auth_config_unittests.cc
+libhttp_unittests_SOURCES += cfg_http_header_unittests.cc
libhttp_unittests_SOURCES += connection_pool_unittests.cc
libhttp_unittests_SOURCES += date_time_unittests.cc
libhttp_unittests_SOURCES += http_header_unittests.cc
--- /dev/null
+// Copyright (C) 2024 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <http/cfg_http_header.h>
+#include <gtest/gtest.h>
+
+using namespace isc::data;
+using namespace isc::http;
+using namespace std;
+
+namespace {
+
+// This test verifies copy to response.
+TEST(CfgHttpHeaderTest, copy) {
+ // Create a response.
+ HttpResponse response(HttpVersion(1, 0), HttpStatusCode::OK);
+ // Create a HSTS header.
+ CfgHttpHeader hsts("Strict-Transport-Security", "max-age=31536000");
+ // Create a random header.
+ CfgHttpHeader foobar("Foo", "bar");
+ // Add them to a collection.
+ CfgHttpHeaders headers;
+ headers.push_back(hsts);
+ headers.push_back(foobar);
+ // Copy headers to response.
+ EXPECT_NO_THROW(copyHttpHeaders(headers, response));
+
+ // Verify.
+ auto const& got = response.context()->headers_;
+ ASSERT_EQ(2, got.size());
+ EXPECT_EQ("Strict-Transport-Security", got[0].name_);
+ EXPECT_EQ("max-age=31536000", got[0].value_);
+ EXPECT_EQ("Foo", got[1].name_);
+ EXPECT_EQ("bar", got[1].value_);
+
+ // Unparse.
+ string expected = "[ ";
+ expected += "{ \"name\": \"Strict-Transport-Security\", ";
+ expected += "\"value\": \"max-age=31536000\" }, ";
+ expected += "{ \"name\": \"Foo\", \"value\": \"bar\" } ]";
+ EXPECT_EQ(expected, toElement(headers)->str());
+}
+
+// This test verifies parse and toElement behavior.
+TEST(CfgHttpHeaderTest, parse) {
+ // Config.
+ string config = "[\n"
+ " {\n"
+ " \"name\": \"Strict-Transport-Security\",\n"
+ " \"value\": \"max-age=31536000\",\n"
+ " \"user-context\": { \"comment\": \"HSTS header\" }\n"
+ " },{\n"
+ " \"name\": \"Foo\", \"value\": \"bar\"\n"
+ " }\n"
+ " ]\n";
+ ConstElementPtr json;
+ ASSERT_NO_THROW(json = Element::fromJSON(config));
+ CfgHttpHeaders headers;
+ ASSERT_NO_THROW(headers = parseCfgHttpHeaders(json));
+ ASSERT_EQ(2, headers.size());
+ EXPECT_EQ("Strict-Transport-Security", headers[0].name_);
+ EXPECT_EQ("max-age=31536000", headers[0].value_);
+ ConstElementPtr user_context = headers[0].getContext();
+ ASSERT_TRUE(user_context);
+ EXPECT_EQ("{ \"comment\": \"HSTS header\" }", user_context->str());
+ EXPECT_EQ("Foo", headers[1].name_);
+ EXPECT_EQ("bar", headers[1].value_);
+ EXPECT_FALSE(headers[1].getContext());
+ ConstElementPtr unparsed;
+ ASSERT_NO_THROW(unparsed = toElement(headers));
+ EXPECT_TRUE(json->equals(*unparsed));
+}
+
+}