net/include/pion/net/TCPConnection.hpp

00001 // ------------------------------------------------------------------
00002 // pion-net: a C++ framework for building lightweight HTTP interfaces
00003 // ------------------------------------------------------------------
00004 // Copyright (C) 2007-2008 Atomic Labs, Inc.  (http://www.atomiclabs.com)
00005 //
00006 // Distributed under the Boost Software License, Version 1.0.
00007 // See http://www.boost.org/LICENSE_1_0.txt
00008 //
00009 
00010 #ifndef __PION_TCPCONNECTION_HEADER__
00011 #define __PION_TCPCONNECTION_HEADER__
00012 
00013 #ifdef PION_HAVE_SSL
00014     #ifdef PION_XCODE
00015         // ignore openssl warnings if building with XCode
00016         #pragma GCC system_header
00017     #endif
00018     #include <boost/asio/ssl.hpp>
00019     #if defined _MSC_VER
00020         #if defined _DEBUG
00021             #pragma comment(lib, "ssleay32d")
00022             #pragma comment(lib, "libeay32d")
00023         #else
00024             #pragma comment(lib, "ssleay32")
00025             #pragma comment(lib, "libeay32")
00026         #endif
00027     #endif 
00028 #endif
00029 
00030 #include <boost/noncopyable.hpp>
00031 #include <boost/shared_ptr.hpp>
00032 #include <boost/lexical_cast.hpp>
00033 #include <boost/enable_shared_from_this.hpp>
00034 #include <boost/asio.hpp>
00035 #include <boost/array.hpp>
00036 #include <boost/function.hpp>
00037 #include <boost/function/function1.hpp>
00038 #include <pion/PionConfig.hpp>
00039 #include <string>
00040 
00041 
00042 namespace pion {    // begin namespace pion
00043 namespace net {     // begin namespace net (Pion Network Library)
00044 
00048 class TCPConnection :
00049     public boost::enable_shared_from_this<TCPConnection>,
00050     private boost::noncopyable
00051 {
00052 public:
00053 
00055     enum LifecycleType {
00056         LIFECYCLE_CLOSE, LIFECYCLE_KEEPALIVE, LIFECYCLE_PIPELINED
00057     };
00058     
00060     enum { READ_BUFFER_SIZE = 8192 };
00061     
00063     typedef boost::function1<void, boost::shared_ptr<TCPConnection> >   ConnectionHandler;
00064     
00066     typedef boost::array<char, READ_BUFFER_SIZE>    ReadBuffer;
00067     
00069     typedef boost::asio::ip::tcp::socket            Socket;
00070 
00071 #ifdef PION_HAVE_SSL
00073     typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket>  SSLSocket;
00074 
00076     typedef boost::asio::ssl::context                               SSLContext;
00077 #else
00078     typedef Socket  SSLSocket;
00079     typedef int     SSLContext;
00080 #endif
00081 
00082     
00092     static inline boost::shared_ptr<TCPConnection> create(boost::asio::io_service& io_service,
00093                                                           SSLContext& ssl_context,
00094                                                           const bool ssl_flag,
00095                                                           ConnectionHandler finished_handler)
00096     {
00097         return boost::shared_ptr<TCPConnection>(new TCPConnection(io_service, ssl_context,
00098                                                                   ssl_flag, finished_handler));
00099     }
00100     
00107     explicit TCPConnection(boost::asio::io_service& io_service, const bool ssl_flag = false)
00108         : m_tcp_socket(io_service),
00109 #ifdef PION_HAVE_SSL
00110         m_ssl_context(io_service, boost::asio::ssl::context::sslv23),
00111         m_ssl_socket(io_service, m_ssl_context),
00112         m_ssl_flag(ssl_flag),
00113 #else
00114         m_ssl_context(0),
00115         m_ssl_socket(io_service),
00116         m_ssl_flag(false),
00117 #endif
00118         m_lifecycle(LIFECYCLE_CLOSE)
00119     {
00120         saveReadPosition(NULL, NULL);
00121     }
00122     
00129     TCPConnection(boost::asio::io_service& io_service, SSLContext& ssl_context)
00130         : m_tcp_socket(io_service),
00131 #ifdef PION_HAVE_SSL
00132         m_ssl_context(io_service, boost::asio::ssl::context::sslv23),
00133         m_ssl_socket(io_service, ssl_context), m_ssl_flag(true),
00134 #else
00135         m_ssl_context(0),
00136         m_ssl_socket(io_service), m_ssl_flag(false), 
00137 #endif
00138         m_lifecycle(LIFECYCLE_CLOSE)
00139     {
00140         saveReadPosition(NULL, NULL);
00141     }
00142     
00144     inline bool is_open(void) const {
00145 #ifdef PION_HAVE_SSL
00146         if (getSSLFlag())
00147             return const_cast<SSLSocket&>(m_ssl_socket).lowest_layer().is_open();
00148         else 
00149 #endif
00150             return m_tcp_socket.is_open();
00151     }
00152     
00154     inline void close(void) {
00155 #ifdef PION_HAVE_SSL
00156         if (getSSLFlag()) {
00157             if (m_ssl_socket.lowest_layer().is_open())
00158                 m_ssl_socket.lowest_layer().close();
00159         } else
00160 #endif
00161         {
00162             if (m_tcp_socket.is_open())
00163                 m_tcp_socket.close();
00164         }
00165     }
00166 
00167     /*
00168     Use close instead; basic_socket::cancel is deprecated for Windows XP.
00169 
00171     inline void cancel(void) {
00172 #ifdef PION_HAVE_SSL
00173         if (getSSLFlag())
00174             m_ssl_socket.lowest_layer().cancel();
00175         else
00176 #endif
00177             m_tcp_socket.cancel();
00178     }
00179     */
00180     
00182     virtual ~TCPConnection() { close(); }
00183     
00192     template <typename AcceptHandler>
00193     inline void async_accept(boost::asio::ip::tcp::acceptor& tcp_acceptor,
00194                              AcceptHandler handler)
00195     {
00196 #ifdef PION_HAVE_SSL
00197         if (getSSLFlag())
00198             tcp_acceptor.async_accept(m_ssl_socket.lowest_layer(), handler);
00199         else
00200 #endif      
00201             tcp_acceptor.async_accept(m_tcp_socket, handler);
00202     }
00203 
00212     inline boost::system::error_code accept(boost::asio::ip::tcp::acceptor& tcp_acceptor)
00213     {
00214         boost::system::error_code ec;
00215 #ifdef PION_HAVE_SSL
00216         if (getSSLFlag())
00217             tcp_acceptor.accept(m_ssl_socket.lowest_layer(), ec);
00218         else
00219 #endif      
00220             tcp_acceptor.accept(m_tcp_socket, ec);
00221         return ec;
00222     }
00223     
00232     template <typename ConnectHandler>
00233     inline void async_connect(boost::asio::ip::tcp::endpoint& tcp_endpoint,
00234                               ConnectHandler handler)
00235     {
00236 #ifdef PION_HAVE_SSL
00237         if (getSSLFlag())
00238             m_ssl_socket.lowest_layer().async_connect(tcp_endpoint, handler);
00239         else
00240 #endif      
00241             m_tcp_socket.async_connect(tcp_endpoint, handler);
00242     }
00243 
00253     template <typename ConnectHandler>
00254     inline void async_connect(const boost::asio::ip::address& remote_addr,
00255                               const unsigned int remote_port,
00256                               ConnectHandler handler)
00257     {
00258         boost::asio::ip::tcp::endpoint tcp_endpoint(remote_addr, remote_port);
00259         async_connect(tcp_endpoint, handler);
00260     }
00261     
00270     inline boost::system::error_code connect(boost::asio::ip::tcp::endpoint& tcp_endpoint)
00271     {
00272         boost::system::error_code ec;
00273 #ifdef PION_HAVE_SSL
00274         if (getSSLFlag())
00275             m_ssl_socket.lowest_layer().connect(tcp_endpoint, ec);
00276         else
00277 #endif      
00278             m_tcp_socket.connect(tcp_endpoint, ec);
00279         return ec;
00280     }
00281 
00291     inline boost::system::error_code connect(const boost::asio::ip::address& remote_addr,
00292                                              const unsigned int remote_port)
00293     {
00294         boost::asio::ip::tcp::endpoint tcp_endpoint(remote_addr, remote_port);
00295         return connect(tcp_endpoint);
00296     }
00297     
00307     inline boost::system::error_code connect(const std::string& remote_server,
00308                                              const unsigned int remote_port)
00309     {
00310         // query a list of matching endpoints
00311         boost::system::error_code ec;
00312         boost::asio::ip::tcp::resolver resolver(m_tcp_socket.get_io_service());
00313         boost::asio::ip::tcp::resolver::query query(remote_server,
00314             boost::lexical_cast<std::string>(remote_port),
00315             boost::asio::ip::tcp::resolver::query::numeric_service);
00316         boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query, ec);
00317         if (ec)
00318             return ec;
00319 
00320         // try each one until we are successful
00321         ec = boost::asio::error::host_not_found;
00322         boost::asio::ip::tcp::resolver::iterator end;
00323         while (ec && endpoint_iterator != end) {
00324             boost::asio::ip::tcp::endpoint ep(endpoint_iterator->endpoint());
00325             ++endpoint_iterator;
00326             ec = connect(ep);
00327             if (ec)
00328                 close();
00329         }
00330 
00331         return ec;
00332     }
00333     
00341     template <typename SSLHandshakeHandler>
00342     inline void async_handshake_client(SSLHandshakeHandler handler) {
00343 #ifdef PION_HAVE_SSL
00344         if (getSSLFlag())
00345             m_ssl_socket.async_handshake(boost::asio::ssl::stream_base::client, handler);
00346 #endif
00347     }
00348 
00356     template <typename SSLHandshakeHandler>
00357     inline void async_handshake_server(SSLHandshakeHandler handler) {
00358 #ifdef PION_HAVE_SSL
00359         if (getSSLFlag())
00360             m_ssl_socket.async_handshake(boost::asio::ssl::stream_base::server, handler);
00361 #endif
00362     }
00363     
00371     inline boost::system::error_code handshake_client(void) {
00372         boost::system::error_code ec;
00373 #ifdef PION_HAVE_SSL
00374         if (getSSLFlag())
00375             m_ssl_socket.handshake(boost::asio::ssl::stream_base::client, ec);
00376 #endif
00377         return ec;
00378     }
00379 
00387     inline boost::system::error_code handshake_server(void) {
00388         boost::system::error_code ec;
00389 #ifdef PION_HAVE_SSL
00390         if (getSSLFlag())
00391             m_ssl_socket.handshake(boost::asio::ssl::stream_base::server, ec);
00392 #endif
00393         return ec;
00394     }
00395     
00403     template <typename ReadHandler>
00404     inline void async_read_some(ReadHandler handler) {
00405 #ifdef PION_HAVE_SSL
00406         if (getSSLFlag())
00407             m_ssl_socket.async_read_some(boost::asio::buffer(m_read_buffer),
00408                                          handler);
00409         else
00410 #endif      
00411             m_tcp_socket.async_read_some(boost::asio::buffer(m_read_buffer),
00412                                          handler);
00413     }
00414     
00423     template <typename ReadBufferType, typename ReadHandler>
00424     inline void async_read_some(ReadBufferType read_buffer,
00425                                 ReadHandler handler) {
00426 #ifdef PION_HAVE_SSL
00427         if (getSSLFlag())
00428             m_ssl_socket.async_read_some(read_buffer, handler);
00429         else
00430 #endif      
00431             m_tcp_socket.async_read_some(read_buffer, handler);
00432     }
00433     
00442     inline std::size_t read_some(boost::system::error_code& ec) {
00443 #ifdef PION_HAVE_SSL
00444         if (getSSLFlag())
00445             return m_ssl_socket.read_some(boost::asio::buffer(m_read_buffer), ec);
00446         else
00447 #endif      
00448             return m_tcp_socket.read_some(boost::asio::buffer(m_read_buffer), ec);
00449     }
00450     
00460     template <typename ReadBufferType>
00461     inline std::size_t read_some(ReadBufferType read_buffer,
00462                                  boost::system::error_code& ec)
00463     {
00464 #ifdef PION_HAVE_SSL
00465         if (getSSLFlag())
00466             return m_ssl_socket.read_some(read_buffer, ec);
00467         else
00468 #endif      
00469             return m_tcp_socket.read_some(read_buffer, ec);
00470     }
00471     
00481     template <typename CompletionCondition, typename ReadHandler>
00482     inline void async_read(CompletionCondition completion_condition,
00483                            ReadHandler handler)
00484     {
00485 #ifdef PION_HAVE_SSL
00486         if (getSSLFlag())
00487             boost::asio::async_read(m_ssl_socket, boost::asio::buffer(m_read_buffer),
00488                                     completion_condition, handler);
00489         else
00490 #endif      
00491             boost::asio::async_read(m_tcp_socket, boost::asio::buffer(m_read_buffer),
00492                                     completion_condition, handler);
00493     }
00494             
00505     template <typename MutableBufferSequence, typename CompletionCondition, typename ReadHandler>
00506     inline void async_read(const MutableBufferSequence& buffers,
00507                            CompletionCondition completion_condition,
00508                            ReadHandler handler)
00509     {
00510 #ifdef PION_HAVE_SSL
00511         if (getSSLFlag())
00512             boost::asio::async_read(m_ssl_socket, buffers,
00513                                     completion_condition, handler);
00514         else
00515 #endif      
00516             boost::asio::async_read(m_tcp_socket, buffers,
00517                                     completion_condition, handler);
00518     }
00519     
00530     template <typename CompletionCondition>
00531     inline std::size_t read(CompletionCondition completion_condition,
00532                             boost::system::error_code& ec)
00533     {
00534 #ifdef PION_HAVE_SSL
00535         if (getSSLFlag())
00536             return boost::asio::async_read(m_ssl_socket, boost::asio::buffer(m_read_buffer),
00537                                            completion_condition, ec);
00538         else
00539 #endif      
00540             return boost::asio::async_read(m_tcp_socket, boost::asio::buffer(m_read_buffer),
00541                                            completion_condition, ec);
00542     }
00543     
00555     template <typename MutableBufferSequence, typename CompletionCondition>
00556     inline std::size_t read(const MutableBufferSequence& buffers,
00557                             CompletionCondition completion_condition,
00558                             boost::system::error_code& ec)
00559     {
00560 #ifdef PION_HAVE_SSL
00561         if (getSSLFlag())
00562             return boost::asio::read(m_ssl_socket, buffers,
00563                                      completion_condition, ec);
00564         else
00565 #endif      
00566             return boost::asio::read(m_tcp_socket, buffers,
00567                                      completion_condition, ec);
00568     }
00569     
00578     template <typename ConstBufferSequence, typename WriteHandler>
00579     inline void async_write(const ConstBufferSequence& buffers, WriteHandler handler) {
00580 #ifdef PION_HAVE_SSL
00581         if (getSSLFlag())
00582             boost::asio::async_write(m_ssl_socket, buffers, handler);
00583         else
00584 #endif      
00585             boost::asio::async_write(m_tcp_socket, buffers, handler);
00586     }   
00587         
00597     template <typename ConstBufferSequence>
00598     inline std::size_t write(const ConstBufferSequence& buffers,
00599                              boost::system::error_code ec)
00600     {
00601 #ifdef PION_HAVE_SSL
00602         if (getSSLFlag())
00603             return boost::asio::write(m_ssl_socket, buffers,
00604                                       boost::asio::transfer_all(), ec);
00605         else
00606 #endif      
00607             return boost::asio::write(m_tcp_socket, buffers,
00608                                       boost::asio::transfer_all(), ec);
00609     }   
00610     
00611     
00614     inline void finish(void) { if (m_finished_handler) m_finished_handler(shared_from_this()); }
00615 
00617     inline bool getSSLFlag(void) const { return m_ssl_flag; }
00618 
00620     inline void setLifecycle(LifecycleType t) { m_lifecycle = t; }
00621     
00623     inline LifecycleType getLifecycle(void) const { return m_lifecycle; }
00624     
00626     inline bool getKeepAlive(void) const { return m_lifecycle != LIFECYCLE_CLOSE; }
00627     
00629     inline bool getPipelined(void) const { return m_lifecycle == LIFECYCLE_PIPELINED; }
00630 
00632     inline ReadBuffer& getReadBuffer(void) { return m_read_buffer; }
00633     
00640     inline void saveReadPosition(const char *read_ptr, const char *read_end_ptr) {
00641         m_read_position.first = read_ptr;
00642         m_read_position.second = read_end_ptr;
00643     }
00644     
00651     inline void loadReadPosition(const char *&read_ptr, const char *&read_end_ptr) const {
00652         read_ptr = m_read_position.first;
00653         read_end_ptr = m_read_position.second;
00654     }
00655 
00657     inline boost::asio::ip::tcp::endpoint getRemoteEndpoint(void) const {
00658         boost::asio::ip::tcp::endpoint remote_endpoint;
00659         try {
00660 #ifdef PION_HAVE_SSL
00661             if (getSSLFlag())
00662                 // const_cast is required since lowest_layer() is only defined non-const in asio
00663                 remote_endpoint = const_cast<SSLSocket&>(m_ssl_socket).lowest_layer().remote_endpoint();
00664             else
00665 #endif
00666                 remote_endpoint = m_tcp_socket.remote_endpoint();
00667         } catch (boost::system::system_error& /* e */) {
00668             // do nothing
00669         }
00670         return remote_endpoint;
00671     }
00672 
00674     inline boost::asio::ip::address getRemoteIp(void) const {
00675         return getRemoteEndpoint().address();
00676     }
00677 
00679     inline unsigned short getRemotePort(void) const {
00680         return getRemoteEndpoint().port();
00681     }
00682     
00683     
00684 protected:
00685         
00695     TCPConnection(boost::asio::io_service& io_service,
00696                   SSLContext& ssl_context,
00697                   const bool ssl_flag,
00698                   ConnectionHandler finished_handler)
00699         : m_tcp_socket(io_service),
00700 #ifdef PION_HAVE_SSL
00701         m_ssl_context(io_service, boost::asio::ssl::context::sslv23),
00702         m_ssl_socket(io_service, ssl_context), m_ssl_flag(ssl_flag),
00703 #else
00704         m_ssl_context(0),
00705         m_ssl_socket(io_service), m_ssl_flag(false), 
00706 #endif
00707         m_lifecycle(LIFECYCLE_CLOSE),
00708         m_finished_handler(finished_handler)
00709     {
00710         saveReadPosition(NULL, NULL);
00711     }
00712     
00713 
00714 private:
00715 
00717     typedef std::pair<const char*, const char*>     ReadPosition;
00718 
00719     
00721     Socket                      m_tcp_socket;
00722     
00724     SSLContext                  m_ssl_context;
00725 
00727     SSLSocket                   m_ssl_socket;
00728 
00730     const bool                  m_ssl_flag;
00731 
00733     ReadBuffer                  m_read_buffer;
00734     
00736     ReadPosition                m_read_position;
00737     
00739     LifecycleType               m_lifecycle;
00740 
00742     ConnectionHandler           m_finished_handler;
00743 };
00744 
00745 
00747 typedef boost::shared_ptr<TCPConnection>    TCPConnectionPtr;
00748 
00749 
00750 }   // end namespace net
00751 }   // end namespace pion
00752 
00753 #endif

Generated on Fri Dec 4 08:54:29 2009 for pion-net by  doxygen 1.4.7