pion  5.0.6
include/pion/tcp/connection.hpp
00001 // ---------------------------------------------------------------------
00002 // pion:  a Boost C++ framework for building lightweight HTTP interfaces
00003 // ---------------------------------------------------------------------
00004 // Copyright (C) 2007-2014 Splunk Inc.  (https://github.com/splunk/pion)
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_TCP_CONNECTION_HEADER__
00011 #define __PION_TCP_CONNECTION_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/config.hpp>
00030 #include <string>
00031 
00032 
00033 namespace pion {    // begin namespace pion
00034 namespace tcp {     // begin namespace tcp
00035 
00036 
00040 class connection :
00041     public boost::enable_shared_from_this<connection>,
00042     private boost::noncopyable
00043 {
00044 public:
00045 
00047     enum lifecycle_type {
00048         LIFECYCLE_CLOSE, LIFECYCLE_KEEPALIVE, LIFECYCLE_PIPELINED
00049     };
00050     
00052     enum { READ_BUFFER_SIZE = 8192 };
00053     
00055     typedef boost::function1<void, boost::shared_ptr<connection> >   connection_handler;
00056     
00058     typedef boost::array<char, READ_BUFFER_SIZE>    read_buffer_type;
00059     
00061     typedef boost::asio::ip::tcp::socket            socket_type;
00062 
00063 #ifdef PION_HAVE_SSL
00064 
00065     typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket>  ssl_socket_type;
00066 
00068     typedef boost::asio::ssl::context                               ssl_context_type;
00069 #else
00070     class ssl_socket_type {
00071     public:
00072         ssl_socket_type(boost::asio::io_service& io_service) : m_socket(io_service) {}
00073         inline socket_type& next_layer(void) { return m_socket; }
00074         inline const socket_type& next_layer(void) const { return m_socket; }
00075         inline socket_type::lowest_layer_type& lowest_layer(void) { return m_socket.lowest_layer(); }
00076         inline const socket_type::lowest_layer_type& lowest_layer(void) const { return m_socket.lowest_layer(); }
00077         inline void shutdown(void) {}
00078     private:
00079         socket_type  m_socket;
00080     };
00081     typedef int     ssl_context_type;
00082 #endif
00083 
00084     
00094     static inline boost::shared_ptr<connection> create(boost::asio::io_service& io_service,
00095                                                           ssl_context_type& ssl_context,
00096                                                           const bool ssl_flag,
00097                                                           connection_handler finished_handler)
00098     {
00099         return boost::shared_ptr<connection>(new connection(io_service, ssl_context,
00100                                                                   ssl_flag, finished_handler));
00101     }
00102     
00109     explicit connection(boost::asio::io_service& io_service, const bool ssl_flag = false)
00110         :
00111 #ifdef PION_HAVE_SSL
00112         m_ssl_context(io_service, boost::asio::ssl::context::sslv23),
00113         m_ssl_socket(io_service, m_ssl_context),
00114         m_ssl_flag(ssl_flag),
00115 #else
00116         m_ssl_context(0),
00117         m_ssl_socket(io_service),
00118         m_ssl_flag(false),
00119 #endif
00120         m_lifecycle(LIFECYCLE_CLOSE)
00121     {
00122         save_read_pos(NULL, NULL);
00123     }
00124     
00131     connection(boost::asio::io_service& io_service, ssl_context_type& ssl_context)
00132         :
00133 #ifdef PION_HAVE_SSL
00134         m_ssl_context(io_service, boost::asio::ssl::context::sslv23),
00135         m_ssl_socket(io_service, ssl_context), m_ssl_flag(true),
00136 #else
00137         m_ssl_context(0),
00138         m_ssl_socket(io_service), m_ssl_flag(false), 
00139 #endif
00140         m_lifecycle(LIFECYCLE_CLOSE)
00141     {
00142         save_read_pos(NULL, NULL);
00143     }
00144     
00146     inline bool is_open(void) const {
00147         return const_cast<ssl_socket_type&>(m_ssl_socket).lowest_layer().is_open();
00148     }
00149     
00151     inline void close(void) {
00152         if (is_open()) {
00153             try {
00154 
00155                 // shutting down SSL will wait forever for a response from the remote end,
00156                 // which causes it to hang indefinitely if the other end died unexpectedly
00157                 // if (get_ssl_flag()) m_ssl_socket.shutdown();
00158 
00159                 // windows seems to require this otherwise it doesn't
00160                 // recognize that connections have been closed
00161                 m_ssl_socket.next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both);
00162                 
00163             } catch (...) {}    // ignore exceptions
00164             
00165             // close the underlying socket (ignore errors)
00166             boost::system::error_code ec;
00167             m_ssl_socket.next_layer().close(ec);
00168         }
00169     }
00170 
00176     inline void cancel(void) {
00177 #if !defined(_MSC_VER) || (_WIN32_WINNT >= 0x0600)
00178         boost::system::error_code ec;
00179         m_ssl_socket.next_layer().cancel(ec);
00180 #endif
00181     }
00182     
00184     virtual ~connection() { close(); }
00185     
00194     template <typename AcceptHandler>
00195     inline void async_accept(boost::asio::ip::tcp::acceptor& tcp_acceptor,
00196                              AcceptHandler handler)
00197     {
00198         tcp_acceptor.async_accept(m_ssl_socket.lowest_layer(), handler);
00199     }
00200 
00209     inline boost::system::error_code accept(boost::asio::ip::tcp::acceptor& tcp_acceptor)
00210     {
00211         boost::system::error_code ec;
00212         tcp_acceptor.accept(m_ssl_socket.lowest_layer(), ec);
00213         return ec;
00214     }
00215     
00224     template <typename ConnectHandler>
00225     inline void async_connect(const boost::asio::ip::tcp::endpoint& tcp_endpoint,
00226                               ConnectHandler handler)
00227     {
00228         m_ssl_socket.lowest_layer().async_connect(tcp_endpoint, handler);
00229     }
00230 
00240     template <typename ConnectHandler>
00241     inline void async_connect(const boost::asio::ip::address& remote_addr,
00242                               const unsigned int remote_port,
00243                               ConnectHandler handler)
00244     {
00245         boost::asio::ip::tcp::endpoint tcp_endpoint(remote_addr, remote_port);
00246         async_connect(tcp_endpoint, handler);
00247     }
00248     
00257     inline boost::system::error_code connect(boost::asio::ip::tcp::endpoint& tcp_endpoint)
00258     {
00259         boost::system::error_code ec;
00260         m_ssl_socket.lowest_layer().connect(tcp_endpoint, ec);
00261         return ec;
00262     }
00263 
00273     inline boost::system::error_code connect(const boost::asio::ip::address& remote_addr,
00274                                              const unsigned int remote_port)
00275     {
00276         boost::asio::ip::tcp::endpoint tcp_endpoint(remote_addr, remote_port);
00277         return connect(tcp_endpoint);
00278     }
00279     
00289     inline boost::system::error_code connect(const std::string& remote_server,
00290                                              const unsigned int remote_port)
00291     {
00292         // query a list of matching endpoints
00293         boost::system::error_code ec;
00294         boost::asio::ip::tcp::resolver resolver(m_ssl_socket.lowest_layer().get_io_service());
00295         boost::asio::ip::tcp::resolver::query query(remote_server,
00296             boost::lexical_cast<std::string>(remote_port),
00297             boost::asio::ip::tcp::resolver::query::numeric_service);
00298         boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query, ec);
00299         if (ec)
00300             return ec;
00301 
00302         // try each one until we are successful
00303         ec = boost::asio::error::host_not_found;
00304         boost::asio::ip::tcp::resolver::iterator end;
00305         while (ec && endpoint_iterator != end) {
00306             boost::asio::ip::tcp::endpoint ep(endpoint_iterator->endpoint());
00307             ++endpoint_iterator;
00308             ec = connect(ep);
00309             if (ec)
00310                 close();
00311         }
00312 
00313         return ec;
00314     }
00315     
00323     template <typename SSLHandshakeHandler>
00324     inline void async_handshake_client(SSLHandshakeHandler handler) {
00325 #ifdef PION_HAVE_SSL
00326         m_ssl_socket.async_handshake(boost::asio::ssl::stream_base::client, handler);
00327         m_ssl_flag = true;
00328 #endif
00329     }
00330 
00338     template <typename SSLHandshakeHandler>
00339     inline void async_handshake_server(SSLHandshakeHandler handler) {
00340 #ifdef PION_HAVE_SSL
00341         m_ssl_socket.async_handshake(boost::asio::ssl::stream_base::server, handler);
00342         m_ssl_flag = true;
00343 #endif
00344     }
00345     
00353     inline boost::system::error_code handshake_client(void) {
00354         boost::system::error_code ec;
00355 #ifdef PION_HAVE_SSL
00356         m_ssl_socket.handshake(boost::asio::ssl::stream_base::client, ec);
00357         m_ssl_flag = true;
00358 #endif
00359         return ec;
00360     }
00361 
00369     inline boost::system::error_code handshake_server(void) {
00370         boost::system::error_code ec;
00371 #ifdef PION_HAVE_SSL
00372         m_ssl_socket.handshake(boost::asio::ssl::stream_base::server, ec);
00373         m_ssl_flag = true;
00374 #endif
00375         return ec;
00376     }
00377     
00385     template <typename ReadHandler>
00386     inline void async_read_some(ReadHandler handler) {
00387 #ifdef PION_HAVE_SSL
00388         if (get_ssl_flag())
00389             m_ssl_socket.async_read_some(boost::asio::buffer(m_read_buffer),
00390                                          handler);
00391         else
00392 #endif      
00393             m_ssl_socket.next_layer().async_read_some(boost::asio::buffer(m_read_buffer),
00394                                          handler);
00395     }
00396     
00405     template <typename ReadBufferType, typename ReadHandler>
00406     inline void async_read_some(ReadBufferType read_buffer,
00407                                 ReadHandler handler) {
00408 #ifdef PION_HAVE_SSL
00409         if (get_ssl_flag())
00410             m_ssl_socket.async_read_some(read_buffer, handler);
00411         else
00412 #endif      
00413             m_ssl_socket.next_layer().async_read_some(read_buffer, handler);
00414     }
00415     
00424     inline std::size_t read_some(boost::system::error_code& ec) {
00425 #ifdef PION_HAVE_SSL
00426         if (get_ssl_flag())
00427             return m_ssl_socket.read_some(boost::asio::buffer(m_read_buffer), ec);
00428         else
00429 #endif      
00430             return m_ssl_socket.next_layer().read_some(boost::asio::buffer(m_read_buffer), ec);
00431     }
00432     
00442     template <typename ReadBufferType>
00443     inline std::size_t read_some(ReadBufferType read_buffer,
00444                                  boost::system::error_code& ec)
00445     {
00446 #ifdef PION_HAVE_SSL
00447         if (get_ssl_flag())
00448             return m_ssl_socket.read_some(read_buffer, ec);
00449         else
00450 #endif      
00451             return m_ssl_socket.next_layer().read_some(read_buffer, ec);
00452     }
00453     
00463     template <typename CompletionCondition, typename ReadHandler>
00464     inline void async_read(CompletionCondition completion_condition,
00465                            ReadHandler handler)
00466     {
00467 #ifdef PION_HAVE_SSL
00468         if (get_ssl_flag())
00469             boost::asio::async_read(m_ssl_socket, boost::asio::buffer(m_read_buffer),
00470                                     completion_condition, handler);
00471         else
00472 #endif      
00473             boost::asio::async_read(m_ssl_socket.next_layer(), boost::asio::buffer(m_read_buffer),
00474                                     completion_condition, handler);
00475     }
00476             
00487     template <typename MutableBufferSequence, typename CompletionCondition, typename ReadHandler>
00488     inline void async_read(const MutableBufferSequence& buffers,
00489                            CompletionCondition completion_condition,
00490                            ReadHandler handler)
00491     {
00492 #ifdef PION_HAVE_SSL
00493         if (get_ssl_flag())
00494             boost::asio::async_read(m_ssl_socket, buffers,
00495                                     completion_condition, handler);
00496         else
00497 #endif      
00498             boost::asio::async_read(m_ssl_socket.next_layer(), buffers,
00499                                     completion_condition, handler);
00500     }
00501     
00512     template <typename CompletionCondition>
00513     inline std::size_t read(CompletionCondition completion_condition,
00514                             boost::system::error_code& ec)
00515     {
00516 #ifdef PION_HAVE_SSL
00517         if (get_ssl_flag())
00518             return boost::asio::async_read(m_ssl_socket, boost::asio::buffer(m_read_buffer),
00519                                            completion_condition, ec);
00520         else
00521 #endif      
00522             return boost::asio::async_read(m_ssl_socket.next_layer(), boost::asio::buffer(m_read_buffer),
00523                                            completion_condition, ec);
00524     }
00525     
00537     template <typename MutableBufferSequence, typename CompletionCondition>
00538     inline std::size_t read(const MutableBufferSequence& buffers,
00539                             CompletionCondition completion_condition,
00540                             boost::system::error_code& ec)
00541     {
00542 #ifdef PION_HAVE_SSL
00543         if (get_ssl_flag())
00544             return boost::asio::read(m_ssl_socket, buffers,
00545                                      completion_condition, ec);
00546         else
00547 #endif      
00548             return boost::asio::read(m_ssl_socket.next_layer(), buffers,
00549                                      completion_condition, ec);
00550     }
00551     
00560     template <typename ConstBufferSequence, typename write_handler_t>
00561     inline void async_write(const ConstBufferSequence& buffers, write_handler_t handler) {
00562 #ifdef PION_HAVE_SSL
00563         if (get_ssl_flag())
00564             boost::asio::async_write(m_ssl_socket, buffers, handler);
00565         else
00566 #endif      
00567             boost::asio::async_write(m_ssl_socket.next_layer(), buffers, handler);
00568     }   
00569         
00579     template <typename ConstBufferSequence>
00580     inline std::size_t write(const ConstBufferSequence& buffers,
00581                              boost::system::error_code& ec)
00582     {
00583 #ifdef PION_HAVE_SSL
00584         if (get_ssl_flag())
00585             return boost::asio::write(m_ssl_socket, buffers,
00586                                       boost::asio::transfer_all(), ec);
00587         else
00588 #endif      
00589             return boost::asio::write(m_ssl_socket.next_layer(), buffers,
00590                                       boost::asio::transfer_all(), ec);
00591     }   
00592     
00593     
00596     inline void finish(void) { if (m_finished_handler) m_finished_handler(shared_from_this()); }
00597 
00599     inline bool get_ssl_flag(void) const { return m_ssl_flag; }
00600 
00602     inline void set_lifecycle(lifecycle_type t) { m_lifecycle = t; }
00603     
00605     inline lifecycle_type get_lifecycle(void) const { return m_lifecycle; }
00606     
00608     inline bool get_keep_alive(void) const { return m_lifecycle != LIFECYCLE_CLOSE; }
00609     
00611     inline bool get_pipelined(void) const { return m_lifecycle == LIFECYCLE_PIPELINED; }
00612 
00614     inline read_buffer_type& get_read_buffer(void) { return m_read_buffer; }
00615     
00622     inline void save_read_pos(const char *read_ptr, const char *read_end_ptr) {
00623         m_read_position.first = read_ptr;
00624         m_read_position.second = read_end_ptr;
00625     }
00626     
00633     inline void load_read_pos(const char *&read_ptr, const char *&read_end_ptr) const {
00634         read_ptr = m_read_position.first;
00635         read_end_ptr = m_read_position.second;
00636     }
00637 
00639     inline boost::asio::ip::tcp::endpoint get_remote_endpoint(void) const {
00640         boost::asio::ip::tcp::endpoint remote_endpoint;
00641         try {
00642             // const_cast is required since lowest_layer() is only defined non-const in asio
00643             remote_endpoint = const_cast<ssl_socket_type&>(m_ssl_socket).lowest_layer().remote_endpoint();
00644         } catch (boost::system::system_error& /* e */) {
00645             // do nothing
00646         }
00647         return remote_endpoint;
00648     }
00649 
00651     inline boost::asio::ip::address get_remote_ip(void) const {
00652         return get_remote_endpoint().address();
00653     }
00654 
00656     inline unsigned short get_remote_port(void) const {
00657         return get_remote_endpoint().port();
00658     }
00659     
00661     inline boost::asio::io_service& get_io_service(void) {
00662         return m_ssl_socket.lowest_layer().get_io_service();
00663     }
00664 
00666     inline socket_type& get_socket(void) { return m_ssl_socket.next_layer(); }
00667     
00669     inline ssl_socket_type& get_ssl_socket(void) { return m_ssl_socket; }
00670 
00672     inline const socket_type& get_socket(void) const { return const_cast<ssl_socket_type&>(m_ssl_socket).next_layer(); }
00673     
00675     inline const ssl_socket_type& get_ssl_socket(void) const { return m_ssl_socket; }
00676 
00677     
00678 protected:
00679         
00689     connection(boost::asio::io_service& io_service,
00690                   ssl_context_type& ssl_context,
00691                   const bool ssl_flag,
00692                   connection_handler finished_handler)
00693         :
00694 #ifdef PION_HAVE_SSL
00695         m_ssl_context(io_service, boost::asio::ssl::context::sslv23),
00696         m_ssl_socket(io_service, ssl_context), m_ssl_flag(ssl_flag),
00697 #else
00698         m_ssl_context(0),
00699         m_ssl_socket(io_service), m_ssl_flag(false), 
00700 #endif
00701         m_lifecycle(LIFECYCLE_CLOSE),
00702         m_finished_handler(finished_handler)
00703     {
00704         save_read_pos(NULL, NULL);
00705     }
00706     
00707 
00708 private:
00709 
00711     typedef std::pair<const char*, const char*>     read_pos_type;
00712 
00713     
00715     ssl_context_type        m_ssl_context;
00716 
00718     ssl_socket_type         m_ssl_socket;
00719 
00721     bool                    m_ssl_flag;
00722 
00724     read_buffer_type        m_read_buffer;
00725     
00727     read_pos_type           m_read_position;
00728     
00730     lifecycle_type          m_lifecycle;
00731 
00733     connection_handler      m_finished_handler;
00734 };
00735 
00736 
00738 typedef boost::shared_ptr<connection>    connection_ptr;
00739 
00740 
00741 }   // end namespace tcp
00742 }   // end namespace pion
00743 
00744 #endif