Commit 0515ecda authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[5099] Updated documentation in the new HTTP classes.

parent 4a9d027f
......@@ -21,40 +21,76 @@
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
......@@ -19,7 +19,6 @@ HttpConnectionPool::start(const HttpConnectionPtr& connection) {
void
HttpConnectionPool::stop(const HttpConnectionPtr& connection) {
connections_.erase(connection);
// connection->close();
}
void
......
......@@ -13,17 +13,42 @@
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_;
};
......
......@@ -5,21 +5,106 @@
// 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
......@@ -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
......
......@@ -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;