00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include <boost/asio.hpp>
00011 #include <boost/bind.hpp>
00012 #include <boost/thread/mutex.hpp>
00013 #include <pion/admin_rights.hpp>
00014 #include <pion/tcp/server.hpp>
00015
00016
00017 namespace pion {
00018 namespace tcp {
00019
00020
00021
00022
00023 server::server(scheduler& sched, const unsigned int tcp_port)
00024 : m_logger(PION_GET_LOGGER("pion.tcp.server")),
00025 m_active_scheduler(sched),
00026 m_tcp_acceptor(m_active_scheduler.get_io_service()),
00027 #ifdef PION_HAVE_SSL
00028 m_ssl_context(m_active_scheduler.get_io_service(), boost::asio::ssl::context::sslv23),
00029 #else
00030 m_ssl_context(0),
00031 #endif
00032 m_endpoint(boost::asio::ip::tcp::v4(), tcp_port), m_ssl_flag(false), m_is_listening(false)
00033 {}
00034
00035 server::server(scheduler& sched, const boost::asio::ip::tcp::endpoint& endpoint)
00036 : m_logger(PION_GET_LOGGER("pion.tcp.server")),
00037 m_active_scheduler(sched),
00038 m_tcp_acceptor(m_active_scheduler.get_io_service()),
00039 #ifdef PION_HAVE_SSL
00040 m_ssl_context(m_active_scheduler.get_io_service(), boost::asio::ssl::context::sslv23),
00041 #else
00042 m_ssl_context(0),
00043 #endif
00044 m_endpoint(endpoint), m_ssl_flag(false), m_is_listening(false)
00045 {}
00046
00047 server::server(const unsigned int tcp_port)
00048 : m_logger(PION_GET_LOGGER("pion.tcp.server")),
00049 m_default_scheduler(), m_active_scheduler(m_default_scheduler),
00050 m_tcp_acceptor(m_active_scheduler.get_io_service()),
00051 #ifdef PION_HAVE_SSL
00052 m_ssl_context(m_active_scheduler.get_io_service(), boost::asio::ssl::context::sslv23),
00053 #else
00054 m_ssl_context(0),
00055 #endif
00056 m_endpoint(boost::asio::ip::tcp::v4(), tcp_port), m_ssl_flag(false), m_is_listening(false)
00057 {}
00058
00059 server::server(const boost::asio::ip::tcp::endpoint& endpoint)
00060 : m_logger(PION_GET_LOGGER("pion.tcp.server")),
00061 m_default_scheduler(), m_active_scheduler(m_default_scheduler),
00062 m_tcp_acceptor(m_active_scheduler.get_io_service()),
00063 #ifdef PION_HAVE_SSL
00064 m_ssl_context(m_active_scheduler.get_io_service(), boost::asio::ssl::context::sslv23),
00065 #else
00066 m_ssl_context(0),
00067 #endif
00068 m_endpoint(endpoint), m_ssl_flag(false), m_is_listening(false)
00069 {}
00070
00071 void server::start(void)
00072 {
00073
00074 boost::mutex::scoped_lock server_lock(m_mutex);
00075
00076 if (! m_is_listening) {
00077 PION_LOG_INFO(m_logger, "Starting server on port " << get_port());
00078
00079 before_starting();
00080
00081
00082 try {
00083
00084 pion::admin_rights use_admin_rights(get_port() < 1024);
00085 m_tcp_acceptor.open(m_endpoint.protocol());
00086
00087
00088 #ifndef _MSC_VER
00089 m_tcp_acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
00090 #endif
00091 m_tcp_acceptor.bind(m_endpoint);
00092 if (m_endpoint.port() == 0) {
00093
00094 m_endpoint = m_tcp_acceptor.local_endpoint();
00095 }
00096 m_tcp_acceptor.listen();
00097 } catch (std::exception& e) {
00098 PION_LOG_ERROR(m_logger, "Unable to bind to port " << get_port() << ": " << e.what());
00099 throw;
00100 }
00101
00102 m_is_listening = true;
00103
00104
00105 server_lock.unlock();
00106 listen();
00107
00108
00109 m_active_scheduler.add_active_user();
00110 }
00111 }
00112
00113 void server::stop(bool wait_until_finished)
00114 {
00115
00116 boost::mutex::scoped_lock server_lock(m_mutex);
00117
00118 if (m_is_listening) {
00119 PION_LOG_INFO(m_logger, "Shutting down server on port " << get_port());
00120
00121 m_is_listening = false;
00122
00123
00124 m_tcp_acceptor.close();
00125
00126 if (! wait_until_finished) {
00127
00128 std::for_each(m_conn_pool.begin(), m_conn_pool.end(),
00129 boost::bind(&connection::close, _1));
00130 }
00131
00132
00133 while (! m_conn_pool.empty()) {
00134
00135 if (prune_connections() == 0)
00136 break;
00137
00138 PION_LOG_INFO(m_logger, "Waiting for open connections to finish");
00139 scheduler::sleep(m_no_more_connections, server_lock, 0, 250000000);
00140 }
00141
00142
00143 m_active_scheduler.remove_active_user();
00144
00145
00146 after_stopping();
00147 m_server_has_stopped.notify_all();
00148 }
00149 }
00150
00151 void server::join(void)
00152 {
00153 boost::mutex::scoped_lock server_lock(m_mutex);
00154 while (m_is_listening) {
00155
00156 m_server_has_stopped.wait(server_lock);
00157 }
00158 }
00159
00160 void server::set_ssl_key_file(const std::string& pem_key_file)
00161 {
00162
00163 set_ssl_flag(true);
00164 #ifdef PION_HAVE_SSL
00165 m_ssl_context.set_options(boost::asio::ssl::context::default_workarounds
00166 | boost::asio::ssl::context::no_sslv2
00167 | boost::asio::ssl::context::single_dh_use);
00168 m_ssl_context.use_certificate_file(pem_key_file, boost::asio::ssl::context::pem);
00169 m_ssl_context.use_private_key_file(pem_key_file, boost::asio::ssl::context::pem);
00170 #endif
00171 }
00172
00173 void server::listen(void)
00174 {
00175
00176 boost::mutex::scoped_lock server_lock(m_mutex);
00177
00178 if (m_is_listening) {
00179
00180 tcp::connection_ptr new_connection(connection::create(get_io_service(),
00181 m_ssl_context, m_ssl_flag,
00182 boost::bind(&server::finish_connection,
00183 this, _1)));
00184
00185
00186 prune_connections();
00187
00188
00189 m_conn_pool.insert(new_connection);
00190
00191
00192 new_connection->async_accept(m_tcp_acceptor,
00193 boost::bind(&server::handle_accept,
00194 this, new_connection,
00195 boost::asio::placeholders::error));
00196 }
00197 }
00198
00199 void server::handle_accept(tcp::connection_ptr& tcp_conn,
00200 const boost::system::error_code& accept_error)
00201 {
00202 if (accept_error) {
00203
00204
00205 if (m_is_listening) {
00206 listen();
00207 PION_LOG_WARN(m_logger, "Accept error on port " << get_port() << ": " << accept_error.message());
00208 }
00209 finish_connection(tcp_conn);
00210 } else {
00211
00212 PION_LOG_DEBUG(m_logger, "New" << (tcp_conn->get_ssl_flag() ? " SSL " : " ")
00213 << "connection on port " << get_port());
00214
00215
00216
00217 if (m_is_listening) listen();
00218
00219
00220 #ifdef PION_HAVE_SSL
00221 if (tcp_conn->get_ssl_flag()) {
00222 tcp_conn->async_handshake_server(boost::bind(&server::handle_ssl_handshake,
00223 this, tcp_conn,
00224 boost::asio::placeholders::error));
00225 } else
00226 #endif
00227
00228 handle_connection(tcp_conn);
00229 }
00230 }
00231
00232 void server::handle_ssl_handshake(tcp::connection_ptr& tcp_conn,
00233 const boost::system::error_code& handshake_error)
00234 {
00235 if (handshake_error) {
00236
00237 PION_LOG_WARN(m_logger, "SSL handshake failed on port " << get_port()
00238 << " (" << handshake_error.message() << ')');
00239 finish_connection(tcp_conn);
00240 } else {
00241
00242 PION_LOG_DEBUG(m_logger, "SSL handshake succeeded on port " << get_port());
00243 handle_connection(tcp_conn);
00244 }
00245 }
00246
00247 void server::finish_connection(tcp::connection_ptr& tcp_conn)
00248 {
00249 boost::mutex::scoped_lock server_lock(m_mutex);
00250 if (m_is_listening && tcp_conn->get_keep_alive()) {
00251
00252
00253 handle_connection(tcp_conn);
00254
00255 } else {
00256 PION_LOG_DEBUG(m_logger, "Closing connection on port " << get_port());
00257
00258
00259 ConnectionPool::iterator conn_itr = m_conn_pool.find(tcp_conn);
00260 if (conn_itr != m_conn_pool.end())
00261 m_conn_pool.erase(conn_itr);
00262
00263
00264 if (!m_is_listening && m_conn_pool.empty())
00265 m_no_more_connections.notify_all();
00266 }
00267 }
00268
00269 std::size_t server::prune_connections(void)
00270 {
00271
00272 ConnectionPool::iterator conn_itr = m_conn_pool.begin();
00273 while (conn_itr != m_conn_pool.end()) {
00274 if (conn_itr->unique()) {
00275 PION_LOG_WARN(m_logger, "Closing orphaned connection on port " << get_port());
00276 ConnectionPool::iterator erase_itr = conn_itr;
00277 ++conn_itr;
00278 (*erase_itr)->close();
00279 m_conn_pool.erase(erase_itr);
00280 } else {
00281 ++conn_itr;
00282 }
00283 }
00284
00285
00286 return m_conn_pool.size();
00287 }
00288
00289 std::size_t server::get_connections(void) const
00290 {
00291 boost::mutex::scoped_lock server_lock(m_mutex);
00292 return (m_is_listening ? (m_conn_pool.size() - 1) : m_conn_pool.size());
00293 }
00294
00295 }
00296 }