- Main Page
- Classes
- Files

- File List

# include/pion/tcp/connection.hpp

    00001 // ---------------------------------------------------------------------
    00002 // pion:  a Boost C++ framework for building lightweight HTTP interfaces
    00003 // ---------------------------------------------------------------------
    00004 // Copyright (C) 2007-2012 Cloudmeter, Inc.  (http://www.cloudmeter.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_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
    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(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