]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[5099] Updated documentation in the new HTTP classes.
authorMarcin Siodelski <marcin@isc.org>
Tue, 31 Jan 2017 09:51:55 +0000 (10:51 +0100)
committerMarcin Siodelski <marcin@isc.org>
Tue, 31 Jan 2017 10:34:46 +0000 (11:34 +0100)
src/lib/http/connection.h
src/lib/http/connection_pool.cc
src/lib/http/connection_pool.h
src/lib/http/listener.cc
src/lib/http/listener.h
src/lib/http/response_creator_factory.h
src/lib/http/tests/listener_unittests.cc

index ee2c25d060128182476ac910bafac1a0ff7a45f1..02b31b647d824c005c3b17594fb4aafc577f7ff4 100644 (file)
 namespace isc {
 namespace http {
 
+/// @brief Generic error reported within @ref HttpConnection class.
 class HttpConnectionError : public Exception {
 public:
     HttpConnectionError(const char* file, size_t line, const char* what) :
         isc::Exception(file, line, what) { };
 };
 
-
+/// @brief Forward declaration to the @ref HttpConnectionPool.
+///
+/// This declaration is needed because we don't include the header file
+/// declaring @ref HttpConnectionPool to avoid circular inclusion.
 class HttpConnectionPool;
 
 class HttpConnection;
+/// @brief Pointer to the @ref HttpConnection.
 typedef boost::shared_ptr<HttpConnection> HttpConnectionPtr;
 
+/// @brief Accepts and handles a single HTTP connection.
 class HttpConnection : public boost::enable_shared_from_this<HttpConnection> {
 private:
 
+    /// @brief Type of the function implementing a callback invoked by the
+    /// @c SocketCallback functor.
     typedef boost::function<void(boost::system::error_code ec, size_t length)>
     SocketCallbackFunction;
 
+    /// @brief Functor associated with the socket object.
+    ///
+    /// This functor calls a callback function specified in the constructor.
     class SocketCallback {
     public:
 
+        /// @brief Constructor.
+        ///
+        /// @param socket_callback Callback to be invoked by the functor upon
+        /// an event associated with the socket.
         SocketCallback(SocketCallbackFunction socket_callback)
             : callback_(socket_callback) {
         }
 
+        /// @brief Operator called when event associated with a socket occurs.
+        ///
+        /// This operator returns immediately when received error code is
+        /// @c boost::system::error_code is equal to
+        /// @c boost::asio::error::operation_aborted, i.e. the callback is not
+        /// invoked.
+        ///
+        /// @param ec Error code.
+        /// @param length Data length.
         void operator()(boost::system::error_code ec, size_t length = 0);
 
     private:
+        /// @brief Supplied callback.
         SocketCallbackFunction callback_;
     };
 
 
 public:
 
+    /// @brief Constructor.
+    ///
+    /// @param io_service IO service to be used by the connection.
+    /// @param acceptor Reference to the TCP acceptor object used to listen for
+    /// new HTTP connections.
+    /// @param connection_pool Connection pool in which this connection is
+    /// stored.
+    /// @param response_creator Pointer to the response creator object used to
+    /// create HTTP response from the HTTP request received.
+    /// @param callback Callback invoked when new connection is accepted.
+    /// @param request_timeout Configured timeout for a HTTP request.
     HttpConnection(asiolink::IOService& io_service,
                    HttpAcceptor& acceptor,
                    HttpConnectionPool& connection_pool,
@@ -62,60 +98,124 @@ public:
                    const HttpAcceptorCallback& callback,
                    const long request_timeout);
 
+    /// @brief Destructor.
+    ///
+    /// Closes current connection.
     ~HttpConnection();
 
+    /// @brief Asynchronously accepts new connection.
+    ///
+    /// When the connection is established successfully, the timeout timer is
+    /// setup and the asynchronous read from the socket is started.
     void asyncAccept();
 
+    /// @brief Closes the socket.
     void close();
 
+    /// @brief Starts asynchronous read from the socket.
+    ///
+    /// The data received over the socket are supplied to the HTTP parser until
+    /// the parser signals that the entire request has been received or until
+    /// the parser signals an error. In the former case the server creates an
+    /// HTTP response using supplied response creator object.
+    ///
+    /// In case of error the connection is stopped.
     void doRead();
 
+private:
+
+    /// @brief Starts asynchronous write to the socket.
+    ///
+    /// The @c output_buf_ must contain the data to be sent.
+    ///
+    /// In case of error the connection is stopped.
     void doWrite();
 
+    /// @brief Sends HTTP response asynchronously.
+    ///
+    /// Internally it calls @ref HttpConnection::doWrite to send the data.
+    ///
+    /// @param response Pointer to the HTTP response to be sent.
+    void asyncSendResponse(const ConstHttpResponsePtr& response);
+
+    /// @brief Local callback invoked when new connection is accepted.
+    ///
+    /// It invokes external (supplied via constructor) acceptor callback. If
+    /// the acceptor is not opened it returns immediately. If the connection
+    /// is accepted successfully the @ref HttpConnection::doRead is called.
+    ///
+    /// @param ec Error code.
     void acceptorCallback(const boost::system::error_code& ec);
 
+    /// @brief Callback invoked when new data is received over the socket.
+    ///
+    /// This callback supplies the data to the HTTP parser and continues
+    /// parsing. When the parser signals end of the HTTP request the callback
+    /// prepares a response and starts asynchronous send over the socket.
+    ///
+    /// @param ec Error code.
+    /// @param length Length of the received data.
     void socketReadCallback(boost::system::error_code ec,
                             size_t length);
 
+    /// @brief Callback invoked when data is sent over the socket.
+    ///
+    /// @param ec Error code.
+    /// @param length Length of the data sent.
     void socketWriteCallback(boost::system::error_code ec,
                              size_t length);
 
+    /// @brief Callback invoked when the HTTP Request Timeout occurs.
+    ///
+    /// This callback creates HTTP response with Request Timeout error code
+    /// and sends it to the client.
     void requestTimeoutCallback();
 
-private:
-
-    void asyncSendResponse(const ConstHttpResponsePtr& response);
-
+    /// @brief Stops current connection.
     void stopThisConnection();
 
+    /// @brief Timer used to detect Request Timeout.
     asiolink::IntervalTimer request_timer_;
 
+    /// @brief Configured Request Timeout in milliseconds.
     long request_timeout_;
 
+    /// @brief Socket used by this connection.
     asiolink::TCPSocket<SocketCallback> socket_;
 
+    /// @brief Callback invoked when data received over the socket.
     SocketCallback socket_callback_;
 
+    /// @brief Callback invoked when data sent over the socket.
     SocketCallback socket_write_callback_;
 
+    /// @brief Reference to the TCP acceptor used to accept new connections.
     HttpAcceptor& acceptor_;
 
+    /// @brief Connection pool holding this connection.
     HttpConnectionPool& connection_pool_;
 
+    /// @brief Pointer to the @ref HttpResponseCreator object used to create
+    /// HTTP responses.
     HttpResponseCreatorPtr response_creator_;
 
+    /// @brief Pointer to the request received over this connection.
     HttpRequestPtr request_;
 
+    /// @brief Pointer to the HTTP request parser.
     HttpRequestParserPtr parser_;
 
+    /// @brief External TCP acceptor callback.
     HttpAcceptorCallback acceptor_callback_;
 
+    /// @brief Buffer for received data.
     std::array<char, 4096> buf_;
 
+    /// @brief Buffer used for outbound data.
     std::string output_buf_;
 };
 
-}
-}
+} // end of namespace isc::http
+} // end of namespace isc
 
 #endif
index 362c29197b4ad1dfd9d9cb0eedad4beeb442ab01..387a8f255a5f03eae47ab120ae43a8a42d0637c2 100644 (file)
@@ -19,7 +19,6 @@ HttpConnectionPool::start(const HttpConnectionPtr& connection) {
 void
 HttpConnectionPool::stop(const HttpConnectionPtr& connection) {
     connections_.erase(connection);
-//    connection->close();
 }
 
 void
index 1cfa7121b655ea018919d91b9f811aed64afef82..a6dd556d647377930efd9548bb9ddd63dcac4702 100644 (file)
 namespace isc {
 namespace http {
 
+/// @brief Pool of active HTTP connections.
+///
+/// The HTTP server is designed to handle many connections simultanously.
+/// The communication between the client and the server may take long time
+/// and the server must be able to react on other events while the communication
+/// with the clients is in progress. Thus, the server must track active
+/// connections and gracefully close them when needed. An obvious case when the
+/// connections must be terminated by the server is when the shutdown signal
+/// is received.
+///
+/// This object is a simple container for the server connections which provides
+/// means to terminate them on request.
 class HttpConnectionPool {
 public:
 
+    /// @brief Start new connection.
+    ///
+    /// The connection is inserted to the pool and the
+    /// @ref HttpConnection::asyncAccept is invoked.
+    ///
+    /// @param connection Pointer to the new connection.
     void start(const HttpConnectionPtr& connection);
 
+    /// @brief Stops a connection and removes it from the pool.
+    ///
+    /// If the connection is not found in the pool, this method is no-op.
+    ///
+    /// @param connection Pointer to the connection.
     void stop(const HttpConnectionPtr& connection);
 
+    /// @brief Stops all connections and removes them from the pool.
     void stopAll();
 
 private:
 
+    /// @brief Set of connections.
     std::set<HttpConnectionPtr> connections_;
 
 };
index 156cb5ff202e6d09a0fde35cca49c3664ebe0cf5..bca6df6bfebe775ee10b7f3082507a9bf7cba1bb 100644 (file)
 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 #include <asiolink/asio_wrapper.h>
+#include <asiolink/tcp_endpoint.h>
+#include <http/connection.h>
+#include <http/connection_pool.h>
+#include <http/http_acceptor.h>
 #include <http/listener.h>
+#include <boost/scoped_ptr.hpp>
 
 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,
-                           const long request_timeout)
+/// @brief Implementation of the @ref HttpListener.
+class HttpListenerImpl {
+public:
+
+    /// @brief Constructor.
+    ///
+    /// This constructor creates new server endpoint using the specified IP
+    /// address and port. It also validates other specified parameters.
+    ///
+    /// This constructor does not start accepting new connections! To start
+    /// accepting connections run @ref HttpListener::start.
+    ///
+    /// @param io_service IO service to be used by the listener.
+    /// @param server_address Address on which the HTTP service should run.
+    /// @param server_port Port number on which the HTTP service should run.
+    /// @param creator_factory Pointer to the caller-defined
+    /// @ref HttpResponseCreatorFactory derivation which should be used to
+    /// create @ref HttpResponseCreator instances.
+    /// @param request_timeout Timeout after which the HTTP Request Timeout
+    /// is generated.
+    ///
+    /// @throw HttpListenerError when any of the specified parameters is
+    /// invalid.
+    HttpListenerImpl(asiolink::IOService& io_service,
+                     const asiolink::IOAddress& server_address,
+                     const unsigned short server_port,
+                     const HttpResponseCreatorFactoryPtr& creator_factory,
+                     const long request_timeout);
+
+    /// @brief Starts accepting new connections.
+    ///
+    /// This method starts accepting and handling new HTTP connections on
+    /// the IP address and port number specified in the constructor.
+    ///
+    /// If the method is invoked successfully, it must not be invoked again
+    /// until @ref HttpListener::stop is called.
+    ///
+    /// @throw HttpListenerError if an error occurred.
+    void start();
+
+    /// @brief Stops all active connections and shuts down the service.
+    void stop();
+
+private:
+
+    /// @brief Creates @ref HttpConnection instance and adds it to the
+    /// pool of active connections.
+    ///
+    /// The next accepted connection will be handled by this instance.
+    void accept();
+
+    /// @brief Callback invoked when the new connection is accepted.
+    ///
+    /// It calls @ref HttpListener::accept to create new @ref HttpConnection
+    /// instance.
+    ///
+    /// @param ec Error code passed to the handler. This is currently ignored.
+    void acceptHandler(const boost::system::error_code& ec);
+
+    /// @brief Reference to the IO service.
+    asiolink::IOService& io_service_;
+
+    /// @brief Acceptor instance.
+    HttpAcceptor acceptor_;
+
+    /// @brief Pointer to the endpoint representing IP address and port on
+    /// which the service is running.
+    boost::scoped_ptr<asiolink::TCPEndpoint> endpoint_;
+
+    /// @brief Pool of active connections.
+    HttpConnectionPool connections_;
+
+    /// @brief Pointer to the @ref HttpResponseCreatorFactory.
+    HttpResponseCreatorFactoryPtr creator_factory_;
+
+    /// @brief Timeout for HTTP Request Timeout desired.
+    long request_timeout_;
+};
+
+HttpListenerImpl::HttpListenerImpl(IOService& io_service,
+                                   const asiolink::IOAddress& server_address,
+                                   const unsigned short server_port,
+                                   const HttpResponseCreatorFactoryPtr& creator_factory,
+                                   const long request_timeout)
     : io_service_(io_service), acceptor_(io_service),
       endpoint_(), creator_factory_(creator_factory),
       request_timeout_(request_timeout) {
+    // Try creating an endpoint. This may cause exceptions.
     try {
         endpoint_.reset(new TCPEndpoint(server_address, server_port));
 
@@ -27,23 +112,22 @@ HttpListener::HttpListener(IOService& io_service,
         isc_throw(HttpListenerError, "unable to create TCP endpoint for "
                   << server_address << ":" << server_port);
     }
+
+    // The factory must not be null.
     if (!creator_factory_) {
         isc_throw(HttpListenerError, "HttpResponseCreatorFactory must not"
                   " be null");
     }
 
+    // Request timeout is signed and must be greater than 0.
     if (request_timeout_ <= 0) {
         isc_throw(HttpListenerError, "Invalid desired HTTP request timeout "
                   << request_timeout_);
     }
 }
 
-HttpListener::~HttpListener() {
-    stop();
-}
-
 void
-HttpListener::start() {
+HttpListenerImpl::start() {
     try {
         acceptor_.open(*endpoint_);
         acceptor_.setOption(HttpAcceptor::ReuseAddress(true));
@@ -51,37 +135,67 @@ HttpListener::start() {
         acceptor_.listen();
 
     } catch (const boost::system::system_error& ex) {
+        stop();
         isc_throw(HttpListenerError, "unable to setup TCP acceptor for "
                   "listening to the incoming HTTP requests: " << ex.what());
-    } 
+    }
 
     accept();
 }
 
 void
-HttpListener::stop() {
+HttpListenerImpl::stop() {
     connections_.stopAll();
     acceptor_.close();
 }
 
 void
-HttpListener::accept() {
+HttpListenerImpl::accept() {
+    // In some cases we may need HttpResponseCreator instance per connection.
+    // But, the factory may also return the same instance each time. It
+    // depends on the use case.
     HttpResponseCreatorPtr response_creator = creator_factory_->create();
     HttpAcceptorCallback acceptor_callback =
-        boost::bind(&HttpListener::acceptHandler, this, _1);
+        boost::bind(&HttpListenerImpl::acceptHandler, this, _1);
     HttpConnectionPtr conn(new HttpConnection(io_service_, acceptor_,
                                               connections_,
                                               response_creator,
                                               acceptor_callback,
                                               request_timeout_));
+    // Add this new connection to the pool.
     connections_.start(conn);
 }
 
 void
-HttpListener::acceptHandler(const boost::system::error_code& ec) {
+HttpListenerImpl::acceptHandler(const boost::system::error_code&) {
+    // The new connection has arrived. Set the acceptor to continue
+    // accepting new connections.
     accept();
 }
 
+HttpListener::HttpListener(IOService& io_service,
+                           const asiolink::IOAddress& server_address,
+                           const unsigned short server_port,
+                           const HttpResponseCreatorFactoryPtr& creator_factory,
+                           const long request_timeout)
+    : impl_(new HttpListenerImpl(io_service, server_address, server_port,
+                                 creator_factory, request_timeout)) {
+}
+
+HttpListener::~HttpListener() {
+    stop();
+}
+
+void
+HttpListener::start() {
+    impl_->start();
+}
+
+void
+HttpListener::stop() {
+    impl_->stop();
+}
+
 
 } // end of namespace isc::http
 } // end of namespace isc
index d8ba74bcd0845c14800421f1e2619b3a57f6e9d4..6aac7628aa2d688232a4e704470273884a14d2e4 100644 (file)
@@ -9,50 +9,96 @@
 
 #include <asiolink/io_address.h>
 #include <asiolink/io_service.h>
-#include <asiolink/tcp_endpoint.h>
 #include <exceptions/exceptions.h>
-#include <http/connection.h>
-#include <http/connection_pool.h>
-#include <http/http_acceptor.h>
 #include <http/response_creator_factory.h>
-#include <boost/scoped_ptr.hpp>
+#include <boost/shared_ptr.hpp>
 
 namespace isc {
 namespace http {
 
+/// @brief A generic error raised by the @ref HttpListener class.
 class HttpListenerError : public Exception {
 public:
     HttpListenerError(const char* file, size_t line, const char* what) :
         isc::Exception(file, line, what) { };
 };
 
+/// @brief HttpListener implementation.
+class HttpListenerImpl;
+
+/// @brief HTTP listener.
+///
+/// This class is an entry point to the use of HTTP services in Kea.
+/// It creates a TCP acceptor service on the specified address and
+/// port and listens to the incoming HTTP connections. The constructor
+/// receives a pointer to the implementation of the
+/// @ref HttpResponseCreatorFactory, which is used by the @ref HttpListener
+/// to create/retrieve an instance of the @ref HttpResponseCreator when the
+/// new HTTP response needs to be generated. The @ref HttpResponseCreator
+/// creates an object derived from the @ref HttpResponse class, encapsulating
+/// a HTTP response following some specific rules, e.g. having
+/// "application/json" content type.
+///
+/// When the listener is started it creates an instance of a @ref HttpConnection
+/// and stores them in the pool of active connections. The @ref HttpConnection
+/// is responsible for managing the next connection received and receiving the
+/// HTTP request and sending appropriate response. The listener can handle
+/// many HTTP connections simultaneously.
+///
+/// When the @ref HttpListener::stop is invoked, all active connections are
+/// closed and the listener stops accepting new connections.
 class HttpListener {
 public:
 
+    /// @brief Constructor.
+    ///
+    /// This constructor creates new server endpoint using the specified IP
+    /// address and port. It also validates other specified parameters.
+    ///
+    /// This constructor does not start accepting new connections! To start
+    /// accepting connections run @ref HttpListener::start.
+    ///
+    /// @param io_service IO service to be used by the listener.
+    /// @param server_address Address on which the HTTP service should run.
+    /// @param server_port Port number on which the HTTP service should run.
+    /// @param creator_factory Pointer to the caller-defined
+    /// @ref HttpResponseCreatorFactory derivation which should be used to
+    /// create @ref HttpResponseCreator instances.
+    /// @param request_timeout Timeout after which the HTTP Request Timeout
+    /// is generated.
+    ///
+    /// @throw HttpListenerError when any of the specified parameters is
+    /// invalid.
     HttpListener(asiolink::IOService& io_service,
                  const asiolink::IOAddress& server_address,
                  const unsigned short server_port,
                  const HttpResponseCreatorFactoryPtr& creator_factory,
                  const long request_timeout);
 
+    /// @brief Destructor.
+    ///
+    /// Stops all active connections and closes TCP acceptor service.
     ~HttpListener();
 
+    /// @brief Starts accepting new connections.
+    ///
+    /// This method starts accepting and handling new HTTP connections on
+    /// the IP address and port number specified in the constructor.
+    ///
+    /// If the method is invoked successfully, it must not be invoked again
+    /// until @ref HttpListener::stop is called.
+    ///
+    /// @throw HttpListenerError if an error occurred.
     void start();
 
+    /// @brief Stops all active connections and shuts down the service.
     void stop();
 
 private:
 
-    void accept();
-
-    void acceptHandler(const boost::system::error_code& ec);
+    /// @brief Pointer to the implementation of the @ref HttpListener.
+    boost::shared_ptr<HttpListenerImpl> impl_;
 
-    asiolink::IOService& io_service_;
-    HttpAcceptor acceptor_;
-    boost::scoped_ptr<asiolink::TCPEndpoint> endpoint_;
-    HttpConnectionPool connections_;
-    HttpResponseCreatorFactoryPtr creator_factory_;
-    long request_timeout_;
 };
 
 } // end of namespace isc::http
index 1278f30e7dc664f74a78288642b5e436d5462653..4a36afeb8b63e99f5f9917f95a32ce72468c1622 100644 (file)
@@ -7,23 +7,52 @@
 #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 {
 
+/// @brief Specifies the interface for implementing custom factory classes
+/// used to create instances of @ref HttpResponseCreator.
+///
+/// The @ref HttpResponseCreator defines an interface for the classes used
+/// to generate HTTP responses. Such classes are defined outside of this
+/// library and they are specific to the needs of the particular module.
+/// In some cases it may be desired to create new instance of the
+/// @ref HttpResponseCreator implementation for every request processed.
+/// The @ref HttpResponseCreatorFactory is an interface to the "factory"
+/// class which generates canned @ref HttpResponseCreator instances. The
+/// pointer to the factory class is passed to the @ref HttpListener and
+/// the listener propagates it down to other classes. These classes call
+/// @ref HttpResponseCreatorFactory::create to retrieve an instance of the
+/// appropriate @ref HttpResponseCreator, which is in turn used to generate
+/// HTTP response.
+///
+/// Note that an implementation of the @ref HttpResponseCreatorFactory::create
+/// may always return the same instance of the @ref HttpResponseCreator
+/// if creating new instance for each request is not required or undesired.
 class HttpResponseCreatorFactory {
 public:
 
+    /// @brief Virtual destructor.
+    ///
+    /// The implementation doesn't need to declare virtual destrtuctor because
+    /// it is already implemented here.
     virtual ~HttpResponseCreatorFactory() { }
 
+    /// @brief Returns an instance of the @ref HttpResponseCreator.
+    ///
+    /// The implementation may create new instance every time this method
+    /// is called, or it may always return the same instance.
+    ///
+    /// @return Pointer to the instance of the @ref HttpResponseCreator to
+    /// be used to generate HTTP response.
     virtual HttpResponseCreatorPtr create() const = 0;
 
 };
 
+/// @brief Pointer to the @ref HttpResponseCreatorFactory.
 typedef boost::shared_ptr<HttpResponseCreatorFactory>
 HttpResponseCreatorFactoryPtr;
 
index 889bf7d737d57a0d384ba80a1d13cc89fe44a919..f224f6ead1e128030d68011543152a0a7544ccd9 100644 (file)
 #include <list>
 #include <string>
 
+using namespace boost::asio::ip;
 using namespace isc::asiolink;
 using namespace isc::http;
 using namespace isc::http::test;
 
 namespace {
 
+/// @brief IP address to which HTTP service is bound.
 const std::string SERVER_ADDRESS = "127.0.0.1";
+
+/// @brief Port number to which HTTP service is bound.
 const unsigned short SERVER_PORT = 18123;
-const long REQUEST_TIMEOUT = 20000;
 
-/// @brief Test timeout in ms.
+/// @brief Request Timeout used in most of the tests (ms).
+const long REQUEST_TIMEOUT = 10000;
+
+/// @brief Test timeout (ms).
 const long TEST_TIMEOUT = 10000;
 
 /// @brief Test HTTP response.
@@ -85,9 +91,13 @@ private:
     }
 };
 
+/// @brief Implementation of the test @ref HttpResponseCreatorFactory.
+///
+/// This factory class creates @ref TestHttpResponseCreator instances.
 class TestHttpResponseCreatorFactory : public HttpResponseCreatorFactory {
 public:
 
+    /// @brief Creates @ref TestHttpResponseCreator instance.
     virtual HttpResponseCreatorPtr create() const {
         HttpResponseCreatorPtr response_creator(new TestHttpResponseCreator());
         return (response_creator);
@@ -116,10 +126,12 @@ public:
         close();
     }
 
+    /// @brief Send HTTP request specified in textual format.
+    ///
+    /// @param request HTTP request in the textual format.
     void startRequest(const std::string& request) {
-        boost::asio::ip::tcp::endpoint
-            endpoint(boost::asio::ip::address::from_string(SERVER_ADDRESS),
-                     SERVER_PORT);
+        tcp::endpoint endpoint(address::from_string(SERVER_ADDRESS),
+                               SERVER_PORT);
         socket_.async_connect(endpoint,
                               [this, request](const boost::system::error_code& ec) {
             if (ec) {
@@ -131,10 +143,16 @@ public:
         });
     }
 
+    /// @brief Send HTTP request.
+    ///
+    /// @param request HTTP request in the textual format.
     void sendRequest(const std::string& request) {
         sendPartialRequest(request);
     }
 
+    /// @brief Send part of the HTTP request.
+    ///
+    /// @param request part of the HTTP request to be sent.
     void sendPartialRequest(std::string request) {
         socket_.async_send(boost::asio::buffer(request.data(), request.size()),
                            [this, request](const boost::system::error_code& ec,
@@ -146,25 +164,36 @@ public:
                 return;
             }
 
+            // Remove the part of the request which has been sent.
             if (bytes_transferred > 0 && (request.size() <= bytes_transferred)) {
                 request.erase(0, bytes_transferred);
             }
 
+            // Continue sending request data if there are still some data to be
+            // sent.
             if (!request.empty()) {
                 sendPartialRequest(request);
 
             } else {
+                // Request has been sent. Start receiving response.
                 response_.clear();
                 receivePartialResponse();
             }
        });
     }
 
+    /// @brief Receive response from the server.
     void receivePartialResponse() {
         socket_.async_read_some(boost::asio::buffer(buf_),
                                 [this](const boost::system::error_code& ec,
                                        std::size_t bytes_transferred) {
             if (ec) {
+                // IO service stopped so simply return.
+                if (ec == boost::asio::error::operation_aborted) {
+                    return;
+                }
+
+                // Error occurred, bail...
                 ADD_FAILURE() << "error occurred while receiving HTTP"
                     " response from the server: " << ec.message();
                 io_service_.stop();
@@ -175,8 +204,13 @@ public:
                                  buf_.data() + bytes_transferred);
             }
 
+            // Two consecutive new lines end the part of the response we're
+            // expecting.
             if (response_.find("\r\n\r\n", 0) != std::string::npos) {
                 io_service_.stop();
+
+            } else {
+                receivePartialResponse();
             }
 
         });
@@ -209,9 +243,13 @@ private:
 /// @brief Pointer to the HttpClient.
 typedef boost::shared_ptr<HttpClient> HttpClientPtr;
 
+/// @brief Test fixture class for @ref HttpListener.
 class HttpListenerTest : public ::testing::Test {
 public:
 
+    /// @brief Constructor.
+    ///
+    /// Starts test timer which detects timeouts.
     HttpListenerTest()
         : io_service_(), factory_(new TestHttpResponseCreatorFactory()),
           test_timer_(io_service_), clients_() {
@@ -219,7 +257,10 @@ public:
                           TEST_TIMEOUT, IntervalTimer::ONE_SHOT);
     }
 
-    ~HttpListenerTest() {
+    /// @brief Destructor.
+    ///
+    /// Removes active HTTP clients.
+    virtual ~HttpListenerTest() {
         for (auto client = clients_.begin(); client != clients_.end();
              ++client) {
             (*client)->close();
@@ -244,8 +285,10 @@ public:
         io_service_.stop();
     }
 
+    /// @brief IO service used in the tests.
     IOService io_service_;
 
+    /// @brief Pointer to the response creator factory.
     HttpResponseCreatorFactoryPtr factory_;
 
     /// @brief Asynchronous timer service to detect timeouts.
@@ -255,6 +298,8 @@ public:
     std::list<HttpClientPtr> clients_;
 };
 
+// This test verifies that HTTP connection can be established and used to
+// transmit HTTP request and receive a response.
 TEST_F(HttpListenerTest, listen) {
     const std::string request = "POST /foo/bar HTTP/1.1\r\n"
         "Content-Type: application/json\r\n"
@@ -278,7 +323,18 @@ TEST_F(HttpListenerTest, listen) {
     io_service_.poll();
 }
 
+// This test verifies that the HTTP listener can't be started twice.
+TEST_F(HttpListenerTest, startTwice) {
+    HttpListener listener(io_service_, IOAddress(SERVER_ADDRESS), SERVER_PORT,
+                          factory_, REQUEST_TIMEOUT);
+    ASSERT_NO_THROW(listener.start());
+    EXPECT_THROW(listener.start(), HttpListenerError);
+}
+
+// This test verifies that Bad Request status is returned when the request
+// is malformed.
 TEST_F(HttpListenerTest, badRequest) {
+    // Content-Type is wrong. This should result in Bad Request status.
     const std::string request = "POST /foo/bar HTTP/1.1\r\n"
         "Content-Type: foo\r\n"
         "Content-Length: 3\r\n\r\n"
@@ -301,6 +357,8 @@ TEST_F(HttpListenerTest, badRequest) {
               client->getResponse());
 }
 
+// This test verifies that NULL pointer can't be specified for the
+// HttpResponseCreatorFactory.
 TEST_F(HttpListenerTest, invalidFactory) {
     EXPECT_THROW(HttpListener(io_service_, IOAddress(SERVER_ADDRESS),
                               SERVER_PORT, HttpResponseCreatorFactoryPtr(),
@@ -308,30 +366,43 @@ TEST_F(HttpListenerTest, invalidFactory) {
                  HttpListenerError);
 }
 
+// This test verifies that the timeout of 0 can't be specified for the
+// Request Timeout.
 TEST_F(HttpListenerTest, invalidRequestTimeout) {
     EXPECT_THROW(HttpListener(io_service_, IOAddress(SERVER_ADDRESS),
                               SERVER_PORT, factory_, 0),
                  HttpListenerError);
 }
 
+// This test verifies that listener can't be bound to the port to which
+// other server is bound.
 TEST_F(HttpListenerTest, addressInUse) {
-    boost::asio::ip::tcp::acceptor acceptor(io_service_.get_io_service());;
-    boost::asio::ip::tcp::endpoint
-        endpoint(boost::asio::ip::address::from_string(SERVER_ADDRESS),
-                 SERVER_PORT + 1);
+    tcp::acceptor acceptor(io_service_.get_io_service());
+    // Use other port than SERVER_PORT to make sure that this TCP connection
+    // doesn't affect subsequent tests.
+    tcp::endpoint endpoint(address::from_string(SERVER_ADDRESS),
+                           SERVER_PORT + 1);
     acceptor.open(endpoint.protocol());
     acceptor.bind(endpoint);
 
+    // Listener should report an error when we try to start it because another
+    // acceptor is bound to that port and address.
     HttpListener listener(io_service_, IOAddress(SERVER_ADDRESS),
                           SERVER_PORT + 1, factory_, REQUEST_TIMEOUT);
     EXPECT_THROW(listener.start(), HttpListenerError);
 }
 
+// This test verifies that HTTP Request Timeout status is returned as
+// expected.
 TEST_F(HttpListenerTest, requestTimeout) {
+    // The part of the request specified here is correct but it is not
+    // a complete request.
     const std::string request = "POST /foo/bar HTTP/1.1\r\n"
-        "Content-Type: foo\r\n"
+        "Content-Type: application/json\r\n"
         "Content-Length:";
 
+    // Open the listener with the Request Timeout of 1 sec and post the
+    // partial request.
     HttpListener listener(io_service_, IOAddress(SERVER_ADDRESS), SERVER_PORT,
                           factory_, 1000);
     ASSERT_NO_THROW(listener.start());
@@ -340,6 +411,10 @@ TEST_F(HttpListenerTest, requestTimeout) {
     ASSERT_EQ(1, clients_.size());
     HttpClientPtr client = *clients_.begin();
     ASSERT_TRUE(client);
+
+    // The server should wait for the missing part of the request for 1 second.
+    // The missing part never arrives so the server should respond with the
+    // HTTP Request Timeout status.
     EXPECT_EQ("HTTP/1.1 408 Request Timeout\r\n"
               "Content-Length: 44\r\n"
               "Content-Type: application/json\r\n"