connection.hpp
// ---------------------------------------------------------------------
// pion: a Boost C++ framework for building lightweight HTTP interfaces
// ---------------------------------------------------------------------
// Copyright (C) 2007-2014 Splunk Inc. (https://github.com/splunk/pion)
//
// Distributed under the Boost Software License, Version 1.0.
// See http://www.boost.org/LICENSE_1_0.txt
//
#ifndef __PION_TCP_CONNECTION_HEADER__
#define __PION_TCP_CONNECTION_HEADER__
#ifdef PION_HAVE_SSL
#ifdef PION_XCODE
// ignore openssl warnings if building with XCode
#pragma GCC system_header
#endif
#include <boost/asio/ssl.hpp>
#endif
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/asio.hpp>
#include <boost/array.hpp>
#include <boost/function.hpp>
#include <boost/function/function1.hpp>
#include <pion/config.hpp>
#include <string>
namespace pion { // begin namespace pion
namespace tcp { // begin namespace tcp
class connection :
public boost::enable_shared_from_this<connection>,
private boost::noncopyable
{
public:
enum lifecycle_type {
LIFECYCLE_CLOSE, LIFECYCLE_KEEPALIVE, LIFECYCLE_PIPELINED
};
enum { READ_BUFFER_SIZE = 8192 };
typedef boost::function1<void, boost::shared_ptr<connection> > connection_handler;
typedef boost::array<char, READ_BUFFER_SIZE> read_buffer_type;
typedef boost::asio::ip::tcp::socket socket_type;
#ifdef PION_HAVE_SSL
typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket_type;
typedef boost::asio::ssl::context ssl_context_type;
#else
class ssl_socket_type {
public:
ssl_socket_type(boost::asio::io_service& io_service) : m_socket(io_service) {}
inline socket_type& next_layer(void) { return m_socket; }
inline const socket_type& next_layer(void) const { return m_socket; }
inline socket_type::lowest_layer_type& lowest_layer(void) { return m_socket.lowest_layer(); }
inline const socket_type::lowest_layer_type& lowest_layer(void) const { return m_socket.lowest_layer(); }
inline void shutdown(void) {}
private:
socket_type m_socket;
};
typedef int ssl_context_type;
#endif
static inline boost::shared_ptr<connection> create(boost::asio::io_service& io_service,
ssl_context_type& ssl_context,
const bool ssl_flag,
connection_handler finished_handler)
{
return boost::shared_ptr<connection>(new connection(io_service, ssl_context,
ssl_flag, finished_handler));
}
explicit connection(boost::asio::io_service& io_service, const bool ssl_flag = false)
:
#ifdef PION_HAVE_SSL
m_ssl_context(io_service, boost::asio::ssl::context::sslv23),
m_ssl_socket(io_service, m_ssl_context),
m_ssl_flag(ssl_flag),
#else
m_ssl_context(0),
m_ssl_socket(io_service),
m_ssl_flag(false),
#endif
m_lifecycle(LIFECYCLE_CLOSE)
{
save_read_pos(NULL, NULL);
}
connection(boost::asio::io_service& io_service, ssl_context_type& ssl_context)
:
#ifdef PION_HAVE_SSL
m_ssl_context(io_service, boost::asio::ssl::context::sslv23),
m_ssl_socket(io_service, ssl_context), m_ssl_flag(true),
#else
m_ssl_context(0),
m_ssl_socket(io_service), m_ssl_flag(false),
#endif
m_lifecycle(LIFECYCLE_CLOSE)
{
save_read_pos(NULL, NULL);
}
inline bool is_open(void) const {
return const_cast<ssl_socket_type&>(m_ssl_socket).lowest_layer().is_open();
}
inline void close(void) {
if (is_open()) {
try {
// shutting down SSL will wait forever for a response from the remote end,
// which causes it to hang indefinitely if the other end died unexpectedly
// if (get_ssl_flag()) m_ssl_socket.shutdown();
// windows seems to require this otherwise it doesn't
// recognize that connections have been closed
m_ssl_socket.next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both);
} catch (...) {} // ignore exceptions
// close the underlying socket (ignore errors)
boost::system::error_code ec;
m_ssl_socket.next_layer().close(ec);
}
}
inline void cancel(void) {
#if !defined(_MSC_VER) || (_WIN32_WINNT >= 0x0600)
boost::system::error_code ec;
m_ssl_socket.next_layer().cancel(ec);
#endif
}
virtual ~connection() { close(); }
template <typename AcceptHandler>
inline void async_accept(boost::asio::ip::tcp::acceptor& tcp_acceptor,
AcceptHandler handler)
{
tcp_acceptor.async_accept(m_ssl_socket.lowest_layer(), handler);
}
inline boost::system::error_code accept(boost::asio::ip::tcp::acceptor& tcp_acceptor)
{
boost::system::error_code ec;
tcp_acceptor.accept(m_ssl_socket.lowest_layer(), ec);
return ec;
}
template <typename ConnectHandler>
inline void async_connect(const boost::asio::ip::tcp::endpoint& tcp_endpoint,
ConnectHandler handler)
{
m_ssl_socket.lowest_layer().async_connect(tcp_endpoint, handler);
}
template <typename ConnectHandler>
inline void async_connect(const boost::asio::ip::address& remote_addr,
const unsigned int remote_port,
ConnectHandler handler)
{
boost::asio::ip::tcp::endpoint tcp_endpoint(remote_addr, remote_port);
async_connect(tcp_endpoint, handler);
}
inline boost::system::error_code connect(boost::asio::ip::tcp::endpoint& tcp_endpoint)
{
boost::system::error_code ec;
m_ssl_socket.lowest_layer().connect(tcp_endpoint, ec);
return ec;
}
inline boost::system::error_code connect(const boost::asio::ip::address& remote_addr,
const unsigned int remote_port)
{
boost::asio::ip::tcp::endpoint tcp_endpoint(remote_addr, remote_port);
return connect(tcp_endpoint);
}
inline boost::system::error_code connect(const std::string& remote_server,
const unsigned int remote_port)
{
// query a list of matching endpoints
boost::system::error_code ec;
boost::asio::ip::tcp::resolver resolver(m_ssl_socket.lowest_layer().get_io_service());
boost::asio::ip::tcp::resolver::query query(remote_server,
boost::lexical_cast<std::string>(remote_port),
boost::asio::ip::tcp::resolver::query::numeric_service);
boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query, ec);
if (ec)
return ec;
// try each one until we are successful
ec = boost::asio::error::host_not_found;
boost::asio::ip::tcp::resolver::iterator end;
while (ec && endpoint_iterator != end) {
boost::asio::ip::tcp::endpoint ep(endpoint_iterator->endpoint());
++endpoint_iterator;
ec = connect(ep);
if (ec)
close();
}
return ec;
}
template <typename SSLHandshakeHandler>
inline void async_handshake_client(SSLHandshakeHandler handler) {
#ifdef PION_HAVE_SSL
m_ssl_socket.async_handshake(boost::asio::ssl::stream_base::client, handler);
m_ssl_flag = true;
#endif
}
template <typename SSLHandshakeHandler>
inline void async_handshake_server(SSLHandshakeHandler handler) {
#ifdef PION_HAVE_SSL
m_ssl_socket.async_handshake(boost::asio::ssl::stream_base::server, handler);
m_ssl_flag = true;
#endif
}
inline boost::system::error_code handshake_client(void) {
boost::system::error_code ec;
#ifdef PION_HAVE_SSL
m_ssl_socket.handshake(boost::asio::ssl::stream_base::client, ec);
m_ssl_flag = true;
#endif
return ec;
}
inline boost::system::error_code handshake_server(void) {
boost::system::error_code ec;
#ifdef PION_HAVE_SSL
m_ssl_socket.handshake(boost::asio::ssl::stream_base::server, ec);
m_ssl_flag = true;
#endif
return ec;
}
template <typename ReadHandler>
inline void async_read_some(ReadHandler handler) {
#ifdef PION_HAVE_SSL
if (get_ssl_flag())
m_ssl_socket.async_read_some(boost::asio::buffer(m_read_buffer),
handler);
else
#endif
m_ssl_socket.next_layer().async_read_some(boost::asio::buffer(m_read_buffer),
handler);
}
template <typename ReadBufferType, typename ReadHandler>
inline void async_read_some(ReadBufferType read_buffer,
ReadHandler handler) {
#ifdef PION_HAVE_SSL
if (get_ssl_flag())
m_ssl_socket.async_read_some(read_buffer, handler);
else
#endif
m_ssl_socket.next_layer().async_read_some(read_buffer, handler);
}
inline std::size_t read_some(boost::system::error_code& ec) {
#ifdef PION_HAVE_SSL
if (get_ssl_flag())
return m_ssl_socket.read_some(boost::asio::buffer(m_read_buffer), ec);
else
#endif
return m_ssl_socket.next_layer().read_some(boost::asio::buffer(m_read_buffer), ec);
}
template <typename ReadBufferType>
inline std::size_t read_some(ReadBufferType read_buffer,
boost::system::error_code& ec)
{
#ifdef PION_HAVE_SSL
if (get_ssl_flag())
return m_ssl_socket.read_some(read_buffer, ec);
else
#endif
return m_ssl_socket.next_layer().read_some(read_buffer, ec);
}
template <typename CompletionCondition, typename ReadHandler>
inline void async_read(CompletionCondition completion_condition,
ReadHandler handler)
{
#ifdef PION_HAVE_SSL
if (get_ssl_flag())
boost::asio::async_read(m_ssl_socket, boost::asio::buffer(m_read_buffer),
completion_condition, handler);
else
#endif
boost::asio::async_read(m_ssl_socket.next_layer(), boost::asio::buffer(m_read_buffer),
completion_condition, handler);
}
template <typename MutableBufferSequence, typename CompletionCondition, typename ReadHandler>
inline void async_read(const MutableBufferSequence& buffers,
CompletionCondition completion_condition,
ReadHandler handler)
{
#ifdef PION_HAVE_SSL
if (get_ssl_flag())
boost::asio::async_read(m_ssl_socket, buffers,
completion_condition, handler);
else
#endif
boost::asio::async_read(m_ssl_socket.next_layer(), buffers,
completion_condition, handler);
}
template <typename CompletionCondition>
inline std::size_t read(CompletionCondition completion_condition,
boost::system::error_code& ec)
{
#ifdef PION_HAVE_SSL
if (get_ssl_flag())
return boost::asio::async_read(m_ssl_socket, boost::asio::buffer(m_read_buffer),
completion_condition, ec);
else
#endif
return boost::asio::async_read(m_ssl_socket.next_layer(), boost::asio::buffer(m_read_buffer),
completion_condition, ec);
}
template <typename MutableBufferSequence, typename CompletionCondition>
inline std::size_t read(const MutableBufferSequence& buffers,
CompletionCondition completion_condition,
boost::system::error_code& ec)
{
#ifdef PION_HAVE_SSL
if (get_ssl_flag())
return boost::asio::read(m_ssl_socket, buffers,
completion_condition, ec);
else
#endif
return boost::asio::read(m_ssl_socket.next_layer(), buffers,
completion_condition, ec);
}
template <typename ConstBufferSequence, typename write_handler_t>
inline void async_write(const ConstBufferSequence& buffers, write_handler_t handler) {
#ifdef PION_HAVE_SSL
if (get_ssl_flag())
boost::asio::async_write(m_ssl_socket, buffers, handler);
else
#endif
boost::asio::async_write(m_ssl_socket.next_layer(), buffers, handler);
}
template <typename ConstBufferSequence>
inline std::size_t write(const ConstBufferSequence& buffers,
boost::system::error_code& ec)
{
#ifdef PION_HAVE_SSL
if (get_ssl_flag())
return boost::asio::write(m_ssl_socket, buffers,
boost::asio::transfer_all(), ec);
else
#endif
return boost::asio::write(m_ssl_socket.next_layer(), buffers,
boost::asio::transfer_all(), ec);
}
inline void finish(void) { if (m_finished_handler) m_finished_handler(shared_from_this()); }
inline bool get_ssl_flag(void) const { return m_ssl_flag; }
inline void set_lifecycle(lifecycle_type t) { m_lifecycle = t; }
inline lifecycle_type get_lifecycle(void) const { return m_lifecycle; }
inline bool get_keep_alive(void) const { return m_lifecycle != LIFECYCLE_CLOSE; }
inline bool get_pipelined(void) const { return m_lifecycle == LIFECYCLE_PIPELINED; }
inline read_buffer_type& get_read_buffer(void) { return m_read_buffer; }
inline void save_read_pos(const char *read_ptr, const char *read_end_ptr) {
m_read_position.first = read_ptr;
m_read_position.second = read_end_ptr;
}
inline void load_read_pos(const char *&read_ptr, const char *&read_end_ptr) const {
read_ptr = m_read_position.first;
read_end_ptr = m_read_position.second;
}
inline boost::asio::ip::tcp::endpoint get_remote_endpoint(void) const {
boost::asio::ip::tcp::endpoint remote_endpoint;
try {
// const_cast is required since lowest_layer() is only defined non-const in asio
remote_endpoint = const_cast<ssl_socket_type&>(m_ssl_socket).lowest_layer().remote_endpoint();
} catch (boost::system::system_error& /* e */) {
// do nothing
}
return remote_endpoint;
}
inline boost::asio::ip::address get_remote_ip(void) const {
return get_remote_endpoint().address();
}
inline unsigned short get_remote_port(void) const {
return get_remote_endpoint().port();
}
inline boost::asio::io_service& get_io_service(void) {
return m_ssl_socket.lowest_layer().get_io_service();
}
inline socket_type& get_socket(void) { return m_ssl_socket.next_layer(); }
inline ssl_socket_type& get_ssl_socket(void) { return m_ssl_socket; }
inline const socket_type& get_socket(void) const { return const_cast<ssl_socket_type&>(m_ssl_socket).next_layer(); }
inline const ssl_socket_type& get_ssl_socket(void) const { return m_ssl_socket; }
protected:
connection(boost::asio::io_service& io_service,
ssl_context_type& ssl_context,
const bool ssl_flag,
connection_handler finished_handler)
:
#ifdef PION_HAVE_SSL
m_ssl_context(io_service, boost::asio::ssl::context::sslv23),
m_ssl_socket(io_service, ssl_context), m_ssl_flag(ssl_flag),
#else
m_ssl_context(0),
m_ssl_socket(io_service), m_ssl_flag(false),
#endif
m_lifecycle(LIFECYCLE_CLOSE),
m_finished_handler(finished_handler)
{
save_read_pos(NULL, NULL);
}
private:
typedef std::pair<const char*, const char*> read_pos_type;
ssl_context_type m_ssl_context;
ssl_socket_type m_ssl_socket;
bool m_ssl_flag;
read_buffer_type m_read_buffer;
read_pos_type m_read_position;
lifecycle_type m_lifecycle;
connection_handler m_finished_handler;
};
typedef boost::shared_ptr<connection> connection_ptr;
} // end namespace tcp
} // end namespace pion
#endif
pion::tcp::connection::save_read_pos
void save_read_pos(const char *read_ptr, const char *read_end_ptr)
Definition: connection.hpp:622
pion::tcp::connection::get_lifecycle
lifecycle_type get_lifecycle(void) const
returns the lifecycle type for the connection
Definition: connection.hpp:605
pion::tcp::connection::handshake_client
boost::system::error_code handshake_client(void)
Definition: connection.hpp:353
pion::tcp::connection::finish
void finish(void)
Definition: connection.hpp:596
pion::tcp::connection::connection
connection(boost::asio::io_service &io_service, ssl_context_type &ssl_context, const bool ssl_flag, connection_handler finished_handler)
Definition: connection.hpp:689
pion::tcp::connection::get_remote_endpoint
boost::asio::ip::tcp::endpoint get_remote_endpoint(void) const
returns an ASIO endpoint for the client connection
Definition: connection.hpp:639
pion::tcp::connection::handshake_server
boost::system::error_code handshake_server(void)
Definition: connection.hpp:369
pion::tcp::connection::create
static boost::shared_ptr< connection > create(boost::asio::io_service &io_service, ssl_context_type &ssl_context, const bool ssl_flag, connection_handler finished_handler)
Definition: connection.hpp:94
pion::tcp::connection::ssl_socket_type
Definition: connection.hpp:70
pion::tcp::connection::connection_handler
boost::function1< void, boost::shared_ptr< connection > > connection_handler
data type for a function that handles TCP connection objects
Definition: connection.hpp:55
pion::tcp::connection::async_handshake_client
void async_handshake_client(SSLHandshakeHandler handler)
Definition: connection.hpp:324
pion::tcp::connection
Definition: connection.hpp:40
pion::tcp::connection::socket_type
boost::asio::ip::tcp::socket socket_type
data type for a socket connection
Definition: connection.hpp:61
pion::tcp::connection::read
std::size_t read(const MutableBufferSequence &buffers, CompletionCondition completion_condition, boost::system::error_code &ec)
Definition: connection.hpp:538
pion::tcp::connection::async_read
void async_read(CompletionCondition completion_condition, ReadHandler handler)
Definition: connection.hpp:464
pion::tcp::connection::get_remote_ip
boost::asio::ip::address get_remote_ip(void) const
returns the client&#39;s IP address
Definition: connection.hpp:651
pion::tcp::connection::read
std::size_t read(CompletionCondition completion_condition, boost::system::error_code &ec)
Definition: connection.hpp:513
pion::tcp::connection::async_read
void async_read(const MutableBufferSequence &buffers, CompletionCondition completion_condition, ReadHandler handler)
Definition: connection.hpp:488
pion::tcp::connection::get_io_service
boost::asio::io_service & get_io_service(void)
returns reference to the io_service used for async operations
Definition: connection.hpp:661
pion::tcp::connection::async_handshake_server
void async_handshake_server(SSLHandshakeHandler handler)
Definition: connection.hpp:339
pion::tcp::connection::read_some
std::size_t read_some(ReadBufferType read_buffer, boost::system::error_code &ec)
Definition: connection.hpp:443
pion::tcp::connection::~connection
virtual ~connection()
virtual destructor
Definition: connection.hpp:184
pion::tcp::connection::close
void close(void)
closes the tcp socket and cancels any pending asynchronous operations
Definition: connection.hpp:151
pion::tcp::connection::get_remote_port
unsigned short get_remote_port(void) const
returns the client&#39;s port number
Definition: connection.hpp:656
pion::tcp::connection::read_some
std::size_t read_some(boost::system::error_code &ec)
Definition: connection.hpp:424
pion::tcp::connection::get_read_buffer
read_buffer_type & get_read_buffer(void)
returns the buffer used for reading data from the TCP connection
Definition: connection.hpp:614
pion::tcp::connection::async_connect
void async_connect(const boost::asio::ip::address &remote_addr, const unsigned int remote_port, ConnectHandler handler)
Definition: connection.hpp:241
pion::tcp::connection::async_read_some
void async_read_some(ReadBufferType read_buffer, ReadHandler handler)
Definition: connection.hpp:406
pion::tcp::connection::read_buffer_type
boost::array< char, READ_BUFFER_SIZE > read_buffer_type
data type for an I/O read buffer
Definition: connection.hpp:58
pion::tcp::connection::get_socket
socket_type & get_socket(void)
returns non-const reference to underlying TCP socket object
Definition: connection.hpp:666
pion::tcp::connection::get_pipelined
bool get_pipelined(void) const
returns true if the HTTP requests are pipelined
Definition: connection.hpp:611
pion::tcp::connection::async_accept
void async_accept(boost::asio::ip::tcp::acceptor &tcp_acceptor, AcceptHandler handler)
Definition: connection.hpp:195
pion::tcp::connection::async_read_some
void async_read_some(ReadHandler handler)
Definition: connection.hpp:386
pion::tcp::connection::get_ssl_socket
const ssl_socket_type & get_ssl_socket(void) const
returns const reference to underlying SSL socket object
Definition: connection.hpp:675
pion::tcp::connection::get_keep_alive
bool get_keep_alive(void) const
returns true if the connection should be kept alive
Definition: connection.hpp:608
pion::tcp::connection::set_lifecycle
void set_lifecycle(lifecycle_type t)
sets the lifecycle type for the connection
Definition: connection.hpp:602
pion
Definition: admin_rights.hpp:19
pion::tcp::connection::load_read_pos
void load_read_pos(const char *&read_ptr, const char *&read_end_ptr) const
Definition: connection.hpp:633
pion::tcp::connection::get_ssl_socket
ssl_socket_type & get_ssl_socket(void)
returns non-const reference to underlying SSL socket object
Definition: connection.hpp:669
pion::tcp::connection::connect
boost::system::error_code connect(const boost::asio::ip::address &remote_addr, const unsigned int remote_port)
Definition: connection.hpp:273
pion::tcp::connection::lifecycle_type
lifecycle_type
data type for the connection&#39;s lifecycle state
Definition: connection.hpp:47
pion::tcp::connection::async_write
void async_write(const ConstBufferSequence &buffers, write_handler_t handler)
Definition: connection.hpp:561
pion::tcp::connection::async_connect
void async_connect(const boost::asio::ip::tcp::endpoint &tcp_endpoint, ConnectHandler handler)
Definition: connection.hpp:225
pion::tcp::connection::cancel
void cancel(void)
Definition: connection.hpp:176
pion::tcp::connection::get_ssl_flag
bool get_ssl_flag(void) const
returns true if the connection is encrypted using SSL
Definition: connection.hpp:599
pion::tcp::connection::connection
connection(boost::asio::io_service &io_service, ssl_context_type &ssl_context)
Definition: connection.hpp:131
pion::tcp::connection::write
std::size_t write(const ConstBufferSequence &buffers, boost::system::error_code &ec)
Definition: connection.hpp:580
pion::tcp::connection::get_socket
const socket_type & get_socket(void) const
returns const reference to underlying TCP socket object
Definition: connection.hpp:672
pion::tcp::connection::accept
boost::system::error_code accept(boost::asio::ip::tcp::acceptor &tcp_acceptor)
Definition: connection.hpp:209
pion::tcp::connection::is_open
bool is_open(void) const
returns true if the connection is currently open
Definition: connection.hpp:146
pion::tcp::connection::connect
boost::system::error_code connect(const std::string &remote_server, const unsigned int remote_port)
Definition: connection.hpp:289
pion::tcp::connection::connect
boost::system::error_code connect(boost::asio::ip::tcp::endpoint &tcp_endpoint)
Definition: connection.hpp:257
pion::tcp::connection::connection
connection(boost::asio::io_service &io_service, const bool ssl_flag=false)
Definition: connection.hpp:109