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