root/lite_comet/dependencies/pion-net-3.0.17/net/src/TCPServer.cpp

Revision 84, 9.3 kB (checked in by ctoa, 3 years ago)

Added lite_comet.

Line 
1 // ------------------------------------------------------------------
2 // pion-net: a C++ framework for building lightweight HTTP interfaces
3 // ------------------------------------------------------------------
4 // Copyright (C) 2007-2008 Atomic Labs, Inc.  (http://www.atomiclabs.com)
5 //
6 // Distributed under the Boost Software License, Version 1.0.
7 // See http://www.boost.org/LICENSE_1_0.txt
8 //
9
10 #include <boost/asio.hpp>
11 #include <boost/bind.hpp>
12 #include <boost/thread/mutex.hpp>
13 #include <pion/PionAdminRights.hpp>
14 #include <pion/net/TCPServer.hpp>
15
16 using boost::asio::ip::tcp;
17
18
19 namespace pion {        // begin namespace pion
20 namespace net {         // begin namespace net (Pion Network Library)
21
22        
23 // TCPServer member functions
24
25 TCPServer::TCPServer(PionScheduler& scheduler, const unsigned int tcp_port)
26         : m_logger(PION_GET_LOGGER("pion.net.TCPServer")),
27         m_active_scheduler(scheduler),
28         m_tcp_acceptor(m_active_scheduler.getIOService()),
29 #ifdef PION_HAVE_SSL
30         m_ssl_context(m_active_scheduler.getIOService(), boost::asio::ssl::context::sslv23),
31 #else
32         m_ssl_context(0),
33 #endif
34         m_endpoint(tcp::v4(), tcp_port), m_ssl_flag(false), m_is_listening(false)
35 {}
36        
37 TCPServer::TCPServer(PionScheduler& scheduler, const tcp::endpoint& endpoint)
38         : m_logger(PION_GET_LOGGER("pion.net.TCPServer")),
39         m_active_scheduler(scheduler),
40         m_tcp_acceptor(m_active_scheduler.getIOService()),
41 #ifdef PION_HAVE_SSL
42         m_ssl_context(m_active_scheduler.getIOService(), boost::asio::ssl::context::sslv23),
43 #else
44         m_ssl_context(0),
45 #endif
46         m_endpoint(endpoint), m_ssl_flag(false), m_is_listening(false)
47 {}
48
49 TCPServer::TCPServer(const unsigned int tcp_port)
50         : m_logger(PION_GET_LOGGER("pion.net.TCPServer")),
51         m_default_scheduler(), m_active_scheduler(m_default_scheduler),
52         m_tcp_acceptor(m_active_scheduler.getIOService()),
53 #ifdef PION_HAVE_SSL
54         m_ssl_context(m_active_scheduler.getIOService(), boost::asio::ssl::context::sslv23),
55 #else
56         m_ssl_context(0),
57 #endif
58         m_endpoint(tcp::v4(), tcp_port), m_ssl_flag(false), m_is_listening(false)
59 {}
60
61 TCPServer::TCPServer(const tcp::endpoint& endpoint)
62         : m_logger(PION_GET_LOGGER("pion.net.TCPServer")),
63         m_default_scheduler(), m_active_scheduler(m_default_scheduler),
64         m_tcp_acceptor(m_active_scheduler.getIOService()),
65 #ifdef PION_HAVE_SSL
66         m_ssl_context(m_active_scheduler.getIOService(), boost::asio::ssl::context::sslv23),
67 #else
68         m_ssl_context(0),
69 #endif
70         m_endpoint(endpoint), m_ssl_flag(false), m_is_listening(false)
71 {}
72        
73 void TCPServer::start(void)
74 {
75         // lock mutex for thread safety
76         boost::mutex::scoped_lock server_lock(m_mutex);
77
78         if (! m_is_listening) {
79                 PION_LOG_INFO(m_logger, "Starting server on port " << getPort());
80                
81                 beforeStarting();
82
83                 // configure the acceptor service
84                 try {
85                         // get admin permissions in case we're binding to a privileged port
86                         pion::PionAdminRights use_admin_rights(getPort() < 1024);
87                         m_tcp_acceptor.open(m_endpoint.protocol());
88                         // allow the acceptor to reuse the address (i.e. SO_REUSEADDR)
89                         // ...except when running not on Windows - see http://msdn.microsoft.com/en-us/library/ms740621%28VS.85%29.aspx
90 #ifndef _MSC_VER
91                         m_tcp_acceptor.set_option(tcp::acceptor::reuse_address(true));
92 #endif
93                         m_tcp_acceptor.bind(m_endpoint);
94                         if (m_endpoint.port() == 0) {
95                                 // update the endpoint to reflect the port chosen by bind
96                                 m_endpoint = m_tcp_acceptor.local_endpoint();
97                         }
98                         m_tcp_acceptor.listen();
99                 } catch (std::exception& e) {
100                         PION_LOG_ERROR(m_logger, "Unable to bind to port " << getPort() << ": " << e.what());
101                         throw;
102                 }
103
104                 m_is_listening = true;
105
106                 // unlock the mutex since listen() requires its own lock
107                 server_lock.unlock();
108                 listen();
109                
110                 // notify the thread scheduler that we need it now
111                 m_active_scheduler.addActiveUser();
112         }
113 }
114
115 void TCPServer::stop(bool wait_until_finished)
116 {
117         // lock mutex for thread safety
118         boost::mutex::scoped_lock server_lock(m_mutex);
119
120         if (m_is_listening) {
121                 PION_LOG_INFO(m_logger, "Shutting down server on port " << getPort());
122        
123                 m_is_listening = false;
124
125                 // this terminates any connections waiting to be accepted
126                 m_tcp_acceptor.close();
127                
128                 if (! wait_until_finished) {
129                         // this terminates any other open connections
130                         std::for_each(m_conn_pool.begin(), m_conn_pool.end(),
131                                                   boost::bind(&TCPConnection::close, _1));
132                 }
133        
134                 // wait for all pending connections to complete
135                 while (! m_conn_pool.empty()) {
136                         // try to prun connections that didn't finish cleanly
137                         if (pruneConnections() == 0)
138                                 break;  // if no more left, then we can stop waiting
139                         // sleep for up to a quarter second to give open connections a chance to finish
140                         PION_LOG_INFO(m_logger, "Waiting for open connections to finish");
141                         PionScheduler::sleep(m_no_more_connections, server_lock, 0, 250000000);
142                 }
143                
144                 // notify the thread scheduler that we no longer need it
145                 m_active_scheduler.removeActiveUser();
146                
147                 // all done!
148                 afterStopping();
149                 m_server_has_stopped.notify_all();
150         }
151 }
152
153 void TCPServer::join(void)
154 {
155         boost::mutex::scoped_lock server_lock(m_mutex);
156         while (m_is_listening) {
157                 // sleep until server_has_stopped condition is signaled
158                 m_server_has_stopped.wait(server_lock);
159         }
160 }
161
162 void TCPServer::setSSLKeyFile(const std::string& pem_key_file)
163 {
164         // configure server for SSL
165         setSSLFlag(true);
166 #ifdef PION_HAVE_SSL
167         m_ssl_context.set_options(boost::asio::ssl::context::default_workarounds
168                                                           | boost::asio::ssl::context::no_sslv2
169                                                           | boost::asio::ssl::context::single_dh_use);
170         m_ssl_context.use_certificate_file(pem_key_file, boost::asio::ssl::context::pem);
171         m_ssl_context.use_private_key_file(pem_key_file, boost::asio::ssl::context::pem);
172 #endif
173 }
174
175 void TCPServer::listen(void)
176 {
177         // lock mutex for thread safety
178         boost::mutex::scoped_lock server_lock(m_mutex);
179        
180         if (m_is_listening) {
181                 // create a new TCP connection object
182                 TCPConnectionPtr new_connection(TCPConnection::create(getIOService(),
183                                                                                                                           m_ssl_context, m_ssl_flag,
184                                                                                                                           boost::bind(&TCPServer::finishConnection,
185                                                                                                                                                   this, _1)));
186                
187                 // prune connections that finished uncleanly
188                 pruneConnections();
189
190                 // keep track of the object in the server's connection pool
191                 m_conn_pool.insert(new_connection);
192                
193                 // use the object to accept a new connection
194                 new_connection->async_accept(m_tcp_acceptor,
195                                                                          boost::bind(&TCPServer::handleAccept,
196                                                                                                  this, new_connection,
197                                                                                                  boost::asio::placeholders::error));
198         }
199 }
200
201 void TCPServer::handleAccept(TCPConnectionPtr& tcp_conn,
202                                                          const boost::system::error_code& accept_error)
203 {
204         if (accept_error) {
205                 // an error occured while trying to a accept a new connection
206                 // this happens when the server is being shut down
207                 if (m_is_listening) {
208                         listen();       // schedule acceptance of another connection
209                         PION_LOG_WARN(m_logger, "Accept error on port " << getPort() << ": " << accept_error.message());
210                 }
211                 finishConnection(tcp_conn);
212         } else {
213                 // got a new TCP connection
214                 PION_LOG_DEBUG(m_logger, "New" << (tcp_conn->getSSLFlag() ? " SSL " : " ")
215                                            << "connection on port " << getPort());
216
217                 // schedule the acceptance of another new connection
218                 // (this returns immediately since it schedules it as an event)
219                 if (m_is_listening) listen();
220                
221                 // handle the new connection
222 #ifdef PION_HAVE_SSL
223                 if (tcp_conn->getSSLFlag()) {
224                         tcp_conn->async_handshake_server(boost::bind(&TCPServer::handleSSLHandshake,
225                                                                                                                  this, tcp_conn,
226                                                                                                                  boost::asio::placeholders::error));
227                 } else
228 #endif
229                         // not SSL -> call the handler immediately
230                         handleConnection(tcp_conn);
231         }
232 }
233
234 void TCPServer::handleSSLHandshake(TCPConnectionPtr& tcp_conn,
235                                                                    const boost::system::error_code& handshake_error)
236 {
237         if (handshake_error) {
238                 // an error occured while trying to establish the SSL connection
239                 PION_LOG_WARN(m_logger, "SSL handshake failed on port " << getPort()
240                                           << " (" << handshake_error.message() << ')');
241                 finishConnection(tcp_conn);
242         } else {
243                 // handle the new connection
244                 PION_LOG_DEBUG(m_logger, "SSL handshake succeeded on port " << getPort());
245                 handleConnection(tcp_conn);
246         }
247 }
248
249 void TCPServer::finishConnection(TCPConnectionPtr& tcp_conn)
250 {
251         boost::mutex::scoped_lock server_lock(m_mutex);
252         if (m_is_listening && tcp_conn->getKeepAlive()) {
253                
254                 // keep the connection alive
255                 handleConnection(tcp_conn);
256
257         } else {
258                 PION_LOG_DEBUG(m_logger, "Closing connection on port " << getPort());
259                
260                 // remove the connection from the server's management pool
261                 ConnectionPool::iterator conn_itr = m_conn_pool.find(tcp_conn);
262                 if (conn_itr != m_conn_pool.end())
263                         m_conn_pool.erase(conn_itr);
264
265                 // trigger the no more connections condition if we're waiting to stop
266                 if (!m_is_listening && m_conn_pool.empty())
267                         m_no_more_connections.notify_all();
268         }
269 }
270
271 std::size_t TCPServer::pruneConnections(void)
272 {
273         // assumes that a server lock has already been acquired
274         ConnectionPool::iterator conn_itr = m_conn_pool.begin();
275         while (conn_itr != m_conn_pool.end()) {
276                 if (conn_itr->unique()) {
277                         PION_LOG_WARN(m_logger, "Closing orphaned connection on port " << getPort());
278                         ConnectionPool::iterator erase_itr = conn_itr;
279                         ++conn_itr;
280                         (*erase_itr)->close();
281                         m_conn_pool.erase(erase_itr);
282                 } else {
283                         ++conn_itr;
284                 }
285         }
286
287         // return the number of connections remaining
288         return m_conn_pool.size();
289 }
290
291 std::size_t TCPServer::getConnections(void) const
292 {
293         boost::mutex::scoped_lock server_lock(m_mutex);
294         return (m_is_listening ? (m_conn_pool.size() - 1) : m_conn_pool.size());
295 }
296
297 }       // end namespace net
298 }       // end namespace pion
299
Note: See TracBrowser for help on using the browser.