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 #endif
00020 
00021 #include <boost/noncopyable.hpp>
00022 #include <boost/shared_ptr.hpp>
00023 #include <boost/lexical_cast.hpp>
00024 #include <boost/enable_shared_from_this.hpp>
00025 #include <boost/asio.hpp>
00026 #include <boost/array.hpp>
00027 #include <boost/function.hpp>
00028 #include <boost/function/function1.hpp>
00029 #include <pion/PionConfig.hpp>
00030 #include <string>
00031 
00032 
00033 namespace pion {    // begin namespace pion
00034 namespace net {     // begin namespace net (Pion Network Library)
00035 
00039 class TCPConnection :
00040     public boost::enable_shared_from_this<TCPConnection>,
00041     private boost::noncopyable
00042 {
00043 public:
00044 
00046     enum LifecycleType {
00047         LIFECYCLE_CLOSE, LIFECYCLE_KEEPALIVE, LIFECYCLE_PIPELINED
00048     };
00049     
00051     enum { READ_BUFFER_SIZE = 8192 };
00052     
00054     typedef boost::function1<void, boost::shared_ptr<TCPConnection> >   ConnectionHandler;
00055     
00057     typedef boost::array<char, READ_BUFFER_SIZE>    ReadBuffer;
00058     
00060     typedef boost::asio::ip::tcp::socket            Socket;
00061 
00062 #ifdef PION_HAVE_SSL
00064     typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket>  SSLSocket;
00065 
00067     typedef boost::asio::ssl::context                               SSLContext;
00068 #else
00069     typedef Socket  SSLSocket;
00070     typedef int     SSLContext;
00071 #endif
00072 
00073     
00083     static inline boost::shared_ptr<TCPConnection> create(boost::asio::io_service& io_service,
00084                                                           SSLContext& ssl_context,
00085                                                           const bool ssl_flag,
00086                                                           ConnectionHandler finished_handler)
00087     {
00088         return boost::shared_ptr<TCPConnection>(new TCPConnection(io_service, ssl_context,
00089                                                                   ssl_flag, finished_handler));
00090     }
00091     
00098     explicit TCPConnection(boost::asio::io_service& io_service, const bool ssl_flag = false)
00099         : m_tcp_socket(io_service),
00100 #ifdef PION_HAVE_SSL
00101         m_ssl_context(io_service, boost::asio::ssl::context::sslv23),
00102         m_ssl_socket(io_service, m_ssl_context),
00103         m_ssl_flag(ssl_flag),
00104 #else
00105         m_ssl_context(0),
00106         m_ssl_socket(io_service),
00107         m_ssl_flag(false),
00108 #endif
00109         m_lifecycle(LIFECYCLE_CLOSE)
00110     {
00111         saveReadPosition(NULL, NULL);
00112     }
00113     
00120     TCPConnection(boost::asio::io_service& io_service, SSLContext& ssl_context)
00121         : m_tcp_socket(io_service),
00122 #ifdef PION_HAVE_SSL
00123         m_ssl_context(io_service, boost::asio::ssl::context::sslv23),
00124         m_ssl_socket(io_service, ssl_context), m_ssl_flag(true),
00125 #else
00126         m_ssl_context(0),
00127         m_ssl_socket(io_service), m_ssl_flag(false), 
00128 #endif
00129         m_lifecycle(LIFECYCLE_CLOSE)
00130     {
00131         saveReadPosition(NULL, NULL);
00132     }
00133     
00135     inline bool is_open(void) const {
00136 #ifdef PION_HAVE_SSL
00137         if (getSSLFlag())
00138             return const_cast<SSLSocket&>(m_ssl_socket).lowest_layer().is_open();
00139         else 
00140 #endif
00141             return m_tcp_socket.is_open();
00142     }
00143     
00145     inline void close(void) {
00146 #ifdef PION_HAVE_SSL
00147         if (getSSLFlag()) {
00148             if (m_ssl_socket.lowest_layer().is_open())
00149                 m_ssl_socket.lowest_layer().close();
00150         } else
00151 #endif
00152         {
00153             if (m_tcp_socket.is_open())
00154                 m_tcp_socket.close();
00155         }
00156     }
00157 
00158     /*
00159     Use close instead; basic_socket::cancel is deprecated for Windows XP.
00160 
00162     inline void cancel(void) {
00163 #ifdef PION_HAVE_SSL
00164         if (getSSLFlag())
00165             m_ssl_socket.lowest_layer().cancel();
00166         else
00167 #endif
00168             m_tcp_socket.cancel();
00169     }
00170     */
00171     
00173     virtual ~TCPConnection() { close(); }
00174     
00183     template <typename AcceptHandler>
00184     inline void async_accept(boost::asio::ip::tcp::acceptor& tcp_acceptor,
00185                              AcceptHandler handler)
00186     {
00187 #ifdef PION_HAVE_SSL
00188         if (getSSLFlag())
00189             tcp_acceptor.async_accept(m_ssl_socket.lowest_layer(), handler);
00190         else
00191 #endif      
00192             tcp_acceptor.async_accept(m_tcp_socket, handler);
00193     }
00194 
00203     inline boost::system::error_code accept(boost::asio::ip::tcp::acceptor& tcp_acceptor)
00204     {
00205         boost::system::error_code ec;
00206 #ifdef PION_HAVE_SSL
00207         if (getSSLFlag())
00208             tcp_acceptor.accept(m_ssl_socket.lowest_layer(), ec);
00209         else
00210 #endif      
00211             tcp_acceptor.accept(m_tcp_socket, ec);
00212         return ec;
00213     }
00214     
00223     template <typename ConnectHandler>
00224     inline void async_connect(boost::asio::ip::tcp::endpoint& tcp_endpoint,
00225                               ConnectHandler handler)
00226     {
00227 #ifdef PION_HAVE_SSL
00228         if (getSSLFlag())
00229             m_ssl_socket.lowest_layer().async_connect(tcp_endpoint, handler);
00230         else
00231 #endif      
00232             m_tcp_socket.async_connect(tcp_endpoint, handler);
00233     }
00234 
00244     template <typename ConnectHandler>
00245     inline void async_connect(const boost::asio::ip::address& remote_addr,
00246                               const unsigned int remote_port,
00247                               ConnectHandler handler)
00248     {
00249         boost::asio::ip::tcp::endpoint tcp_endpoint(remote_addr, remote_port);
00250         async_connect(tcp_endpoint, handler);
00251     }
00252     
00261     inline boost::system::error_code connect(boost::asio::ip::tcp::endpoint& tcp_endpoint)
00262     {
00263         boost::system::error_code ec;
00264 #ifdef PION_HAVE_SSL
00265         if (getSSLFlag())
00266             m_ssl_socket.lowest_layer().connect(tcp_endpoint, ec);
00267         else
00268 #endif      
00269             m_tcp_socket.connect(tcp_endpoint, ec);
00270         return ec;
00271     }
00272 
00282     inline boost::system::error_code connect(const boost::asio::ip::address& remote_addr,
00283                                              const unsigned int remote_port)
00284     {
00285         boost::asio::ip::tcp::endpoint tcp_endpoint(remote_addr, remote_port);
00286         return connect(tcp_endpoint);
00287     }
00288     
00298     inline boost::system::error_code connect(const std::string& remote_server,
00299                                              const unsigned int remote_port)
00300     {
00301         // query a list of matching endpoints
00302         boost::system::error_code ec;
00303         boost::asio::ip::tcp::resolver resolver(m_tcp_socket.get_io_service());
00304         boost::asio::ip::tcp::resolver::query query(remote_server,
00305             boost::lexical_cast<std::string>(remote_port),
00306             boost::asio::ip::tcp::resolver::query::numeric_service);
00307         boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query, ec);
00308         if (ec)
00309             return ec;
00310 
00311         // try each one until we are successful
00312         ec = boost::asio::error::host_not_found;
00313         boost::asio::ip::tcp::resolver::iterator end;
00314         while (ec && endpoint_iterator != end) {
00315             boost::asio::ip::tcp::endpoint ep(endpoint_iterator->endpoint());
00316             ++endpoint_iterator;
00317             ec = connect(ep);
00318             if (ec)
00319                 close();
00320         }
00321 
00322         return ec;
00323     }
00324     
00332     template <typename SSLHandshakeHandler>
00333     inline void async_handshake_client(SSLHandshakeHandler handler) {
00334 #ifdef PION_HAVE_SSL
00335         if (getSSLFlag())
00336             m_ssl_socket.async_handshake(boost::asio::ssl::stream_base::client, handler);
00337 #endif
00338     }
00339 
00347     template <typename SSLHandshakeHandler>
00348     inline void async_handshake_server(SSLHandshakeHandler handler) {
00349 #ifdef PION_HAVE_SSL
00350         if (getSSLFlag())
00351             m_ssl_socket.async_handshake(boost::asio::ssl::stream_base::server, handler);
00352 #endif
00353     }
00354     
00362     inline boost::system::error_code handshake_client(void) {
00363         boost::system::error_code ec;
00364 #ifdef PION_HAVE_SSL
00365         if (getSSLFlag())
00366             m_ssl_socket.handshake(boost::asio::ssl::stream_base::client, ec);
00367 #endif
00368         return ec;
00369     }
00370 
00378     inline boost::system::error_code handshake_server(void) {
00379         boost::system::error_code ec;
00380 #ifdef PION_HAVE_SSL
00381         if (getSSLFlag())
00382             m_ssl_socket.handshake(boost::asio::ssl::stream_base::server, ec);
00383 #endif
00384         return ec;
00385     }
00386     
00394     template <typename ReadHandler>
00395     inline void async_read_some(ReadHandler handler) {
00396 #ifdef PION_HAVE_SSL
00397         if (getSSLFlag())
00398             m_ssl_socket.async_read_some(boost::asio::buffer(m_read_buffer),
00399                                          handler);
00400         else
00401 #endif      
00402             m_tcp_socket.async_read_some(boost::asio::buffer(m_read_buffer),
00403                                          handler);
00404     }
00405     
00414     template <typename ReadBufferType, typename ReadHandler>
00415     inline void async_read_some(ReadBufferType read_buffer,
00416                                 ReadHandler handler) {
00417 #ifdef PION_HAVE_SSL
00418         if (getSSLFlag())
00419             m_ssl_socket.async_read_some(read_buffer, handler);
00420         else
00421 #endif      
00422             m_tcp_socket.async_read_some(read_buffer, handler);
00423     }
00424     
00433     inline std::size_t read_some(boost::system::error_code& ec) {
00434 #ifdef PION_HAVE_SSL
00435         if (getSSLFlag())
00436             return m_ssl_socket.read_some(boost::asio::buffer(m_read_buffer), ec);
00437         else
00438 #endif      
00439             return m_tcp_socket.read_some(boost::asio::buffer(m_read_buffer), ec);
00440     }
00441     
00451     template <typename ReadBufferType>
00452     inline std::size_t read_some(ReadBufferType read_buffer,
00453                                  boost::system::error_code& ec)
00454     {
00455 #ifdef PION_HAVE_SSL
00456         if (getSSLFlag())
00457             return m_ssl_socket.read_some(read_buffer, ec);
00458         else
00459 #endif      
00460             return m_tcp_socket.read_some(read_buffer, ec);
00461     }
00462     
00472     template <typename CompletionCondition, typename ReadHandler>
00473     inline void async_read(CompletionCondition completion_condition,
00474                            ReadHandler handler)
00475     {
00476 #ifdef PION_HAVE_SSL
00477         if (getSSLFlag())
00478             boost::asio::async_read(m_ssl_socket, boost::asio::buffer(m_read_buffer),
00479                                     completion_condition, handler);
00480         else
00481 #endif      
00482             boost::asio::async_read(m_tcp_socket, boost::asio::buffer(m_read_buffer),
00483                                     completion_condition, handler);
00484     }
00485             
00496     template <typename MutableBufferSequence, typename CompletionCondition, typename ReadHandler>
00497     inline void async_read(const MutableBufferSequence& buffers,
00498                            CompletionCondition completion_condition,
00499                            ReadHandler handler)
00500     {
00501 #ifdef PION_HAVE_SSL
00502         if (getSSLFlag())
00503             boost::asio::async_read(m_ssl_socket, buffers,
00504                                     completion_condition, handler);
00505         else
00506 #endif      
00507             boost::asio::async_read(m_tcp_socket, buffers,
00508                                     completion_condition, handler);
00509     }
00510     
00521     template <typename CompletionCondition>
00522     inline std::size_t read(CompletionCondition completion_condition,
00523                             boost::system::error_code& ec)
00524     {
00525 #ifdef PION_HAVE_SSL
00526         if (getSSLFlag())
00527             return boost::asio::async_read(m_ssl_socket, boost::asio::buffer(m_read_buffer),
00528                                            completion_condition, ec);
00529         else
00530 #endif      
00531             return boost::asio::async_read(m_tcp_socket, boost::asio::buffer(m_read_buffer),
00532                                            completion_condition, ec);
00533     }
00534     
00546     template <typename MutableBufferSequence, typename CompletionCondition>
00547     inline std::size_t read(const MutableBufferSequence& buffers,
00548                             CompletionCondition completion_condition,
00549                             boost::system::error_code& ec)
00550     {
00551 #ifdef PION_HAVE_SSL
00552         if (getSSLFlag())
00553             return boost::asio::read(m_ssl_socket, buffers,
00554                                      completion_condition, ec);
00555         else
00556 #endif      
00557             return boost::asio::read(m_tcp_socket, buffers,
00558                                      completion_condition, ec);
00559     }
00560     
00569     template <typename ConstBufferSequence, typename WriteHandler>
00570     inline void async_write(const ConstBufferSequence& buffers, WriteHandler handler) {
00571 #ifdef PION_HAVE_SSL
00572         if (getSSLFlag())
00573             boost::asio::async_write(m_ssl_socket, buffers, handler);
00574         else
00575 #endif      
00576             boost::asio::async_write(m_tcp_socket, buffers, handler);
00577     }   
00578         
00588     template <typename ConstBufferSequence>
00589     inline std::size_t write(const ConstBufferSequence& buffers,
00590                              boost::system::error_code& ec)
00591     {
00592 #ifdef PION_HAVE_SSL
00593         if (getSSLFlag())
00594             return boost::asio::write(m_ssl_socket, buffers,
00595                                       boost::asio::transfer_all(), ec);
00596         else
00597 #endif      
00598             return boost::asio::write(m_tcp_socket, buffers,
00599                                       boost::asio::transfer_all(), ec);
00600     }   
00601     
00602     
00605     inline void finish(void) { if (m_finished_handler) m_finished_handler(shared_from_this()); }
00606 
00608     inline bool getSSLFlag(void) const { return m_ssl_flag; }
00609 
00611     inline void setLifecycle(LifecycleType t) { m_lifecycle = t; }
00612     
00614     inline LifecycleType getLifecycle(void) const { return m_lifecycle; }
00615     
00617     inline bool getKeepAlive(void) const { return m_lifecycle != LIFECYCLE_CLOSE; }
00618     
00620     inline bool getPipelined(void) const { return m_lifecycle == LIFECYCLE_PIPELINED; }
00621 
00623     inline ReadBuffer& getReadBuffer(void) { return m_read_buffer; }
00624     
00631     inline void saveReadPosition(const char *read_ptr, const char *read_end_ptr) {
00632         m_read_position.first = read_ptr;
00633         m_read_position.second = read_end_ptr;
00634     }
00635     
00642     inline void loadReadPosition(const char *&read_ptr, const char *&read_end_ptr) const {
00643         read_ptr = m_read_position.first;
00644         read_end_ptr = m_read_position.second;
00645     }
00646 
00648     inline boost::asio::ip::tcp::endpoint getRemoteEndpoint(void) const {
00649         boost::asio::ip::tcp::endpoint remote_endpoint;
00650         try {
00651 #ifdef PION_HAVE_SSL
00652             if (getSSLFlag())
00653                 // const_cast is required since lowest_layer() is only defined non-const in asio
00654                 remote_endpoint = const_cast<SSLSocket&>(m_ssl_socket).lowest_layer().remote_endpoint();
00655             else
00656 #endif
00657                 remote_endpoint = m_tcp_socket.remote_endpoint();
00658         } catch (boost::system::system_error& /* e */) {
00659             // do nothing
00660         }
00661         return remote_endpoint;
00662     }
00663 
00665     inline boost::asio::ip::address getRemoteIp(void) const {
00666         return getRemoteEndpoint().address();
00667     }
00668 
00670     inline unsigned short getRemotePort(void) const {
00671         return getRemoteEndpoint().port();
00672     }
00673     
00675     inline boost::asio::io_service& getIOService(void) {
00676         return m_tcp_socket.io_service();
00677     }
00678 
00680     inline Socket& getSocket(void) { return m_tcp_socket; }
00681     
00683     inline SSLSocket& getSSLSocket(void) { return m_ssl_socket; }
00684 
00686     inline const Socket& getSocket(void) const { return m_tcp_socket; }
00687     
00689     inline const SSLSocket& getSSLSocket(void) const { return m_ssl_socket; }
00690 
00691     
00692 protected:
00693         
00703     TCPConnection(boost::asio::io_service& io_service,
00704                   SSLContext& ssl_context,
00705                   const bool ssl_flag,
00706                   ConnectionHandler finished_handler)
00707         : m_tcp_socket(io_service),
00708 #ifdef PION_HAVE_SSL
00709         m_ssl_context(io_service, boost::asio::ssl::context::sslv23),
00710         m_ssl_socket(io_service, ssl_context), m_ssl_flag(ssl_flag),
00711 #else
00712         m_ssl_context(0),
00713         m_ssl_socket(io_service), m_ssl_flag(false), 
00714 #endif
00715         m_lifecycle(LIFECYCLE_CLOSE),
00716         m_finished_handler(finished_handler)
00717     {
00718         saveReadPosition(NULL, NULL);
00719     }
00720     
00721 
00722 private:
00723 
00725     typedef std::pair<const char*, const char*>     ReadPosition;
00726 
00727     
00729     Socket                      m_tcp_socket;
00730     
00732     SSLContext                  m_ssl_context;
00733 
00735     SSLSocket                   m_ssl_socket;
00736 
00738     const bool                  m_ssl_flag;
00739 
00741     ReadBuffer                  m_read_buffer;
00742     
00744     ReadPosition                m_read_position;
00745     
00747     LifecycleType               m_lifecycle;
00748 
00750     ConnectionHandler           m_finished_handler;
00751 };
00752 
00753 
00755 typedef boost::shared_ptr<TCPConnection>    TCPConnectionPtr;
00756 
00757 
00758 }   // end namespace net
00759 }   // end namespace pion
00760 
00761 #endif

Generated on Fri Apr 30 14:48:53 2010 for pion-net by  doxygen 1.4.7