# 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     class SSLSocket {
    00070     public:
    00071         SSLSocket(boost::asio::io_service& io_service) : m_socket(io_service) {}
    00072         inline Socket& next_layer(void) { return m_socket; }
    00073         inline const Socket& next_layer(void) const { return m_socket; }
    00074         inline Socket& lowest_layer(void) { return m_socket.lowest_layer(); }
    00075         inline const Socket& lowest_layer(void) const { return m_socket.lowest_layer(); }
    00076     private:
    00077         Socket  m_socket;
    00078     };
    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         :
    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         :
    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         return const_cast<SSLSocket&>(m_ssl_socket).lowest_layer().is_open();
    00146     }
    00147
    00149     inline void close(void) {
    00150         if (m_ssl_socket.lowest_layer().is_open())
    00151             m_ssl_socket.lowest_layer().close();
    00152     }
    00153
    00154     /*
    00155     Use close instead; basic_socket::cancel is deprecated for Windows XP.
    00156
    00158     inline void cancel(void) {
    00159         m_ssl_socket.lowest_layer().cancel();
    00160     }
    00161     */
    00162
    00164     virtual ~TCPConnection() { close(); }
    00165
    00174     template <typename AcceptHandler>
    00175     inline void async_accept(boost::asio::ip::tcp::acceptor& tcp_acceptor,
    00176                              AcceptHandler handler)
    00177     {
    00178         tcp_acceptor.async_accept(m_ssl_socket.lowest_layer(), handler);
    00179     }
    00180
    00189     inline boost::system::error_code accept(boost::asio::ip::tcp::acceptor& tcp_acceptor)
    00190     {
    00191         boost::system::error_code ec;
    00192         tcp_acceptor.accept(m_ssl_socket.lowest_layer(), ec);
    00193         return ec;
    00194     }
    00195
    00204     template <typename ConnectHandler>
    00205     inline void async_connect(boost::asio::ip::tcp::endpoint& tcp_endpoint,
    00206                               ConnectHandler handler)
    00207     {
    00208         m_ssl_socket.lowest_layer().async_connect(tcp_endpoint, handler);
    00209     }
    00210
    00220     template <typename ConnectHandler>
    00221     inline void async_connect(const boost::asio::ip::address& remote_addr,
    00222                               const unsigned int remote_port,
    00223                               ConnectHandler handler)
    00224     {
    00225         boost::asio::ip::tcp::endpoint tcp_endpoint(remote_addr, remote_port);
    00226         async_connect(tcp_endpoint, handler);
    00227     }
    00228
    00237     inline boost::system::error_code connect(boost::asio::ip::tcp::endpoint& tcp_endpoint)
    00238     {
    00239         boost::system::error_code ec;
    00240         m_ssl_socket.lowest_layer().connect(tcp_endpoint, ec);
    00241         return ec;
    00242     }
    00243
    00253     inline boost::system::error_code connect(const boost::asio::ip::address& remote_addr,
    00254                                              const unsigned int remote_port)
    00255     {
    00256         boost::asio::ip::tcp::endpoint tcp_endpoint(remote_addr, remote_port);
    00257         return connect(tcp_endpoint);
    00258     }
    00259
    00269     inline boost::system::error_code connect(const std::string& remote_server,
    00270                                              const unsigned int remote_port)
    00271     {
    00272         // query a list of matching endpoints
    00273         boost::system::error_code ec;
    00274         boost::asio::ip::tcp::resolver resolver(m_ssl_socket.lowest_layer().get_io_service());
    00275         boost::asio::ip::tcp::resolver::query query(remote_server,
    00276             boost::lexical_cast<std::string>(remote_port),
    00277             boost::asio::ip::tcp::resolver::query::numeric_service);
    00278         boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query, ec);
    00279         if (ec)
    00280             return ec;
    00281
    00282         // try each one until we are successful
    00283         ec = boost::asio::error::host_not_found;
    00284         boost::asio::ip::tcp::resolver::iterator end;
    00285         while (ec && endpoint_iterator != end) {
    00286             boost::asio::ip::tcp::endpoint ep(endpoint_iterator->endpoint());
    00287             ++endpoint_iterator;
    00288             ec = connect(ep);
    00289             if (ec)
    00290                 close();
    00291         }
    00292
    00293         return ec;
    00294     }
    00295
    00303     template <typename SSLHandshakeHandler>
    00304     inline void async_handshake_client(SSLHandshakeHandler handler) {
    00305 #ifdef PION_HAVE_SSL
    00306         m_ssl_socket.async_handshake(boost::asio::ssl::stream_base::client, handler);
    00307         m_ssl_flag = true;
    00308 #endif
    00309     }
    00310
    00318     template <typename SSLHandshakeHandler>
    00319     inline void async_handshake_server(SSLHandshakeHandler handler) {
    00320 #ifdef PION_HAVE_SSL
    00321         m_ssl_socket.async_handshake(boost::asio::ssl::stream_base::server, handler);
    00322         m_ssl_flag = true;
    00323 #endif
    00324     }
    00325
    00333     inline boost::system::error_code handshake_client(void) {
    00334         boost::system::error_code ec;
    00335 #ifdef PION_HAVE_SSL
    00336         m_ssl_socket.handshake(boost::asio::ssl::stream_base::client, ec);
    00337         m_ssl_flag = true;
    00338 #endif
    00339         return ec;
    00340     }
    00341
    00349     inline boost::system::error_code handshake_server(void) {
    00350         boost::system::error_code ec;
    00351 #ifdef PION_HAVE_SSL
    00352         m_ssl_socket.handshake(boost::asio::ssl::stream_base::server, ec);
    00353         m_ssl_flag = true;
    00354 #endif
    00355         return ec;
    00356     }
    00357
    00365     template <typename ReadHandler>
    00366     inline void async_read_some(ReadHandler handler) {
    00367 #ifdef PION_HAVE_SSL
    00368         if (getSSLFlag())
    00369             m_ssl_socket.async_read_some(boost::asio::buffer(m_read_buffer),
    00370                                          handler);
    00371         else
    00372 #endif
    00373             m_ssl_socket.next_layer().async_read_some(boost::asio::buffer(m_read_buffer),
    00374                                          handler);
    00375     }
    00376
    00385     template <typename ReadBufferType, typename ReadHandler>
    00386     inline void async_read_some(ReadBufferType read_buffer,
    00387                                 ReadHandler handler) {
    00388 #ifdef PION_HAVE_SSL
    00389         if (getSSLFlag())
    00390             m_ssl_socket.async_read_some(read_buffer, handler);
    00391         else
    00392 #endif
    00393             m_ssl_socket.next_layer().async_read_some(read_buffer, handler);
    00394     }
    00395
    00404     inline std::size_t read_some(boost::system::error_code& ec) {
    00405 #ifdef PION_HAVE_SSL
    00406         if (getSSLFlag())
    00407             return m_ssl_socket.read_some(boost::asio::buffer(m_read_buffer), ec);
    00408         else
    00409 #endif
    00410             return m_ssl_socket.next_layer().read_some(boost::asio::buffer(m_read_buffer), ec);
    00411     }
    00412
    00422     template <typename ReadBufferType>
    00423     inline std::size_t read_some(ReadBufferType read_buffer,
    00424                                  boost::system::error_code& ec)
    00425     {
    00426 #ifdef PION_HAVE_SSL
    00427         if (getSSLFlag())
    00428             return m_ssl_socket.read_some(read_buffer, ec);
    00429         else
    00430 #endif
    00431             return m_ssl_socket.next_layer().read_some(read_buffer, ec);
    00432     }
    00433
    00443     template <typename CompletionCondition, typename ReadHandler>
    00444     inline void async_read(CompletionCondition completion_condition,
    00445                            ReadHandler handler)
    00446     {
    00447 #ifdef PION_HAVE_SSL
    00448         if (getSSLFlag())
    00449             boost::asio::async_read(m_ssl_socket, boost::asio::buffer(m_read_buffer),
    00450                                     completion_condition, handler);
    00451         else
    00452 #endif
    00453             boost::asio::async_read(m_ssl_socket.next_layer(), boost::asio::buffer(m_read_buffer),
    00454                                     completion_condition, handler);
    00455     }
    00456
    00467     template <typename MutableBufferSequence, typename CompletionCondition, typename ReadHandler>
    00468     inline void async_read(const MutableBufferSequence& buffers,
    00469                            CompletionCondition completion_condition,
    00470                            ReadHandler handler)
    00471     {
    00472 #ifdef PION_HAVE_SSL
    00473         if (getSSLFlag())
    00474             boost::asio::async_read(m_ssl_socket, buffers,
    00475                                     completion_condition, handler);
    00476         else
    00477 #endif
    00478             boost::asio::async_read(m_ssl_socket.next_layer(), buffers,
    00479                                     completion_condition, handler);
    00480     }
    00481
    00492     template <typename CompletionCondition>
    00493     inline std::size_t read(CompletionCondition completion_condition,
    00494                             boost::system::error_code& ec)
    00495     {
    00496 #ifdef PION_HAVE_SSL
    00497         if (getSSLFlag())
    00498             return boost::asio::async_read(m_ssl_socket, boost::asio::buffer(m_read_buffer),
    00499                                            completion_condition, ec);
    00500         else
    00501 #endif
    00502             return boost::asio::async_read(m_ssl_socket.next_layer(), boost::asio::buffer(m_read_buffer),
    00503                                            completion_condition, ec);
    00504     }
    00505
    00517     template <typename MutableBufferSequence, typename CompletionCondition>
    00518     inline std::size_t read(const MutableBufferSequence& buffers,
    00519                             CompletionCondition completion_condition,
    00520                             boost::system::error_code& ec)
    00521     {
    00522 #ifdef PION_HAVE_SSL
    00523         if (getSSLFlag())
    00524             return boost::asio::read(m_ssl_socket, buffers,
    00525                                      completion_condition, ec);
    00526         else
    00527 #endif
    00528             return boost::asio::read(m_ssl_socket.next_layer(), buffers,
    00529                                      completion_condition, ec);
    00530     }
    00531
    00540     template <typename ConstBufferSequence, typename WriteHandler>
    00541     inline void async_write(const ConstBufferSequence& buffers, WriteHandler handler) {
    00542 #ifdef PION_HAVE_SSL
    00543         if (getSSLFlag())
    00544             boost::asio::async_write(m_ssl_socket, buffers, handler);
    00545         else
    00546 #endif
    00547             boost::asio::async_write(m_ssl_socket.next_layer(), buffers, handler);
    00548     }
    00549
    00559     template <typename ConstBufferSequence>
    00560     inline std::size_t write(const ConstBufferSequence& buffers,
    00561                              boost::system::error_code& ec)
    00562     {
    00563 #ifdef PION_HAVE_SSL
    00564         if (getSSLFlag())
    00565             return boost::asio::write(m_ssl_socket, buffers,
    00566                                       boost::asio::transfer_all(), ec);
    00567         else
    00568 #endif
    00569             return boost::asio::write(m_ssl_socket.next_layer(), buffers,
    00570                                       boost::asio::transfer_all(), ec);
    00571     }
    00572
    00573
    00576     inline void finish(void) { if (m_finished_handler) m_finished_handler(shared_from_this()); }
    00577
    00579     inline bool getSSLFlag(void) const { return m_ssl_flag; }
    00580
    00582     inline void setLifecycle(LifecycleType t) { m_lifecycle = t; }
    00583
    00585     inline LifecycleType getLifecycle(void) const { return m_lifecycle; }
    00586
    00588     inline bool getKeepAlive(void) const { return m_lifecycle != LIFECYCLE_CLOSE; }
    00589
    00591     inline bool getPipelined(void) const { return m_lifecycle == LIFECYCLE_PIPELINED; }
    00592
    00594     inline ReadBuffer& getReadBuffer(void) { return m_read_buffer; }
    00595
    00602     inline void saveReadPosition(const char *read_ptr, const char *read_end_ptr) {
    00603         m_read_position.first = read_ptr;
    00604         m_read_position.second = read_end_ptr;
    00605     }
    00606
    00613     inline void loadReadPosition(const char *&read_ptr, const char *&read_end_ptr) const {
    00614         read_ptr = m_read_position.first;
    00615         read_end_ptr = m_read_position.second;
    00616     }
    00617
    00619     inline boost::asio::ip::tcp::endpoint getRemoteEndpoint(void) const {
    00620         boost::asio::ip::tcp::endpoint remote_endpoint;
    00621         try {
    00622             // const_cast is required since lowest_layer() is only defined non-const in asio
    00623             remote_endpoint = const_cast<SSLSocket&>(m_ssl_socket).lowest_layer().remote_endpoint();
    00624         } catch (boost::system::system_error& /* e */) {
    00625             // do nothing
    00626         }
    00627         return remote_endpoint;
    00628     }
    00629
    00631     inline boost::asio::ip::address getRemoteIp(void) const {
    00632         return getRemoteEndpoint().address();
    00633     }
    00634
    00636     inline unsigned short getRemotePort(void) const {
    00637         return getRemoteEndpoint().port();
    00638     }
    00639
    00641     inline boost::asio::io_service& getIOService(void) {
    00642         return m_ssl_socket.lowest_layer().io_service();
    00643     }
    00644
    00646     inline Socket& getSocket(void) { return m_ssl_socket.next_layer(); }
    00647
    00649     inline SSLSocket& getSSLSocket(void) { return m_ssl_socket; }
    00650
    00652     inline const Socket& getSocket(void) const { return const_cast<SSLSocket&>(m_ssl_socket).next_layer(); }
    00653
    00655     inline const SSLSocket& getSSLSocket(void) const { return m_ssl_socket; }
    00656
    00657
    00658 protected:
    00659
    00669     TCPConnection(boost::asio::io_service& io_service,
    00670                   SSLContext& ssl_context,
    00671                   const bool ssl_flag,
    00672                   ConnectionHandler finished_handler)
    00673         :
    00674 #ifdef PION_HAVE_SSL
    00675         m_ssl_context(io_service, boost::asio::ssl::context::sslv23),
    00676         m_ssl_socket(io_service, ssl_context), m_ssl_flag(ssl_flag),
    00677 #else
    00678         m_ssl_context(0),
    00679         m_ssl_socket(io_service), m_ssl_flag(false),
    00680 #endif
    00681         m_lifecycle(LIFECYCLE_CLOSE),
    00682         m_finished_handler(finished_handler)
    00683     {
    00684         saveReadPosition(NULL, NULL);
    00685     }
    00686
    00687
    00688 private:
    00689
    00691     typedef std::pair<const char*, const char*>     ReadPosition;
    00692
    00693
    00695     SSLContext                  m_ssl_context;
    00696
    00698     SSLSocket                   m_ssl_socket;
    00699
    00701     bool                        m_ssl_flag;
    00702
    00704     ReadBuffer                  m_read_buffer;
    00705
    00707     ReadPosition                m_read_position;
    00708
    00710     LifecycleType               m_lifecycle;
    00711
    00713     ConnectionHandler           m_finished_handler;
    00714 };
    00715
    00716
    00718 typedef boost::shared_ptr<TCPConnection>    TCPConnectionPtr;
    00719
    00720
    00721 }   // end namespace net
    00722 }   // end namespace pion
    00723
    00724 #endif