pion-net  4.0.9
TCPConnection.hpp
1 // ------------------------------------------------------------------
2 // pion-net: a C++ framework for building lightweight HTTP interfaces
3 // ------------------------------------------------------------------
4 // Copyright (C) 2007-2008 Atomic Labs, Inc. (http://www.atomiclabs.com)
5 //
6 // Distributed under the Boost Software License, Version 1.0.
7 // See http://www.boost.org/LICENSE_1_0.txt
8 //
9 
10 #ifndef __PION_TCPCONNECTION_HEADER__
11 #define __PION_TCPCONNECTION_HEADER__
12 
13 #ifdef PION_HAVE_SSL
14  #ifdef PION_XCODE
15  // ignore openssl warnings if building with XCode
16  #pragma GCC system_header
17  #endif
18  #include <boost/asio/ssl.hpp>
19 #endif
20 
21 #include <boost/noncopyable.hpp>
22 #include <boost/shared_ptr.hpp>
23 #include <boost/lexical_cast.hpp>
24 #include <boost/enable_shared_from_this.hpp>
25 #include <boost/asio.hpp>
26 #include <boost/array.hpp>
27 #include <boost/function.hpp>
28 #include <boost/function/function1.hpp>
29 #include <pion/PionConfig.hpp>
30 #include <string>
31 
32 
33 namespace pion { // begin namespace pion
34 namespace net { // begin namespace net (Pion Network Library)
35 
40  public boost::enable_shared_from_this<TCPConnection>,
41  private boost::noncopyable
42 {
43 public:
44 
47  LIFECYCLE_CLOSE, LIFECYCLE_KEEPALIVE, LIFECYCLE_PIPELINED
48  };
49 
51  enum { READ_BUFFER_SIZE = 8192 };
52 
54  typedef boost::function1<void, boost::shared_ptr<TCPConnection> > ConnectionHandler;
55 
57  typedef boost::array<char, READ_BUFFER_SIZE> ReadBuffer;
58 
60  typedef boost::asio::ip::tcp::socket Socket;
61 
62 #ifdef PION_HAVE_SSL
63  typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> SSLSocket;
65 
67  typedef boost::asio::ssl::context SSLContext;
68 #else
69  class SSLSocket {
70  public:
71  SSLSocket(boost::asio::io_service& io_service) : m_socket(io_service) {}
72  inline Socket& next_layer(void) { return m_socket; }
73  inline const Socket& next_layer(void) const { return m_socket; }
74  inline Socket& lowest_layer(void) { return m_socket.lowest_layer(); }
75  inline const Socket& lowest_layer(void) const { return m_socket.lowest_layer(); }
76  private:
77  Socket m_socket;
78  };
79  typedef int SSLContext;
80 #endif
81 
82 
92  static inline boost::shared_ptr<TCPConnection> create(boost::asio::io_service& io_service,
93  SSLContext& ssl_context,
94  const bool ssl_flag,
95  ConnectionHandler finished_handler)
96  {
97  return boost::shared_ptr<TCPConnection>(new TCPConnection(io_service, ssl_context,
98  ssl_flag, finished_handler));
99  }
100 
107  explicit TCPConnection(boost::asio::io_service& io_service, const bool ssl_flag = false)
108  :
109 #ifdef PION_HAVE_SSL
110  m_ssl_context(io_service, boost::asio::ssl::context::sslv23),
111  m_ssl_socket(io_service, m_ssl_context),
112  m_ssl_flag(ssl_flag),
113 #else
114  m_ssl_context(0),
115  m_ssl_socket(io_service),
116  m_ssl_flag(false),
117 #endif
118  m_lifecycle(LIFECYCLE_CLOSE)
119  {
120  saveReadPosition(NULL, NULL);
121  }
122 
129  TCPConnection(boost::asio::io_service& io_service, SSLContext& ssl_context)
130  :
131 #ifdef PION_HAVE_SSL
132  m_ssl_context(io_service, boost::asio::ssl::context::sslv23),
133  m_ssl_socket(io_service, ssl_context), m_ssl_flag(true),
134 #else
135  m_ssl_context(0),
136  m_ssl_socket(io_service), m_ssl_flag(false),
137 #endif
138  m_lifecycle(LIFECYCLE_CLOSE)
139  {
140  saveReadPosition(NULL, NULL);
141  }
142 
144  inline bool is_open(void) const {
145  return const_cast<SSLSocket&>(m_ssl_socket).lowest_layer().is_open();
146  }
147 
149  inline void close(void) {
150  if (m_ssl_socket.lowest_layer().is_open())
151  m_ssl_socket.lowest_layer().close();
152  }
153 
154  /*
155  Use close instead; basic_socket::cancel is deprecated for Windows XP.
156 
158  inline void cancel(void) {
159  m_ssl_socket.lowest_layer().cancel();
160  }
161  */
162 
164  virtual ~TCPConnection() { close(); }
165 
174  template <typename AcceptHandler>
175  inline void async_accept(boost::asio::ip::tcp::acceptor& tcp_acceptor,
176  AcceptHandler handler)
177  {
178  tcp_acceptor.async_accept(m_ssl_socket.lowest_layer(), handler);
179  }
180 
189  inline boost::system::error_code accept(boost::asio::ip::tcp::acceptor& tcp_acceptor)
190  {
191  boost::system::error_code ec;
192  tcp_acceptor.accept(m_ssl_socket.lowest_layer(), ec);
193  return ec;
194  }
195 
204  template <typename ConnectHandler>
205  inline void async_connect(boost::asio::ip::tcp::endpoint& tcp_endpoint,
206  ConnectHandler handler)
207  {
208  m_ssl_socket.lowest_layer().async_connect(tcp_endpoint, handler);
209  }
210 
220  template <typename ConnectHandler>
221  inline void async_connect(const boost::asio::ip::address& remote_addr,
222  const unsigned int remote_port,
223  ConnectHandler handler)
224  {
225  boost::asio::ip::tcp::endpoint tcp_endpoint(remote_addr, remote_port);
226  async_connect(tcp_endpoint, handler);
227  }
228 
237  inline boost::system::error_code connect(boost::asio::ip::tcp::endpoint& tcp_endpoint)
238  {
239  boost::system::error_code ec;
240  m_ssl_socket.lowest_layer().connect(tcp_endpoint, ec);
241  return ec;
242  }
243 
253  inline boost::system::error_code connect(const boost::asio::ip::address& remote_addr,
254  const unsigned int remote_port)
255  {
256  boost::asio::ip::tcp::endpoint tcp_endpoint(remote_addr, remote_port);
257  return connect(tcp_endpoint);
258  }
259 
269  inline boost::system::error_code connect(const std::string& remote_server,
270  const unsigned int remote_port)
271  {
272  // query a list of matching endpoints
273  boost::system::error_code ec;
274  boost::asio::ip::tcp::resolver resolver(m_ssl_socket.lowest_layer().get_io_service());
275  boost::asio::ip::tcp::resolver::query query(remote_server,
276  boost::lexical_cast<std::string>(remote_port),
277  boost::asio::ip::tcp::resolver::query::numeric_service);
278  boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query, ec);
279  if (ec)
280  return ec;
281 
282  // try each one until we are successful
283  ec = boost::asio::error::host_not_found;
284  boost::asio::ip::tcp::resolver::iterator end;
285  while (ec && endpoint_iterator != end) {
286  boost::asio::ip::tcp::endpoint ep(endpoint_iterator->endpoint());
287  ++endpoint_iterator;
288  ec = connect(ep);
289  if (ec)
290  close();
291  }
292 
293  return ec;
294  }
295 
303  template <typename SSLHandshakeHandler>
304  inline void async_handshake_client(SSLHandshakeHandler handler) {
305 #ifdef PION_HAVE_SSL
306  m_ssl_socket.async_handshake(boost::asio::ssl::stream_base::client, handler);
307  m_ssl_flag = true;
308 #endif
309  }
310 
318  template <typename SSLHandshakeHandler>
319  inline void async_handshake_server(SSLHandshakeHandler handler) {
320 #ifdef PION_HAVE_SSL
321  m_ssl_socket.async_handshake(boost::asio::ssl::stream_base::server, handler);
322  m_ssl_flag = true;
323 #endif
324  }
325 
333  inline boost::system::error_code handshake_client(void) {
334  boost::system::error_code ec;
335 #ifdef PION_HAVE_SSL
336  m_ssl_socket.handshake(boost::asio::ssl::stream_base::client, ec);
337  m_ssl_flag = true;
338 #endif
339  return ec;
340  }
341 
349  inline boost::system::error_code handshake_server(void) {
350  boost::system::error_code ec;
351 #ifdef PION_HAVE_SSL
352  m_ssl_socket.handshake(boost::asio::ssl::stream_base::server, ec);
353  m_ssl_flag = true;
354 #endif
355  return ec;
356  }
357 
365  template <typename ReadHandler>
366  inline void async_read_some(ReadHandler handler) {
367 #ifdef PION_HAVE_SSL
368  if (getSSLFlag())
369  m_ssl_socket.async_read_some(boost::asio::buffer(m_read_buffer),
370  handler);
371  else
372 #endif
373  m_ssl_socket.next_layer().async_read_some(boost::asio::buffer(m_read_buffer),
374  handler);
375  }
376 
385  template <typename ReadBufferType, typename ReadHandler>
386  inline void async_read_some(ReadBufferType read_buffer,
387  ReadHandler handler) {
388 #ifdef PION_HAVE_SSL
389  if (getSSLFlag())
390  m_ssl_socket.async_read_some(read_buffer, handler);
391  else
392 #endif
393  m_ssl_socket.next_layer().async_read_some(read_buffer, handler);
394  }
395 
404  inline std::size_t read_some(boost::system::error_code& ec) {
405 #ifdef PION_HAVE_SSL
406  if (getSSLFlag())
407  return m_ssl_socket.read_some(boost::asio::buffer(m_read_buffer), ec);
408  else
409 #endif
410  return m_ssl_socket.next_layer().read_some(boost::asio::buffer(m_read_buffer), ec);
411  }
412 
422  template <typename ReadBufferType>
423  inline std::size_t read_some(ReadBufferType read_buffer,
424  boost::system::error_code& ec)
425  {
426 #ifdef PION_HAVE_SSL
427  if (getSSLFlag())
428  return m_ssl_socket.read_some(read_buffer, ec);
429  else
430 #endif
431  return m_ssl_socket.next_layer().read_some(read_buffer, ec);
432  }
433 
443  template <typename CompletionCondition, typename ReadHandler>
444  inline void async_read(CompletionCondition completion_condition,
445  ReadHandler handler)
446  {
447 #ifdef PION_HAVE_SSL
448  if (getSSLFlag())
449  boost::asio::async_read(m_ssl_socket, boost::asio::buffer(m_read_buffer),
450  completion_condition, handler);
451  else
452 #endif
453  boost::asio::async_read(m_ssl_socket.next_layer(), boost::asio::buffer(m_read_buffer),
454  completion_condition, handler);
455  }
456 
467  template <typename MutableBufferSequence, typename CompletionCondition, typename ReadHandler>
468  inline void async_read(const MutableBufferSequence& buffers,
469  CompletionCondition completion_condition,
470  ReadHandler handler)
471  {
472 #ifdef PION_HAVE_SSL
473  if (getSSLFlag())
474  boost::asio::async_read(m_ssl_socket, buffers,
475  completion_condition, handler);
476  else
477 #endif
478  boost::asio::async_read(m_ssl_socket.next_layer(), buffers,
479  completion_condition, handler);
480  }
481 
492  template <typename CompletionCondition>
493  inline std::size_t read(CompletionCondition completion_condition,
494  boost::system::error_code& ec)
495  {
496 #ifdef PION_HAVE_SSL
497  if (getSSLFlag())
498  return boost::asio::async_read(m_ssl_socket, boost::asio::buffer(m_read_buffer),
499  completion_condition, ec);
500  else
501 #endif
502  return boost::asio::async_read(m_ssl_socket.next_layer(), boost::asio::buffer(m_read_buffer),
503  completion_condition, ec);
504  }
505 
517  template <typename MutableBufferSequence, typename CompletionCondition>
518  inline std::size_t read(const MutableBufferSequence& buffers,
519  CompletionCondition completion_condition,
520  boost::system::error_code& ec)
521  {
522 #ifdef PION_HAVE_SSL
523  if (getSSLFlag())
524  return boost::asio::read(m_ssl_socket, buffers,
525  completion_condition, ec);
526  else
527 #endif
528  return boost::asio::read(m_ssl_socket.next_layer(), buffers,
529  completion_condition, ec);
530  }
531 
540  template <typename ConstBufferSequence, typename WriteHandler>
541  inline void async_write(const ConstBufferSequence& buffers, WriteHandler handler) {
542 #ifdef PION_HAVE_SSL
543  if (getSSLFlag())
544  boost::asio::async_write(m_ssl_socket, buffers, handler);
545  else
546 #endif
547  boost::asio::async_write(m_ssl_socket.next_layer(), buffers, handler);
548  }
549 
559  template <typename ConstBufferSequence>
560  inline std::size_t write(const ConstBufferSequence& buffers,
561  boost::system::error_code& ec)
562  {
563 #ifdef PION_HAVE_SSL
564  if (getSSLFlag())
565  return boost::asio::write(m_ssl_socket, buffers,
566  boost::asio::transfer_all(), ec);
567  else
568 #endif
569  return boost::asio::write(m_ssl_socket.next_layer(), buffers,
570  boost::asio::transfer_all(), ec);
571  }
572 
573 
576  inline void finish(void) { if (m_finished_handler) m_finished_handler(shared_from_this()); }
577 
579  inline bool getSSLFlag(void) const { return m_ssl_flag; }
580 
582  inline void setLifecycle(LifecycleType t) { m_lifecycle = t; }
583 
585  inline LifecycleType getLifecycle(void) const { return m_lifecycle; }
586 
588  inline bool getKeepAlive(void) const { return m_lifecycle != LIFECYCLE_CLOSE; }
589 
591  inline bool getPipelined(void) const { return m_lifecycle == LIFECYCLE_PIPELINED; }
592 
594  inline ReadBuffer& getReadBuffer(void) { return m_read_buffer; }
595 
602  inline void saveReadPosition(const char *read_ptr, const char *read_end_ptr) {
603  m_read_position.first = read_ptr;
604  m_read_position.second = read_end_ptr;
605  }
606 
613  inline void loadReadPosition(const char *&read_ptr, const char *&read_end_ptr) const {
614  read_ptr = m_read_position.first;
615  read_end_ptr = m_read_position.second;
616  }
617 
619  inline boost::asio::ip::tcp::endpoint getRemoteEndpoint(void) const {
620  boost::asio::ip::tcp::endpoint remote_endpoint;
621  try {
622  // const_cast is required since lowest_layer() is only defined non-const in asio
623  remote_endpoint = const_cast<SSLSocket&>(m_ssl_socket).lowest_layer().remote_endpoint();
624  } catch (boost::system::system_error& /* e */) {
625  // do nothing
626  }
627  return remote_endpoint;
628  }
629 
631  inline boost::asio::ip::address getRemoteIp(void) const {
632  return getRemoteEndpoint().address();
633  }
634 
636  inline unsigned short getRemotePort(void) const {
637  return getRemoteEndpoint().port();
638  }
639 
641  inline boost::asio::io_service& getIOService(void) {
642  return m_ssl_socket.lowest_layer().get_io_service();
643  }
644 
646  inline Socket& getSocket(void) { return m_ssl_socket.next_layer(); }
647 
649  inline SSLSocket& getSSLSocket(void) { return m_ssl_socket; }
650 
652  inline const Socket& getSocket(void) const { return const_cast<SSLSocket&>(m_ssl_socket).next_layer(); }
653 
655  inline const SSLSocket& getSSLSocket(void) const { return m_ssl_socket; }
656 
657 
658 protected:
659 
669  TCPConnection(boost::asio::io_service& io_service,
670  SSLContext& ssl_context,
671  const bool ssl_flag,
672  ConnectionHandler finished_handler)
673  :
674 #ifdef PION_HAVE_SSL
675  m_ssl_context(io_service, boost::asio::ssl::context::sslv23),
676  m_ssl_socket(io_service, ssl_context), m_ssl_flag(ssl_flag),
677 #else
678  m_ssl_context(0),
679  m_ssl_socket(io_service), m_ssl_flag(false),
680 #endif
681  m_lifecycle(LIFECYCLE_CLOSE),
682  m_finished_handler(finished_handler)
683  {
684  saveReadPosition(NULL, NULL);
685  }
686 
687 
688 private:
689 
691  typedef std::pair<const char*, const char*> ReadPosition;
692 
693 
695  SSLContext m_ssl_context;
696 
698  SSLSocket m_ssl_socket;
699 
701  bool m_ssl_flag;
702 
704  ReadBuffer m_read_buffer;
705 
707  ReadPosition m_read_position;
708 
710  LifecycleType m_lifecycle;
711 
713  ConnectionHandler m_finished_handler;
714 };
715 
716 
718 typedef boost::shared_ptr<TCPConnection> TCPConnectionPtr;
719 
720 
721 } // end namespace net
722 } // end namespace pion
723 
724 #endif
unsigned short getRemotePort(void) const
returns the client's port number
const SSLSocket & getSSLSocket(void) const
returns const reference to underlying SSL socket object
boost::system::error_code connect(boost::asio::ip::tcp::endpoint &tcp_endpoint)
bool getKeepAlive(void) const
returns true if the connection should be kept alive
std::size_t read(const MutableBufferSequence &buffers, CompletionCondition completion_condition, boost::system::error_code &ec)
void async_accept(boost::asio::ip::tcp::acceptor &tcp_acceptor, AcceptHandler handler)
bool getPipelined(void) const
returns true if the HTTP requests are pipelined
boost::asio::ip::tcp::endpoint getRemoteEndpoint(void) const
returns an ASIO endpoint for the client connection
boost::system::error_code handshake_client(void)
boost::asio::ip::address getRemoteIp(void) const
returns the client's IP address
void async_read(const MutableBufferSequence &buffers, CompletionCondition completion_condition, ReadHandler handler)
boost::system::error_code connect(const boost::asio::ip::address &remote_addr, const unsigned int remote_port)
void setLifecycle(LifecycleType t)
sets the lifecycle type for the connection
LifecycleType getLifecycle(void) const
returns the lifecycle type for the connection
std::size_t read_some(ReadBufferType read_buffer, boost::system::error_code &ec)
static boost::shared_ptr< TCPConnection > create(boost::asio::io_service &io_service, SSLContext &ssl_context, const bool ssl_flag, ConnectionHandler finished_handler)
void async_read_some(ReadHandler handler)
void async_handshake_client(SSLHandshakeHandler handler)
boost::asio::io_service & getIOService(void)
returns reference to the io_service used for async operations
LifecycleType
data type for the connection's lifecycle state
void async_connect(boost::asio::ip::tcp::endpoint &tcp_endpoint, ConnectHandler handler)
void saveReadPosition(const char *read_ptr, const char *read_end_ptr)
std::size_t read_some(boost::system::error_code &ec)
void async_read(CompletionCondition completion_condition, ReadHandler handler)
bool getSSLFlag(void) const
returns true if the connection is encrypted using SSL
const Socket & getSocket(void) const
returns const reference to underlying TCP socket object
boost::function1< void, boost::shared_ptr< TCPConnection > > ConnectionHandler
data type for a function that handles TCP connection objects
std::size_t read(CompletionCondition completion_condition, boost::system::error_code &ec)
boost::system::error_code accept(boost::asio::ip::tcp::acceptor &tcp_acceptor)
boost::array< char, READ_BUFFER_SIZE > ReadBuffer
data type for an I/O read buffer
void async_connect(const boost::asio::ip::address &remote_addr, const unsigned int remote_port, ConnectHandler handler)
void async_handshake_server(SSLHandshakeHandler handler)
void close(void)
closes the tcp socket and cancels any pending asynchronous operations
boost::system::error_code handshake_server(void)
boost::system::error_code connect(const std::string &remote_server, const unsigned int remote_port)
TCPConnection(boost::asio::io_service &io_service, SSLContext &ssl_context, const bool ssl_flag, ConnectionHandler finished_handler)
void loadReadPosition(const char *&read_ptr, const char *&read_end_ptr) const
TCPConnection(boost::asio::io_service &io_service, SSLContext &ssl_context)
std::size_t write(const ConstBufferSequence &buffers, boost::system::error_code &ec)
Socket & getSocket(void)
returns non-const reference to underlying TCP socket object
void async_read_some(ReadBufferType read_buffer, ReadHandler handler)
bool is_open(void) const
returns true if the connection is currently open
SSLSocket & getSSLSocket(void)
returns non-const reference to underlying SSL socket object
boost::asio::ip::tcp::socket Socket
data type for a socket connection
virtual ~TCPConnection()
virtual destructor
void async_write(const ConstBufferSequence &buffers, WriteHandler handler)
TCPConnection(boost::asio::io_service &io_service, const bool ssl_flag=false)
ReadBuffer & getReadBuffer(void)
returns the buffer used for reading data from the TCP connection