libagent_la_SOURCES = ctrl_agent_cfg_mgr.cc ctrl_agent_cfg_mgr.h
libagent_la_SOURCES += ctrl_agent_controller.cc ctrl_agent_controller.h
+libagent_la_SOURCES += ctrl_agent_command_mgr.cc ctrl_agent_command_mgr.h
libagent_la_SOURCES += ctrl_agent_log.cc ctrl_agent_log.h
libagent_la_SOURCES += ctrl_agent_process.cc ctrl_agent_process.h
libagent_la_SOURCES += agent_parser.cc agent_parser.h
libagent_la_SOURCES += parser_context.cc parser_context.h parser_context_decl.h
+libagent_la_SOURCES += ctrl_agent_response_creator.cc ctrl_agent_response_creator.h
+libagent_la_SOURCES += ctrl_agent_response_creator_factory.h
libagent_la_SOURCES += agent_lexer.ll
nodist_libagent_la_SOURCES = ctrl_agent_messages.h ctrl_agent_messages.cc
kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la
kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
+kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/http/libkea-http.la
kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
kea_ctrl_agent_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la
--- /dev/null
+// Copyright (C) 2017 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 <agent/ctrl_agent_command_mgr.h>
+#include <cc/data.h>
+#include <boost/pointer_cast.hpp>
+
+using namespace isc::data;
+
+namespace isc {
+namespace agent {
+
+CtrlAgentCommandMgr&
+CtrlAgentCommandMgr::instance() {
+ static CtrlAgentCommandMgr command_mgr;
+ return (command_mgr);
+}
+
+CtrlAgentCommandMgr::CtrlAgentCommandMgr()
+ : HookedCommandMgr() {
+}
+
+ConstElementPtr
+CtrlAgentCommandMgr::handleCommand(const std::string& cmd_name,
+ const isc::data::ConstElementPtr& params) {
+ ElementPtr answer_list = Element::createList();
+ answer_list->add(boost::const_pointer_cast<
+ Element>(HookedCommandMgr::handleCommand(cmd_name, params)));
+ return (answer_list);
+}
+
+
+} // end of namespace isc::agent
+} // end of namespace isc
--- /dev/null
+// Copyright (C) 2017 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 CTRL_AGENT_COMMAND_MGR_H
+#define CTRL_AGENT_COMMAND_MGR_H
+
+#include <config/hooked_command_mgr.h>
+#include <boost/noncopyable.hpp>
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace agent {
+
+/// @brief Command Manager for Control Agent.
+///
+/// This is an implementation of the Command Manager within Control Agent.
+/// In addition to the standard capabilities of the @ref HookedCommandMgr
+/// it is also intended to forward commands to the respective Kea servers
+/// when the command is not supported directly by the Control Agent.
+///
+/// @todo This Command Manager doesn't yet support forwarding commands.
+///
+/// The @ref CtrlAgentCommandMgr is implemented as a singleton. The commands
+/// are registered using @c CtrlAgentCommandMgr::instance().registerCommand().
+/// The @ref CtrlAgentResponseCreator uses the sole instance of the Command
+/// Manager to handle incoming commands.
+class CtrlAgentCommandMgr : public config::HookedCommandMgr,
+ public boost::noncopyable {
+public:
+
+ /// @brief Returns sole instance of the Command Manager.
+ static CtrlAgentCommandMgr& instance();
+
+private:
+
+ /// @brief Handles the command having a given name and arguments.
+ ///
+ /// This method extends the base implementation with the ability to forward
+ /// commands to Kea servers if the Control Agent failed to handle it itself.
+ ///
+ /// @todo Currently this method only wraps an answer within a list Element.
+ /// This will be later used to include multiple answers within this list.
+ /// For now it is just a single answer from the Control Agent.
+ ///
+ /// @param cmd_name Command name.
+ /// @param params Command arguments.
+ ///
+ /// @return Pointer to the const data element representing response
+ /// to a command.
+ virtual isc::data::ConstElementPtr
+ handleCommand(const std::string& cmd_name,
+ const isc::data::ConstElementPtr& params);
+
+
+ /// @brief Private constructor.
+ ///
+ /// The instance should be created using @ref CtrlAgentCommandMgr::instance,
+ /// thus the constructor is private.
+ CtrlAgentCommandMgr();
+
+};
+
+} // end of namespace isc::agent
+} // end of namespace isc
+
+#endif
#include <config.h>
#include <agent/ctrl_agent_process.h>
+#include <agent/ctrl_agent_response_creator_factory.h>
#include <agent/ctrl_agent_log.h>
+#include <asiolink/io_address.h>
#include <cc/command_interpreter.h>
+#include <http/listener.h>
#include <boost/pointer_cast.hpp>
+using namespace isc::asiolink;
+using namespace isc::http;
using namespace isc::process;
+// Temporarily hardcoded configuration.
+namespace {
+
+const IOAddress SERVER_ADDRESS("127.0.0.1");
+const unsigned short SERVER_PORT = 8081;
+const long REQUEST_TIMEOUT = 10000;
+
+}
+
namespace isc {
namespace agent {
LOG_INFO(agent_logger, CTRL_AGENT_STARTED).arg(VERSION);
try {
+
+ HttpResponseCreatorFactoryPtr rcf(new CtrlAgentResponseCreatorFactory());
+ HttpListener http_listener(*getIoService(), SERVER_ADDRESS,
+ SERVER_PORT, rcf, REQUEST_TIMEOUT);
+ http_listener.start();
+
while (!shouldShutdown()) {
getIoService()->run_one();
}
--- /dev/null
+// Copyright (C) 2017 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 <agent/ctrl_agent_command_mgr.h>
+#include <agent/ctrl_agent_response_creator.h>
+#include <cc/data.h>
+#include <http/post_request_json.h>
+#include <http/response_json.h>
+#include <boost/pointer_cast.hpp>
+
+using namespace isc::data;
+using namespace isc::http;
+
+namespace isc {
+namespace agent {
+
+HttpRequestPtr
+CtrlAgentResponseCreator::createNewHttpRequest() const {
+ return (HttpRequestPtr(new PostHttpRequestJson()));
+}
+
+HttpResponsePtr
+CtrlAgentResponseCreator::
+createStockHttpResponse(const ConstHttpRequestPtr& request,
+ const HttpStatusCode& status_code) const {
+ // The request hasn't been finalized so the request object
+ // doesn't contain any information about the HTTP version number
+ // used. But, the context should have this data (assuming the
+ // HTTP version is parsed ok).
+ HttpVersion http_version(request->context()->http_version_major_,
+ request->context()->http_version_minor_);
+ // We only accept HTTP version 1.0 or 1.1. If other version number is found
+ // we fall back to HTTP/1.0.
+ if ((http_version < HttpVersion(1, 0)) || (HttpVersion(1, 1) < http_version)) {
+ http_version.major_ = 1;
+ http_version.minor_ = 0;
+ }
+ // This will generate the response holding JSON content.
+ HttpResponsePtr response(new HttpResponseJson(http_version, status_code));
+ return (response);
+}
+
+HttpResponsePtr
+CtrlAgentResponseCreator::
+createDynamicHttpResponse(const ConstHttpRequestPtr& request) {
+ // The request is always non-null, because this is verified by the
+ // createHttpResponse method. Let's try to convert it to the
+ // ConstPostHttpRequestJson type as this is the type generated by the
+ // createNewHttpRequest. If the conversion result is null it means that
+ // the caller did not use createNewHttpRequest method to create this
+ // instance. This is considered an error in the server logic.
+ ConstPostHttpRequestJsonPtr request_json = boost::dynamic_pointer_cast<
+ const PostHttpRequestJson>(request);
+ if (!request_json) {
+ // Notify the client that we have a problem with our server.
+ return (createStockHttpResponse(request, HttpStatusCode::INTERNAL_SERVER_ERROR));
+ }
+
+ // We have already checked that the request is finalized so the call
+ // to getBodyAsJson must not trigger an exception.
+ ConstElementPtr command = request_json->getBodyAsJson();
+
+ // Process command doesn't generate exceptions but can possibly return
+ // null response, if the handler is not implemented properly. This is
+ // again an internal server issue.
+ ConstElementPtr response = CtrlAgentCommandMgr::instance().processCommand(command);
+ if (!response) {
+ // Notify the client that we have a problem with our server.
+ return (createStockHttpResponse(request, HttpStatusCode::INTERNAL_SERVER_ERROR));
+ }
+ // The response is ok, so let's create new HTTP response with the status OK.
+ HttpResponseJsonPtr http_response = boost::dynamic_pointer_cast<
+ HttpResponseJson>(createStockHttpResponse(request, HttpStatusCode::OK));
+ http_response->setBodyAsJson(response);
+
+ return (http_response);
+}
+
+
+
+} // end of namespace isc::agent
+} // end of namespace isc
--- /dev/null
+// Copyright (C) 2017 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 CTRL_AGENT_RESPONSE_CREATOR_H
+#define CTRL_AGENT_RESPONSE_CREATOR_H
+
+#include <agent/ctrl_agent_command_mgr.h>
+#include <http/response_creator.h>
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace agent {
+
+class CtrlAgentResponseCreator;
+
+/// @brief Pointer to the @ref CtrlAgentResponseCreator.
+typedef boost::shared_ptr<CtrlAgentResponseCreator> CtrlAgentResponseCreatorPtr;
+
+/// @brief Concrete implementation of the HTTP response crator used
+/// by the Control Agent.
+///
+/// See the documentation of the @ref HttpResponseCreator for the basic
+/// information how HTTP response creators are utilized by the libkea-http
+/// library to generate HTTP responses.
+///
+/// This creator expects that received requests are encapsulated in the
+/// @ref PostHttpRequestJson objects. The generated responses are
+/// encapsulated in the HttpResponseJson objects.
+///
+/// This class uses @ref CtrlAgentCommandMgr singleton to process commands
+/// conveyed in the HTTP body. The JSON responses returned by the manager
+/// are placed in the body of the generated HTTP responses.
+class CtrlAgentResponseCreator : public http::HttpResponseCreator {
+public:
+
+ /// @brief Create a new request.
+ ///
+ /// This method creates a bare instance of the @ref PostHttpRequestJson.
+ ///
+ /// @return Pointer to the new instance of the @ref PostHttpRequestJson.
+ virtual http::HttpRequestPtr createNewHttpRequest() const;
+
+ /// @brief Creates stock HTTP response.
+ ///
+ /// @param request Pointer to an object representing HTTP request.
+ /// @param status_code Status code of the response.
+ /// @return Pointer to an @ref HttpResponseJson object representing stock
+ /// HTTP response.
+ virtual http::HttpResponsePtr
+ createStockHttpResponse(const http::ConstHttpRequestPtr& request,
+ const http::HttpStatusCode& status_code) const;
+
+private:
+
+ /// @brief Creates implementation specific HTTP response.
+ ///
+ /// @param request Pointer to an object representing HTTP request.
+ /// @return Pointer to an object representing HTTP response.
+ virtual http::HttpResponsePtr
+ createDynamicHttpResponse(const http::ConstHttpRequestPtr& request);
+};
+
+} // end of namespace isc::agent
+} // end of namespace isc
+
+#endif
--- /dev/null
+// Copyright (C) 2017 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 CTRL_AGENT_RESPONSE_CREATOR_FACTORY_H
+#define CTRL_AGENT_RESPONSE_CREATOR_FACTORY_H
+
+#include <agent/ctrl_agent_response_creator.h>
+#include <http/response_creator_factory.h>
+
+namespace isc {
+namespace agent {
+
+/// @brief HTTP response creator factory for Control Agent.
+///
+/// See the documentation of the @ref HttpResponseCreatorFactory for
+/// the details how the response factory object is used by the
+/// @ref HttpListener.
+///
+/// This class always returns the same instance of the
+/// @ref CtrlAgentResponseCreator which @ref HttpListener and
+/// @ref HttpConnection classes use to generate HTTP response messages
+/// which comply with the formats required by the Control Agent.
+class CtrlAgentResponseCreatorFactory : public http::HttpResponseCreatorFactory {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Creates sole instance of the @ref CtrlAgentResponseCreator object
+ /// returned by the @ref CtrlAgentResponseCreatorFactory::create.
+ CtrlAgentResponseCreatorFactory()
+ : sole_creator_(new CtrlAgentResponseCreator()) {
+ }
+
+ /// @brief Returns an instance of the @ref CtrlAgentResponseCreator which
+ /// is used by HTTP server to generate responses to commands.
+ ///
+ /// @return Pointer to the @ref CtrlAgentResponseCreator object.
+ virtual http::HttpResponseCreatorPtr create() const {
+ return (sole_creator_);
+ }
+
+private:
+
+ /// @brief Instance of the @ref CtrlAgentResponseCreator returned.
+ http::HttpResponseCreatorPtr sole_creator_;
+
+};
+
+} // end of namespace isc::agent
+} // end of namespace isc
+
+#endif
ctrl_agent_unittest_SOURCES = ctrl_agent_cfg_mgr_unittest.cc
ctrl_agent_unittest_SOURCES += ctrl_agent_controller_unittest.cc
+ctrl_agent_unittests_SOURCES += ctrl_agent_command_mgr_unittests.cc
ctrl_agent_unittest_SOURCES += parser_unittest.cc
ctrl_agent_unittest_SOURCES += ctrl_agent_process_unittest.cc
+ctrl_agent_unittests_SOURCES += ctrl_agent_response_creator_factory_unittests.cc
ctrl_agent_unittest_SOURCES += ctrl_agent_unittest.cc
+ctrl_agent_unittests_SOURCES += ctrl_agent_response_creator_unittests.cc
ctrl_agent_unittest_CPPFLAGS = $(AM_CPPFLAGS) $(GTEST_INCLUDES)
ctrl_agent_unittest_LDFLAGS = $(AM_LDFLAGS) $(CRYPTO_LDFLAGS) $(GTEST_LDFLAGS)
ctrl_agent_unittest_LDADD += $(top_builddir)/src/lib/stats/libkea-stats.la
ctrl_agent_unittest_LDADD += $(top_builddir)/src/lib/config/libkea-cfgclient.la
ctrl_agent_unittest_LDADD += $(top_builddir)/src/lib/dhcp/libkea-dhcp++.la
+ctrl_agent_unittests_LDADD += $(top_builddir)/src/lib/http/libkea-http.la
ctrl_agent_unittest_LDADD += $(top_builddir)/src/lib/asiolink/libkea-asiolink.la
ctrl_agent_unittest_LDADD += $(top_builddir)/src/lib/cc/libkea-cc.la
ctrl_agent_unittest_LDADD += $(top_builddir)/src/lib/dns/libkea-dns++.la
--- /dev/null
+// Copyright (C) 2017 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 <agent/ctrl_agent_command_mgr.h>
+#include <gtest/gtest.h>
+
+using namespace isc::agent;
+
+namespace {
+
+/// @brief Test fixture class for @ref CtrlAgentCommandMgr.
+///
+/// @todo Add tests for various commands, including the cases when the
+/// commands are forwarded to other servers via unix sockets.
+/// Meanwhile, this is just a placeholder for the tests.
+class CtrlAgentCommandMgrTest : public ::testing::Test {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Deregisters all commands except 'list-commands'.
+ CtrlAgentCommandMgrTest() {
+ CtrlAgentCommandMgr::instance().deregisterAll();
+ }
+
+ /// @brief Destructor.
+ ///
+ /// Deregisters all commands except 'list-commands'.
+ virtual ~CtrlAgentCommandMgrTest() {
+ CtrlAgentCommandMgr::instance().deregisterAll();
+ }
+};
+
+}
--- /dev/null
+// Copyright (C) 2017 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 <agent/ctrl_agent_response_creator.h>
+#include <agent/ctrl_agent_response_creator_factory.h>
+#include <boost/pointer_cast.hpp>
+#include <gtest/gtest.h>
+
+using namespace isc::agent;
+
+namespace {
+
+// This test verifies that create() method always returns the same
+// instance of the CtrlAgentResponseCreator object.
+TEST(CtrlAgentResponseCreatorFactory, create) {
+ CtrlAgentResponseCreatorFactory factory;
+
+ // Invoke twice.
+ CtrlAgentResponseCreatorPtr response1;
+ CtrlAgentResponseCreatorPtr response2;
+ ASSERT_NO_THROW(response1 = boost::dynamic_pointer_cast<
+ CtrlAgentResponseCreator>(factory.create()));
+ ASSERT_NO_THROW(response2 = boost::dynamic_pointer_cast<
+ CtrlAgentResponseCreator>(factory.create()));
+
+ // It must always return non-null object.
+ ASSERT_TRUE(response1);
+ ASSERT_TRUE(response2);
+
+ // And it must always return the same object.
+ EXPECT_TRUE(response1 == response2);
+
+}
+
+}
--- /dev/null
+// Copyright (C) 2017 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 <agent/ctrl_agent_command_mgr.h>
+#include <agent/ctrl_agent_response_creator.h>
+#include <cc/command_interpreter.h>
+#include <http/post_request.h>
+#include <http/post_request_json.h>
+#include <http/response_json.h>
+#include <gtest/gtest.h>
+#include <boost/bind.hpp>
+#include <boost/pointer_cast.hpp>
+
+using namespace isc::agent;
+using namespace isc::config;
+using namespace isc::data;
+using namespace isc::http;
+
+namespace {
+
+/// @brief Test fixture class for @ref CtrlAgentResponseCreator.
+class CtrlAgentResponseCreatorTest : public ::testing::Test {
+public:
+
+ /// @brief Constructor.
+ ///
+ /// Creates instance of the response creator and uses this instance to
+ /// create "empty" request. It also removes registered commands from the
+ /// command manager.
+ CtrlAgentResponseCreatorTest()
+ : response_creator_(),
+ request_(response_creator_.createNewHttpRequest()) {
+ // Deregisters commands.
+ CtrlAgentCommandMgr::instance().deregisterAll();
+ CtrlAgentCommandMgr::instance().
+ registerCommand("foo", boost::bind(&CtrlAgentResponseCreatorTest::
+ fooCommandHandler,
+ this, _1, _2));
+
+ // Make sure that the request has been initialized properly.
+ if (!request_) {
+ ADD_FAILURE() << "CtrlAgentResponseCreator::createNewHttpRequest"
+ " returns NULL!";
+ }
+ }
+
+ /// @brief Destructor.
+ ///
+ /// Removes registered commands from the command manager.
+ virtual ~CtrlAgentResponseCreatorTest() {
+ CtrlAgentCommandMgr::instance().deregisterAll();
+ }
+
+ /// @brief Fills request context with required data to create new request.
+ ///
+ /// @param request Request which context should be configured.
+ void setBasicContext(const HttpRequestPtr& request) {
+ request->context()->method_ = "POST";
+ request->context()->http_version_major_ = 1;
+ request->context()->http_version_minor_ = 1;
+ request->context()->uri_ = "/foo";
+
+ // Content-Type
+ HttpHeaderContext content_type;
+ content_type.name_ = "Content-Type";
+ content_type.value_ = "application/json";
+ request->context()->headers_.push_back(content_type);
+
+ // Content-Length
+ HttpHeaderContext content_length;
+ content_length.name_ = "Content-Length";
+ content_length.value_ = "0";
+ request->context()->headers_.push_back(content_length);
+ }
+
+ /// @brief Test creation of stock response.
+ ///
+ /// @param status_code Status code to be included in the response.
+ /// @param must_contain Text that must be present in the textual
+ /// representation of the generated response.
+ void testStockResponse(const HttpStatusCode& status_code,
+ const std::string& must_contain) {
+ HttpResponsePtr response;
+ ASSERT_NO_THROW(
+ response = response_creator_.createStockHttpResponse(request_,
+ status_code)
+ );
+ ASSERT_TRUE(response);
+ HttpResponseJsonPtr response_json = boost::dynamic_pointer_cast<
+ HttpResponseJson>(response);
+ ASSERT_TRUE(response_json);
+ // Make sure the response contains the string specified as argument.
+ EXPECT_TRUE(response_json->toString().find(must_contain) != std::string::npos);
+
+ }
+
+ /// @brief Handler for the 'foo' test command.
+ ///
+ /// @param command_name Command name, i.e. 'foo'.
+ /// @param command_arguments Command arguments (empty).
+ ///
+ /// @return Returns response with a single string "bar".
+ ConstElementPtr fooCommandHandler(const std::string& command_name,
+ const ConstElementPtr& command_arguments) {
+ ElementPtr arguments = Element::createList();
+ arguments->add(Element::create("bar"));
+ return (createAnswer(CONTROL_RESULT_SUCCESS, arguments));
+ }
+
+ /// @brief Instance of the response crator.
+ CtrlAgentResponseCreator response_creator_;
+
+ /// @brief Instance of the "empty" request.
+ ///
+ /// The context belonging to this request may be modified by the unit
+ /// tests to verify various scenarios of response creation.
+ HttpRequestPtr request_;
+
+};
+
+// This test verifies that the created "empty" reuqest has valid type.
+TEST_F(CtrlAgentResponseCreatorTest, createNewHttpRequest) {
+ // The request must be of PostHttpRequestJson type.
+ PostHttpRequestJsonPtr request_json = boost::dynamic_pointer_cast<
+ PostHttpRequestJson>(request_);
+ ASSERT_TRUE(request_json);
+}
+
+// Test that HTTP version of stock response is set to 1.0 if the request
+// context doesn't specify any version.
+TEST_F(CtrlAgentResponseCreatorTest, createStockHttpResponseNoVersion) {
+ testStockResponse(HttpStatusCode::BAD_REQUEST, "HTTP/1.0 400 Bad Request");
+}
+
+// Test that HTTP version of stock response is set to 1.0 if the request
+// version is higher than 1.1.
+TEST_F(CtrlAgentResponseCreatorTest, createStockHttpResponseHighVersion) {
+ request_->context()->http_version_major_ = 2;
+ testStockResponse(HttpStatusCode::REQUEST_TIMEOUT,
+ "HTTP/1.0 408 Request Timeout");
+}
+
+// Test that the server responsds with version 1.1 if request version is 1.1.
+TEST_F(CtrlAgentResponseCreatorTest, createStockHttpResponseCorrectVersion) {
+ request_->context()->http_version_major_ = 1;
+ request_->context()->http_version_minor_ = 1;
+ testStockResponse(HttpStatusCode::NO_CONTENT, "HTTP/1.1 204 No Content");
+}
+
+// Test successful server response when the client specifies valid command.
+TEST_F(CtrlAgentResponseCreatorTest, createDynamicHttpResponse) {
+ setBasicContext(request_);
+
+ // Body: "foo" command has been registered in the test fixture constructor.
+ request_->context()->body_ = "{ \"command\": \"foo\" }";
+
+ // All requests must be finalized before they can be processed.
+ ASSERT_NO_THROW(request_->finalize());
+
+ // Create response from the request.
+ HttpResponsePtr response;
+ ASSERT_NO_THROW(response = response_creator_.createHttpResponse(request_));
+ ASSERT_TRUE(response);
+
+ // Response must be convertible to HttpResponseJsonPtr.
+ HttpResponseJsonPtr response_json = boost::dynamic_pointer_cast<
+ HttpResponseJson>(response);
+ ASSERT_TRUE(response_json);
+
+ // Response must be successful.
+ EXPECT_TRUE(response_json->toString().find("HTTP/1.1 200 OK") !=
+ std::string::npos);
+ // Response must contain JSON body with "result" of 0.
+ EXPECT_TRUE(response_json->toString().find("\"result\": 0") !=
+ std::string::npos);
+}
+
+// This test verifies that Internal Server Error is returned when invalid C++
+// request type is used. This is considered an error in the server logic.
+TEST_F(CtrlAgentResponseCreatorTest, createDynamicHttpResponseInvalidType) {
+ PostHttpRequestPtr request(new PostHttpRequest());
+ setBasicContext(request);
+
+ // Body: "list-commands" is natively supported by the command manager.
+ request->context()->body_ = "{ \"command\": \"list-commands\" }";
+
+ // All requests must be finalized before they can be processed.
+ ASSERT_NO_THROW(request->finalize());
+
+ HttpResponsePtr response;
+ ASSERT_NO_THROW(response = response_creator_.createHttpResponse(request));
+ ASSERT_TRUE(response);
+
+ // Response must be convertible to HttpResponseJsonPtr.
+ HttpResponseJsonPtr response_json = boost::dynamic_pointer_cast<
+ HttpResponseJson>(response);
+ ASSERT_TRUE(response_json);
+
+ // Response must contain Internal Server Error status code.
+ EXPECT_TRUE(response_json->toString().find("HTTP/1.1 500 Internal Server Error") !=
+ std::string::npos);
+
+}
+
+}
return (callout_handle_);
}
-private:
-
/// @brief Handles the command having a given name and arguments.
///
/// This method checks if the hook library is installed which implements
/// callouts for the 'control_command_receive' hook point, and calls them
/// if they exist. If the hook library supports the given command it creates
/// a response and returns it in the 'response' argument of the
- /// @ref ConstElementPtr type. If the callout also sets the 'skip' status,
- /// the response created by the callout is returned. Otherwise, the
+ /// @ref isc::data::ConstElementPtr type. If the callout also sets the 'skip'
+ /// status, the response created by the callout is returned. Otherwise, the
/// @ref BaseCommandMgr::handleCommand is called.
///
- /// This method is private because it is its final implementation which
- /// should not be overridden in the derived classes.
- ///
/// @param cmd_name Command name.
/// @param params Command arguments.
///
#include <http/connection.h>
#include <http/connection_pool.h>
#include <boost/bind.hpp>
-#include <iostream>
using namespace isc::asiolink;
socket_.asyncSend(output_buf_.data(),
output_buf_.length(),
socket_write_callback_);
+ } else {
+ stopThisConnection();
}
} catch (const std::exception& ex) {
stopThisConnection();
} else {
output_buf_.clear();
+ stopThisConnection();
}
}
#define HTTP_POST_REQUEST_H
#include <http/request.h>
+#include <boost/shared_ptr.hpp>
namespace isc {
namespace http {
+class PostHttpRequest;
+
+/// @brief Pointer to @ref PostHttpRequest.
+typedef boost::shared_ptr<PostHttpRequest> PostHttpRequestPtr;
+/// @brief Pointer to const @ref PostHttpRequest.
+typedef boost::shared_ptr<const PostHttpRequest> ConstPostHttpRequestPtr;
+
/// @brief Represents HTTP POST request.
///
/// Instructs the parent class to require:
PostHttpRequest();
};
+
} // namespace http
} // namespace isc
-// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2016-2017 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
}
ConstElementPtr
-PostHttpRequestJson::getBodyAsJson() {
+PostHttpRequestJson::getBodyAsJson() const {
checkFinalized();
return (json_);
}
ConstElementPtr
-PostHttpRequestJson::getJsonElement(const std::string& element_name) {
+PostHttpRequestJson::getJsonElement(const std::string& element_name) const {
try {
ConstElementPtr body = getBodyAsJson();
if (body) {
-// Copyright (C) 2016 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2016-2017 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
#include <cc/data.h>
#include <exceptions/exceptions.h>
#include <http/post_request.h>
+#include <boost/shared_ptr.hpp>
#include <string>
namespace isc {
HttpRequestError(file, line, what) { };
};
+class PostHttpRequestJson;
+
+/// @brief Pointer to @ref PostHttpRequestJson.
+typedef boost::shared_ptr<PostHttpRequestJson> PostHttpRequestJsonPtr;
+/// @brief Pointer to const @ref PostHttpRequestJson.
+typedef boost::shared_ptr<const PostHttpRequestJson> ConstPostHttpRequestJsonPtr;
+
/// @brief Represents HTTP POST request with JSON body.
///
/// In addition to the requirements specified by the @ref PostHttpRequest
///
/// @return Pointer to the root element of the JSON structure.
/// @throw HttpRequestJsonError if an error occurred.
- data::ConstElementPtr getBodyAsJson();
+ data::ConstElementPtr getBodyAsJson() const;
/// @brief Retrieves a single JSON element.
///
/// @return Pointer to the specified element or NULL if such element
/// doesn't exist.
/// @throw HttpRequestJsonError if an error occurred.
- data::ConstElementPtr getJsonElement(const std::string& element_name);
+ data::ConstElementPtr getJsonElement(const std::string& element_name) const;
protected:
// Update or add "Date" header.
addHeaderInternal("Date", getDateHeaderValue(), headers);
- // Add "Content-Length" if body present.
- if (!body_.empty()) {
- addHeaderInternal("Content-Length", body_.length(), headers);
- }
+ // Always add "Content-Length", perhaps equal to 0.
+ addHeaderInternal("Content-Length", body_.length(), headers);
// Include all headers.
for (auto header = headers.cbegin(); header != headers.cend();
HttpClientPtr client = *clients_.begin();
ASSERT_TRUE(client);
EXPECT_EQ("HTTP/1.1 200 OK\r\n"
+ "Content-Length: 0\r\n"
"Content-Type: application/json\r\n"
"Date: Tue, 19 Dec 2016 18:53:35 GMT\r\n"
"\r\n",
private:
- /// @brief Creates HTTP response..
+ /// @brief Creates HTTP response.
///
/// @param request Pointer to the HTTP request.
/// @return Pointer to the generated HTTP response.
ASSERT_TRUE(response);
EXPECT_EQ("HTTP/1.0 200 OK\r\n"
+ "Content-Length: 0\r\n"
"Content-Type: application/json\r\n"
"Date: Tue, 19 Dec 2016 18:53:35 GMT\r\n\r\n",
response->toString());
HttpResponse::isServerError(status_code)) {
response_string << "Content-Length: " << status_message_json.str().size()
<< "\r\n";
+ } else {
+ response_string << "Content-Length: 0\r\n";
}
// Content-Type and Date are automatically included.
std::ostringstream response_string;
response_string << "HTTP/1.0 " << static_cast<uint16_t>(status_code)
<< " " << status_message << "\r\n"
+ << "Content-Length: 0\r\n"
<< "Content-Type: text/html\r\n"
<< "Date: " << response.getDateHeaderValue() << "\r\n\r\n";