Browse Source

[5099] Implemented HttpListener and other classes.

- HttpConnection
- HttpConnectionPool
Marcin Siodelski 8 years ago
parent
commit
dba214ecf3

+ 9 - 13
src/lib/asiolink/tcp_socket.h

@@ -1,4 +1,4 @@
-// Copyright (C) 2011-2016 Internet Systems Consortium, Inc. ("ISC")
+// Copyright (C) 2011-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
@@ -166,7 +166,6 @@ private:
     // construction, or where it is asked to manage its own socket.
     boost::asio::ip::tcp::socket*      socket_ptr_;    ///< Pointer to own socket
     boost::asio::ip::tcp::socket&      socket_;        ///< Socket
-    bool                               isopen_;        ///< true when socket is open
 
     // TODO: Remove temporary buffer
     // The current implementation copies the buffer passed to asyncSend() into
@@ -188,7 +187,7 @@ private:
 
 template <typename C>
 TCPSocket<C>::TCPSocket(boost::asio::ip::tcp::socket& socket) :
-    socket_ptr_(NULL), socket_(socket), isopen_(true), send_buffer_()
+    socket_ptr_(NULL), socket_(socket), send_buffer_()
 {
 }
 
@@ -197,7 +196,7 @@ TCPSocket<C>::TCPSocket(boost::asio::ip::tcp::socket& socket) :
 template <typename C>
 TCPSocket<C>::TCPSocket(IOService& service) :
     socket_ptr_(new boost::asio::ip::tcp::socket(service.get_io_service())),
-    socket_(*socket_ptr_), isopen_(false)
+    socket_(*socket_ptr_)
 {
 }
 
@@ -217,14 +216,13 @@ TCPSocket<C>::open(const IOEndpoint* endpoint, C& callback) {
     // Ignore opens on already-open socket.  Don't throw a failure because
     // of uncertainties as to what precedes whan when using asynchronous I/O.
     // At also allows us a treat a passed-in socket as a self-managed socket.
-    if (!isopen_) {
+    if (!socket_.is_open()) {
         if (endpoint->getFamily() == AF_INET) {
             socket_.open(boost::asio::ip::tcp::v4());
         }
         else {
             socket_.open(boost::asio::ip::tcp::v6());
         }
-        isopen_ = true;
 
         // Set options on the socket:
 
@@ -254,7 +252,7 @@ template <typename C> void
 TCPSocket<C>::asyncSend(const void* data, size_t length,
     const IOEndpoint*, C& callback)
 {
-    if (isopen_) {
+    if (socket_.is_open()) {
 
         // Need to copy the data into a temporary buffer and precede it with
         // a two-byte count field.
@@ -264,8 +262,7 @@ TCPSocket<C>::asyncSend(const void* data, size_t length,
             uint16_t count = boost::numeric_cast<uint16_t>(length);
 
             // Copy data into a buffer preceded by the count field.
-            send_buffer_.reset(new isc::util::OutputBuffer(length + 2));
-            send_buffer_->writeUint16(count);
+            send_buffer_.reset(new isc::util::OutputBuffer(length));
             send_buffer_->writeData(data, length);
 
             // ... and send it
@@ -289,7 +286,7 @@ template <typename C> void
 TCPSocket<C>::asyncReceive(void* data, size_t length, size_t offset,
     IOEndpoint* endpoint, C& callback)
 {
-    if (isopen_) {
+    if (socket_.is_open()) {
         // Upconvert to a TCPEndpoint.  We need to do this because although
         // IOEndpoint is the base class of UDPEndpoint and TCPEndpoint, it
         // does not contain a method for getting at the underlying endpoint
@@ -391,7 +388,7 @@ TCPSocket<C>::processReceivedData(const void* staging, size_t length,
 
 template <typename C> void
 TCPSocket<C>::cancel() {
-    if (isopen_) {
+    if (socket_.is_open()) {
         socket_.cancel();
     }
 }
@@ -401,9 +398,8 @@ TCPSocket<C>::cancel() {
 
 template <typename C> void
 TCPSocket<C>::close() {
-    if (isopen_ && socket_ptr_) {
+    if (socket_.is_open() && socket_ptr_) {
         socket_.close();
-        isopen_ = false;
     }
 }
 

+ 6 - 1
src/lib/http/Makefile.am

@@ -22,10 +22,14 @@ EXTRA_DIST = http_messages.mes
 CLEANFILES = *.gcno *.gcda http_messages.h http_messages.cc s-messages
 
 lib_LTLIBRARIES = libkea-http.la
-libkea_http_la_SOURCES  = date_time.cc date_time.h
+libkea_http_la_SOURCES  = connection.cc connection.h
+libkea_http_la_SOURCES += connection_pool.cc connection_pool.h
+libkea_http_la_SOURCES += date_time.cc date_time.h
 libkea_http_la_SOURCES += http_log.cc http_log.h
 libkea_http_la_SOURCES += header_context.h
+libkea_http_la_SOURCES += http_acceptor.h
 libkea_http_la_SOURCES += http_types.h
+libkea_http_la_SOURCES += listener.cc listener.h
 libkea_http_la_SOURCES += post_request.cc post_request.h
 libkea_http_la_SOURCES += post_request_json.cc post_request_json.h
 libkea_http_la_SOURCES += request.cc request.h
@@ -33,6 +37,7 @@ libkea_http_la_SOURCES += request_context.h
 libkea_http_la_SOURCES += request_parser.cc request_parser.h
 libkea_http_la_SOURCES += response.cc response.h
 libkea_http_la_SOURCES += response_creator.cc response_creator.h
+libkea_http_la_SOURCES += response_creator_factory.h
 libkea_http_la_SOURCES += response_json.cc response_json.h
 
 nodist_libkea_http_la_SOURCES = http_messages.cc http_messages.h

+ 125 - 0
src/lib/http/connection.cc

@@ -0,0 +1,125 @@
+// 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 <asiolink/asio_wrapper.h>
+#include <http/connection.h>
+#include <http/connection_pool.h>
+#include <boost/bind.hpp>
+#include <iostream>
+
+using namespace isc::asiolink;
+
+namespace isc {
+namespace http {
+
+void
+HttpConnection::
+SocketCallback::operator()(boost::system::error_code ec, size_t length) {
+    callback_(ec, length);
+}
+
+HttpConnection:: HttpConnection(asiolink::IOService& io_service,
+                                HttpAcceptor& acceptor,
+                                HttpConnectionPool& connection_pool,
+                                const HttpResponseCreatorPtr& response_creator,
+                                const HttpAcceptorCallback& callback)
+    : socket_(io_service),
+      socket_callback_(boost::bind(&HttpConnection::socketReadCallback, this,
+                                   _1, _2)),
+      socket_write_callback_(boost::bind(&HttpConnection::socketWriteCallback,
+                                         this, _1, _2)),
+      acceptor_(acceptor),
+      connection_pool_(connection_pool),
+      response_creator_(response_creator),
+      request_(response_creator_->createNewHttpRequest()),
+      parser_(new HttpRequestParser(*request_)),
+      acceptor_callback_(callback),
+      buf_() {
+    parser_->initModel();
+}
+
+HttpConnection::~HttpConnection() {
+    close();
+}
+
+void
+HttpConnection::asyncAccept() {
+    HttpAcceptorCallback cb = boost::bind(&HttpConnection::acceptorCallback,
+                                          this, _1);
+    acceptor_.asyncAccept(socket_, cb);
+}
+
+void
+HttpConnection::close() {
+    socket_.close();
+}
+
+void
+HttpConnection::doRead() {
+    TCPEndpoint endpoint;
+    socket_.asyncReceive(static_cast<void*>(buf_.data()), buf_.size(), 0, &endpoint,
+                         socket_callback_);
+}
+
+void
+HttpConnection::doWrite() {
+    if (!output_buf_.empty()) {
+        TCPEndpoint endpoint;
+        socket_.asyncSend(output_buf_.data(),
+                          output_buf_.length(), &endpoint,
+                          socket_write_callback_);
+    }
+}
+
+void
+HttpConnection::acceptorCallback(const boost::system::error_code& ec) {
+    if (!acceptor_.isOpen()) {
+        return;
+    }
+
+    if (ec) {
+        connection_pool_.stop(shared_from_this());
+    }
+
+    acceptor_callback_(ec);
+
+    if (!ec) {
+        doRead();
+    }
+
+}
+
+void
+HttpConnection::socketReadCallback(boost::system::error_code ec, size_t length) {
+    std::string s(&buf_[0], buf_[0] + length);
+    parser_->postBuffer(static_cast<void*>(buf_.data()), length);
+    parser_->poll();
+    if (parser_->needData()) {
+        doRead();
+
+    } else {
+        request_->finalize();
+        HttpResponsePtr response = response_creator_->createHttpResponse(request_);
+        output_buf_ = response->toString();
+        doWrite();
+    }
+}
+
+void
+HttpConnection::socketWriteCallback(boost::system::error_code ec,
+                                    size_t length) {
+    if (length <= output_buf_.size()) {
+        output_buf_.erase(0, length);
+        doWrite();
+
+    } else {
+        output_buf_.clear();
+    }
+}
+
+} // end of namespace isc::http
+} // end of namespace isc
+

+ 102 - 0
src/lib/http/connection.h

@@ -0,0 +1,102 @@
+// 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 HTTP_CONNECTION_H
+#define HTTP_CONNECTION_H
+
+#include <asiolink/io_service.h>
+#include <http/http_acceptor.h>
+#include <http/request_parser.h>
+#include <http/response_creator_factory.h>
+#include <boost/function.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/system/error_code.hpp>
+#include <boost/shared_ptr.hpp>
+#include <array>
+
+namespace isc {
+namespace http {
+
+class HttpConnectionPool;
+
+class HttpConnection;
+typedef boost::shared_ptr<HttpConnection> HttpConnectionPtr;
+
+class HttpConnection : public boost::enable_shared_from_this<HttpConnection> {
+private:
+
+    typedef boost::function<void(boost::system::error_code ec, size_t length)>
+    SocketCallbackFunction;
+
+    class SocketCallback {
+    public:
+
+        SocketCallback(SocketCallbackFunction socket_callback)
+            : callback_(socket_callback) {
+        }
+
+        void operator()(boost::system::error_code ec, size_t length = 0);
+
+    private:
+        SocketCallbackFunction callback_;
+    };
+
+
+public:
+
+    HttpConnection(asiolink::IOService& io_service,
+                   HttpAcceptor& acceptor,
+                   HttpConnectionPool& connection_pool,
+                   const HttpResponseCreatorPtr& response_creator,
+                   const HttpAcceptorCallback& callback);
+
+    ~HttpConnection();
+
+    void asyncAccept();
+
+    void close();
+
+    void doRead();
+
+    void doWrite();
+
+    void acceptorCallback(const boost::system::error_code& ec);
+
+    void socketReadCallback(boost::system::error_code ec,
+                            size_t length);
+
+    void socketWriteCallback(boost::system::error_code ec,
+                             size_t length);
+
+private:
+
+    asiolink::TCPSocket<SocketCallback> socket_;
+
+    SocketCallback socket_callback_;
+
+    SocketCallback socket_write_callback_;
+
+    HttpAcceptor& acceptor_;
+
+    HttpConnectionPool& connection_pool_;
+
+    HttpResponseCreatorPtr response_creator_;
+
+    HttpRequestPtr request_;
+
+    HttpRequestParserPtr parser_;
+
+    HttpAcceptorCallback acceptor_callback_;
+
+    std::array<char, 4096> buf_;
+
+    std::string output_buf_;
+};
+
+}
+}
+
+#endif

+ 27 - 0
src/lib/http/connection_pool.cc

@@ -0,0 +1,27 @@
+// 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 <asiolink/asio_wrapper.h>
+#include <http/connection_pool.h>
+
+namespace isc {
+namespace http {
+
+void
+HttpConnectionPool::start(const HttpConnectionPtr& connection) {
+    connections_.insert(connection);
+    connection->asyncAccept();
+}
+
+void
+HttpConnectionPool::stop(const HttpConnectionPtr& connection) {
+    connections_.erase(connection);
+    connection->close();
+}
+
+
+}
+}

+ 33 - 0
src/lib/http/connection_pool.h

@@ -0,0 +1,33 @@
+// 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 HTTP_CONNECTION_POOL_H
+#define HTTP_CONNECTION_POOL_H
+
+#include <http/connection.h>
+#include <set>
+
+namespace isc {
+namespace http {
+
+class HttpConnectionPool {
+public:
+
+    void start(const HttpConnectionPtr& connection);
+
+    void stop(const HttpConnectionPtr& connection);
+
+private:
+
+    std::set<HttpConnectionPtr> connections_;
+
+};
+
+}
+}
+
+#endif
+

+ 27 - 0
src/lib/http/http_acceptor.h

@@ -0,0 +1,27 @@
+// 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 HTTP_ACCEPTOR_H
+#define HTTP_ACCEPTOR_H
+
+#include <asiolink/tcp_acceptor.h>
+#include <boost/function.hpp>
+#include <boost/system/system_error.hpp>
+
+namespace isc {
+namespace http {
+
+/// @brief Type of the callback for the TCP acceptor used in this library.
+typedef boost::function<void(const boost::system::error_code&)>
+HttpAcceptorCallback;
+
+/// @brief Type of the TCP acceptor used in this library.
+typedef asiolink::TCPAcceptor<HttpAcceptorCallback> HttpAcceptor;
+
+} // end of namespace isc::http
+} // end of namespace isc
+
+#endif

+ 6 - 0
src/lib/http/http_types.h

@@ -7,6 +7,9 @@
 #ifndef HTTP_TYPES_H
 #define HTTP_TYPES_H
 
+namespace isc {
+namespace http {
+
 /// @brief HTTP protocol version.
 struct HttpVersion {
     unsigned major_; ///< Major HTTP version.
@@ -43,4 +46,7 @@ struct HttpVersion {
     }
 };
 
+} // end of namespace isc::http
+} // end of namespace isc
+
 #endif

+ 54 - 0
src/lib/http/listener.cc

@@ -0,0 +1,54 @@
+// 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 <asiolink/asio_wrapper.h>
+#include <http/listener.h>
+
+using namespace isc::asiolink;
+
+namespace isc {
+namespace http {
+
+HttpListener::HttpListener(IOService& io_service,
+                           const asiolink::IOAddress& server_address,
+                           const unsigned short server_port,
+                           const HttpResponseCreatorFactoryPtr& creator_factory)
+    : io_service_(io_service), acceptor_(io_service),
+      endpoint_(server_address, server_port),
+      creator_factory_(creator_factory) {
+}
+
+void
+HttpListener::start() {
+    acceptor_.open(endpoint_);
+    acceptor_.bind(endpoint_);
+    acceptor_.listen();
+
+    accept();
+}
+
+void
+HttpListener::accept() {
+    HttpResponseCreatorPtr response_creator = creator_factory_->create();
+    HttpAcceptorCallback acceptor_callback =
+        boost::bind(&HttpListener::acceptHandler, this, _1);
+    HttpConnectionPtr conn(new HttpConnection(io_service_, acceptor_,
+                                              connections_,
+                                              response_creator,
+                                              acceptor_callback));
+    connections_.start(conn);
+}
+
+void
+HttpListener::acceptHandler(const boost::system::error_code& ec) {
+    if (!ec) {
+        accept();
+    }
+}
+
+
+} // end of namespace isc::http
+} // end of namespace isc

+ 48 - 0
src/lib/http/listener.h

@@ -0,0 +1,48 @@
+// 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 HTTP_LISTENER_H
+#define HTTP_LISTENER_H
+
+#include <asiolink/io_address.h>
+#include <asiolink/io_service.h>
+#include <asiolink/tcp_endpoint.h>
+#include <http/connection.h>
+#include <http/connection_pool.h>
+#include <http/http_acceptor.h>
+#include <http/response_creator_factory.h>
+
+namespace isc {
+namespace http {
+
+class HttpListener {
+public:
+
+    HttpListener(asiolink::IOService& io_service,
+                 const asiolink::IOAddress& server_address,
+                 const unsigned short server_port,
+                 const HttpResponseCreatorFactoryPtr& creator_factory);
+
+    void start();
+
+private:
+
+    void accept();
+
+    void acceptHandler(const boost::system::error_code& ec);
+
+    asiolink::IOService& io_service_;
+    HttpAcceptor acceptor_;
+    asiolink::TCPEndpoint endpoint_;
+    HttpConnectionPool connections_;
+    HttpResponseCreatorFactoryPtr creator_factory_;
+
+};
+
+} // end of namespace isc::http
+} // end of namespace isc
+
+#endif

+ 7 - 1
src/lib/http/request_parser.h

@@ -1,4 +1,4 @@
-// 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
@@ -11,6 +11,7 @@
 #include <http/request.h>
 #include <util/state_model.h>
 #include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
 #include <list>
 #include <stdint.h>
 #include <string>
@@ -28,6 +29,11 @@ public:
         isc::Exception(file, line, what) { };
 };
 
+class HttpRequestParser;
+
+/// @brief Pointer to the @ref HttpRequestParser.
+typedef boost::shared_ptr<HttpRequestParser> HttpRequestParserPtr;
+
 /// @brief A generic parser for HTTP requests.
 ///
 /// This class implements a parser for HTTP requests. The parser derives from

+ 17 - 0
src/lib/http/response_creator.h

@@ -9,10 +9,16 @@
 
 #include <http/request.h>
 #include <http/response.h>
+#include <boost/shared_ptr.hpp>
 
 namespace isc {
 namespace http {
 
+class HttpResponseCreator;
+
+/// @brief Pointer to the @ref HttpResponseCreator object.
+typedef boost::shared_ptr<HttpResponseCreator> HttpResponseCreatorPtr;
+
 /// @brief Specifies an interface for classes creating HTTP responses
 /// from HTTP requests.
 ///
@@ -70,6 +76,17 @@ public:
     virtual HttpResponsePtr
     createHttpResponse(const ConstHttpRequestPtr& request) final;
 
+    /// @brief Create a new request.
+    ///
+    /// This method creates an instance of the @ref HttpRequest or derived
+    /// class. The type of the object is compatible with the instance of
+    /// the @ref HttpResponseCreator implementation which creates it, i.e.
+    /// can be used as an argument in the call to @ref createHttpResponse.
+    ///
+    /// @return Pointer to the new instance of the @ref HttpRequest.
+    virtual HttpRequestPtr
+    createNewHttpRequest() const = 0;
+
 protected:
 
     /// @brief Creates implementation specific HTTP 400 response.

+ 33 - 0
src/lib/http/response_creator_factory.h

@@ -0,0 +1,33 @@
+// 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 HTTP_RESPONSE_CREATOR_FACTORY_H
+#define HTTP_RESPONSE_CREATOR_FACTORY_H
+
+#include <http/request.h>
+#include <http/response.h>
+#include <http/response_creator.h>
+#include <boost/shared_ptr.hpp>
+
+namespace isc {
+namespace http {
+
+class HttpResponseCreatorFactory {
+public:
+
+    virtual ~HttpResponseCreatorFactory() { }
+
+    virtual HttpResponseCreatorPtr create() const = 0;
+
+};
+
+typedef boost::shared_ptr<HttpResponseCreatorFactory>
+HttpResponseCreatorFactoryPtr;
+
+} // end of namespace isc::http
+} // end of namespace isc
+
+#endif

+ 1 - 0
src/lib/http/tests/Makefile.am

@@ -21,6 +21,7 @@ if HAVE_GTEST
 TESTS += libhttp_unittests
 
 libhttp_unittests_SOURCES  = date_time_unittests.cc
+libhttp_unittests_SOURCES += listener_unittests.cc
 libhttp_unittests_SOURCES += post_request_json_unittests.cc
 libhttp_unittests_SOURCES += request_parser_unittests.cc
 libhttp_unittests_SOURCES += request_test.h

+ 268 - 0
src/lib/http/tests/listener_unittests.cc

@@ -0,0 +1,268 @@
+// 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 <asiolink/asio_wrapper.h>
+#include <asiolink/interval_timer.h>
+#include <http/listener.h>
+#include <http/post_request_json.h>
+#include <http/response_creator.h>
+#include <http/response_creator_factory.h>
+#include <http/response_json.h>
+#include <http/tests/response_test.h>
+#include <boost/asio/buffer.hpp>
+#include <boost/bind.hpp>
+#include <gtest/gtest.h>
+#include <list>
+#include <string>
+
+using namespace isc::asiolink;
+using namespace isc::http;
+using namespace isc::http::test;
+
+namespace {
+
+const std::string SERVER_ADDRESS = "127.0.0.1";
+const unsigned short SERVER_PORT = 18123;
+
+/// @brief Test timeout in ms.
+const long TEST_TIMEOUT = 10000;
+
+/// @brief Test HTTP response.
+typedef TestHttpResponseBase<HttpResponseJson> Response;
+
+/// @brief Pointer to test HTTP response.
+typedef boost::shared_ptr<Response> ResponsePtr;
+
+/// @brief Implementation of the @ref HttpResponseCreator.
+class TestHttpResponseCreator : public HttpResponseCreator {
+public:
+
+    /// @brief Create a new request.
+    ///
+    /// @return Pointer to the new instance of the @ref HttpRequest.
+    virtual HttpRequestPtr
+    createNewHttpRequest() const {
+        return (HttpRequestPtr(new PostHttpRequestJson()));
+    }
+
+private:
+
+    /// @brief Creates HTTP 400 response.
+    ///
+    /// @param request Pointer to the HTTP request.
+    /// @return Pointer to the generated HTTP 400 response.
+    virtual HttpResponsePtr
+    createStockBadRequest(const ConstHttpRequestPtr& request) 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_);
+        // This will generate the response holding JSON content.
+        ResponsePtr response(new Response(http_version,
+                                          HttpStatusCode::BAD_REQUEST));
+        return (response);
+    }
+
+    /// @brief Creates HTTP response.
+    ///
+    /// @param request Pointer to the HTTP request.
+    /// @return Pointer to the generated HTTP OK response with no content.
+    virtual HttpResponsePtr
+    createDynamicHttpResponse(const ConstHttpRequestPtr& request) {
+        // The simplest thing is to create a response with no content.
+        // We don't need content to test our class.
+        ResponsePtr response(new Response(request->getHttpVersion(),
+                                          HttpStatusCode::OK));
+        return (response);
+    }
+};
+
+class TestHttpResponseCreatorFactory : public HttpResponseCreatorFactory {
+public:
+
+    virtual HttpResponseCreatorPtr create() const {
+        HttpResponseCreatorPtr response_creator(new TestHttpResponseCreator());
+        return (response_creator);
+    }
+};
+
+/// @brief Entity which can connect to the HTTP server endpoint.
+class HttpClient : public boost::noncopyable {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// This constructor creates new socket instance. It doesn't connect. Call
+    /// connect() to connect to the server.
+    ///
+    /// @param io_service IO service to be stopped on error.
+    explicit HttpClient(IOService& io_service)
+        : io_service_(io_service.get_io_service()), socket_(io_service_),
+          buf_(), response_() {
+    }
+
+    /// @brief Destructor.
+    ///
+    /// Closes the underlying socket if it is open.
+    ~HttpClient() {
+        close();
+    }
+
+    void startRequest() {
+        boost::asio::ip::tcp::endpoint
+            endpoint(boost::asio::ip::address::from_string(SERVER_ADDRESS),
+                     SERVER_PORT);
+        socket_.async_connect(endpoint, [this](const boost::system::error_code& ec) {
+            if (ec) {
+                ADD_FAILURE() << "error occurred while connecting: "
+                              << ec.message();
+                io_service_.stop();
+            }
+            sendRequest();
+        });
+    }
+
+    void sendRequest() {
+        const std::string request = "POST /foo/bar HTTP/1.1\r\n"
+            "Content-Type: application/json\r\n"
+            "Content-Length: 3\r\n\r\n"
+            "{ }";
+        sendPartialRequest(request);
+    }
+
+    void sendPartialRequest(std::string request) {
+        socket_.async_send(boost::asio::buffer(request.data(), request.size()),
+                           [this, request](const boost::system::error_code& ec,
+                                           std::size_t bytes_transferred) mutable {
+            if (ec) {
+                ADD_FAILURE() << "error occurred while connecting: "
+                              << ec.message();
+                io_service_.stop();
+                return;
+            }
+
+            if (bytes_transferred > 0 && (request.size() <= bytes_transferred)) {
+                request.erase(0, bytes_transferred);
+            }
+
+            if (!request.empty()) {
+                sendPartialRequest(request);
+
+            } else {
+                response_.clear();
+                receivePartialResponse();
+            }
+       });
+    }
+
+    void receivePartialResponse() {
+        socket_.async_read_some(boost::asio::buffer(buf_),
+                                [this](const boost::system::error_code& ec,
+                                       std::size_t bytes_transferred) {
+            if (ec) {
+                ADD_FAILURE() << "error occurred while receiving HTTP"
+                    " response from the server: " << ec.message();
+                io_service_.stop();
+            }
+
+            if (bytes_transferred > 0) {
+                response_.insert(response_.end(), buf_.data(),
+                                 buf_.data() + bytes_transferred);
+            }
+
+            if (response_.find("\r\n\r\n", 0) != std::string::npos) {
+                io_service_.stop();
+            }
+
+        });
+    }
+
+    /// @brief Close connection.
+    void close() {
+        socket_.close();
+    }
+
+    std::string getResponse() const {
+        return (response_);
+    }
+
+private:
+
+    /// @brief Holds reference to the IO service.
+    boost::asio::io_service& io_service_;
+
+    /// @brief A socket used for the connection.
+    boost::asio::ip::tcp::socket socket_;
+
+    /// @brief Buffer into which response is written.
+    std::array<char, 8192> buf_;
+
+    /// @brief Response in the textual format.
+    std::string response_;
+};
+
+/// @brief Pointer to the HttpClient.
+typedef boost::shared_ptr<HttpClient> HttpClientPtr;
+
+class HttpListenerTest : public ::testing::Test {
+public:
+
+    HttpListenerTest()
+        : io_service_(), factory_(new TestHttpResponseCreatorFactory()),
+          test_timer_(io_service_) {
+        test_timer_.setup(boost::bind(&HttpListenerTest::timeoutHandler, this),
+                          TEST_TIMEOUT, IntervalTimer::ONE_SHOT);
+    }
+
+    /// @brief Connect to the endpoint.
+    ///
+    /// This method creates HttpClient instance and retains it in the clients_
+    /// list.
+    void startRequest() {
+        HttpClientPtr client(new HttpClient(io_service_));
+        clients_.push_back(client);
+        clients_.back()->startRequest();
+    }
+
+    /// @brief Callback function invoke upon test timeout.
+    ///
+    /// It stops the IO service and reports test timeout.
+    void timeoutHandler() {
+        ADD_FAILURE() << "Timeout occurred while running the test!";
+        io_service_.stop();
+    }
+
+    IOService io_service_;
+
+    HttpResponseCreatorFactoryPtr factory_;
+
+    /// @brief Asynchronous timer service to detect timeouts.
+    IntervalTimer test_timer_;
+
+    /// @brief List of client connections.
+    std::list<HttpClientPtr> clients_;
+};
+
+TEST_F(HttpListenerTest, listen) {
+    HttpListener listener(io_service_, IOAddress(SERVER_ADDRESS), SERVER_PORT,
+                          factory_);
+    ASSERT_NO_THROW(listener.start());
+    ASSERT_NO_THROW(startRequest());
+    ASSERT_NO_THROW(io_service_.run());
+    ASSERT_EQ(1, clients_.size());
+    HttpClientPtr client = *clients_.begin();
+    ASSERT_TRUE(client);
+    EXPECT_EQ("HTTP/1.1 200 OK\r\n"
+              "Content-Type: application/json\r\n"
+              "Date: Tue, 19 Dec 2016 18:53:35 GMT\r\n"
+              "\r\n",
+              client->getResponse());
+}
+
+}

+ 10 - 0
src/lib/http/tests/response_creator_unittests.cc

@@ -27,6 +27,16 @@ typedef boost::shared_ptr<Response> ResponsePtr;
 
 /// @brief Implementation of the @ref HttpResponseCreator.
 class TestHttpResponseCreator : public HttpResponseCreator {
+public:
+
+    /// @brief Create a new request.
+    ///
+    /// @return Pointer to the new instance of the @ref HttpRequest.
+    virtual HttpRequestPtr
+    createNewHttpRequest() const {
+        return (HttpRequestPtr(new HttpRequest()));
+    }
+
 private:
 
     /// @brief Creates HTTP 400 response.