10 #include <boost/asio.hpp> 11 #include <boost/bind.hpp> 12 #include <boost/thread/mutex.hpp> 13 #include <pion/admin_rights.hpp> 14 #include <pion/tcp/server.hpp> 24 : m_logger(PION_GET_LOGGER(
"pion.tcp.server")),
25 m_active_scheduler(sched),
26 m_tcp_acceptor(m_active_scheduler.get_io_service()),
28 m_ssl_context(m_active_scheduler.get_io_service(), boost::asio::ssl::context::sslv23),
32 m_endpoint(boost::asio::ip::tcp::v4(), tcp_port), m_ssl_flag(false), m_is_listening(false)
36 :
m_logger(PION_GET_LOGGER(
"pion.tcp.server")),
37 m_active_scheduler(sched),
40 m_ssl_context(m_active_scheduler.
get_io_service(), boost::asio::ssl::context::sslv23),
44 m_endpoint(endpoint), m_ssl_flag(false), m_is_listening(false)
48 :
m_logger(PION_GET_LOGGER(
"pion.tcp.server")),
49 m_default_scheduler(), m_active_scheduler(m_default_scheduler),
52 m_ssl_context(m_active_scheduler.
get_io_service(), boost::asio::ssl::context::sslv23),
56 m_endpoint(boost::asio::ip::tcp::v4(), tcp_port), m_ssl_flag(false), m_is_listening(false)
60 :
m_logger(PION_GET_LOGGER(
"pion.tcp.server")),
61 m_default_scheduler(), m_active_scheduler(m_default_scheduler),
64 m_ssl_context(m_active_scheduler.
get_io_service(), boost::asio::ssl::context::sslv23),
68 m_endpoint(endpoint), m_ssl_flag(false), m_is_listening(false)
74 boost::mutex::scoped_lock server_lock(m_mutex);
76 if (! m_is_listening) {
85 m_tcp_acceptor.open(m_endpoint.protocol());
89 m_tcp_acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(
true));
91 m_tcp_acceptor.bind(m_endpoint);
92 if (m_endpoint.port() == 0) {
94 m_endpoint = m_tcp_acceptor.local_endpoint();
96 m_tcp_acceptor.listen();
97 }
catch (std::exception& e) {
98 PION_LOG_ERROR(
m_logger,
"Unable to bind to port " <<
get_port() <<
": " << e.what());
102 m_is_listening =
true;
105 server_lock.unlock();
116 boost::mutex::scoped_lock server_lock(m_mutex);
118 if (m_is_listening) {
121 m_is_listening =
false;
124 m_tcp_acceptor.close();
126 if (! wait_until_finished) {
128 std::for_each(m_conn_pool.begin(), m_conn_pool.end(),
133 while (! m_conn_pool.empty()) {
135 if (prune_connections() == 0)
138 PION_LOG_INFO(
m_logger,
"Waiting for open connections to finish");
147 m_server_has_stopped.notify_all();
153 boost::mutex::scoped_lock server_lock(m_mutex);
154 while (m_is_listening) {
156 m_server_has_stopped.wait(server_lock);
165 m_ssl_context.set_options(boost::asio::ssl::context::default_workarounds
166 | boost::asio::ssl::context::no_sslv2
167 | boost::asio::ssl::context::single_dh_use);
168 m_ssl_context.use_certificate_file(pem_key_file, boost::asio::ssl::context::pem);
169 m_ssl_context.use_private_key_file(pem_key_file, boost::asio::ssl::context::pem);
173 void server::listen(
void)
176 boost::mutex::scoped_lock server_lock(m_mutex);
178 if (m_is_listening) {
181 m_ssl_context, m_ssl_flag,
182 boost::bind(&server::finish_connection,
189 m_conn_pool.insert(new_connection);
192 new_connection->async_accept(m_tcp_acceptor,
193 boost::bind(&server::handle_accept,
194 this, new_connection,
195 boost::asio::placeholders::error));
199 void server::handle_accept(
const tcp::connection_ptr& tcp_conn,
200 const boost::system::error_code& accept_error)
205 if (m_is_listening) {
207 PION_LOG_WARN(
m_logger,
"Accept error on port " <<
get_port() <<
": " << accept_error.message());
209 finish_connection(tcp_conn);
212 PION_LOG_DEBUG(
m_logger,
"New" << (tcp_conn->get_ssl_flag() ?
" SSL " :
" ")
213 <<
"connection on port " <<
get_port());
217 if (m_is_listening) listen();
221 if (tcp_conn->get_ssl_flag()) {
222 tcp_conn->async_handshake_server(boost::bind(&server::handle_ssl_handshake,
224 boost::asio::placeholders::error));
232 void server::handle_ssl_handshake(
const tcp::connection_ptr& tcp_conn,
233 const boost::system::error_code& handshake_error)
235 if (handshake_error) {
238 <<
" (" << handshake_error.message() <<
')');
239 finish_connection(tcp_conn);
247 void server::finish_connection(
const tcp::connection_ptr& tcp_conn)
249 boost::mutex::scoped_lock server_lock(m_mutex);
250 if (m_is_listening && tcp_conn->get_keep_alive()) {
259 ConnectionPool::iterator conn_itr = m_conn_pool.find(tcp_conn);
260 if (conn_itr != m_conn_pool.end())
261 m_conn_pool.erase(conn_itr);
264 if (!m_is_listening && m_conn_pool.empty())
265 m_no_more_connections.notify_all();
269 std::size_t server::prune_connections(
void)
272 ConnectionPool::iterator conn_itr = m_conn_pool.begin();
273 while (conn_itr != m_conn_pool.end()) {
274 if (conn_itr->unique()) {
275 PION_LOG_WARN(
m_logger,
"Closing orphaned connection on port " <<
get_port());
276 ConnectionPool::iterator erase_itr = conn_itr;
278 (*erase_itr)->close();
279 m_conn_pool.erase(erase_itr);
286 return m_conn_pool.size();
291 boost::mutex::scoped_lock server_lock(m_mutex);
292 return (m_is_listening ? (m_conn_pool.size() - 1) : m_conn_pool.size());
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)
void join(void)
the calling thread will sleep until the server has stopped listening for connections ...
boost::asio::io_service & get_io_service(void)
returns an async I/O service used to schedule work
void start(void)
starts listening for new connections
void set_ssl_key_file(const std::string &pem_key_file)
virtual void handle_connection(const tcp::connection_ptr &tcp_conn)
void remove_active_user(void)
unregisters an active user with the thread scheduler
void close(void)
closes the tcp socket and cancels any pending asynchronous operations
unsigned int get_port(void) const
returns tcp port number that the server listens for connections on
static void sleep(boost::uint32_t sleep_sec, boost::uint32_t sleep_nsec)
virtual void before_starting(void)
called before the TCP server starts listening for new connections
std::size_t get_connections(void) const
returns the number of active tcp connections
void set_ssl_flag(bool b=true)
sets value of SSL flag (true if the server uses SSL to encrypt connections)
server(const unsigned int tcp_port)
virtual void after_stopping(void)
called after the TCP server has stopped listing for new connections
logger m_logger
primary logging interface used by this class
void add_active_user(void)
void stop(bool wait_until_finished=false)