pion-net  4.0.9
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
00063 
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().get_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