Commit dba214ec authored by Marcin Siodelski's avatar Marcin Siodelski
Browse files

[5099] Implemented HttpListener and other classes.

- HttpConnection
- HttpConnectionPool
parent 74afe627
// 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;
}
}
......
......@@ -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
......
// 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
// 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
// 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();
}
}
}
// 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
// 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
......@@ -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
// 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
// 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
// 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
......
......@@ -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.
......
// 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>