pion  5.0.6
connection.hpp
1 // ---------------------------------------------------------------------
2 // pion: a Boost C++ framework for building lightweight HTTP interfaces
3 // ---------------------------------------------------------------------
4 // Copyright (C) 2007-2014 Splunk Inc. (https://github.com/splunk/pion)
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_TCP_CONNECTION_HEADER__
11 #define __PION_TCP_CONNECTION_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/config.hpp>
30 #include <string>
31 
32 
33 namespace pion { // begin namespace pion
34 namespace tcp { // begin namespace tcp
35 
36 
40 class connection :
41  public boost::enable_shared_from_this<connection>,
42  private boost::noncopyable
43 {
44 public:
45 
48  LIFECYCLE_CLOSE, LIFECYCLE_KEEPALIVE, LIFECYCLE_PIPELINED
49  };
50 
52  enum { READ_BUFFER_SIZE = 8192 };
53 
55  typedef boost::function1<void, boost::shared_ptr<connection> > connection_handler;
56 
58  typedef boost::array<char, READ_BUFFER_SIZE> read_buffer_type;
59 
61  typedef boost::asio::ip::tcp::socket socket_type;
62 
63 #ifdef PION_HAVE_SSL
64  typedef boost::asio::ssl::stream<boost::asio::ip::tcp::socket> ssl_socket_type;
66 
68  typedef boost::asio::ssl::context ssl_context_type;
69 #else
70  class ssl_socket_type {
71  public:
72  ssl_socket_type(boost::asio::io_service& io_service) : m_socket(io_service) {}
73  inline socket_type& next_layer(void) { return m_socket; }
74  inline const socket_type& next_layer(void) const { return m_socket; }
75  inline socket_type::lowest_layer_type& lowest_layer(void) { return m_socket.lowest_layer(); }
76  inline const socket_type::lowest_layer_type& lowest_layer(void) const { return m_socket.lowest_layer(); }
77  inline void shutdown(void) {}
78  private:
79  socket_type m_socket;
80  };
81  typedef int ssl_context_type;
82 #endif
83 
84 
94  static inline boost::shared_ptr<connection> create(boost::asio::io_service& io_service,
95  ssl_context_type& ssl_context,
96  const bool ssl_flag,
97  connection_handler finished_handler)
98  {
99  return boost::shared_ptr<connection>(new connection(io_service, ssl_context,
100  ssl_flag, finished_handler));
101  }
102 
109  explicit connection(boost::asio::io_service& io_service, const bool ssl_flag = false)
110  :
111 #ifdef PION_HAVE_SSL
112  m_ssl_context(boost::asio::ssl::context::sslv23),
113  m_ssl_socket(io_service, m_ssl_context),
114  m_ssl_flag(ssl_flag),
115 #else
116  m_ssl_context(0),
117  m_ssl_socket(io_service),
118  m_ssl_flag(false),
119 #endif
120  m_lifecycle(LIFECYCLE_CLOSE)
121  {
122  save_read_pos(NULL, NULL);
123  }
124 
131  connection(boost::asio::io_service& io_service, ssl_context_type& ssl_context)
132  :
133 #ifdef PION_HAVE_SSL
134  m_ssl_context(boost::asio::ssl::context::sslv23),
135  m_ssl_socket(io_service, ssl_context), m_ssl_flag(true),
136 #else
137  m_ssl_context(0),
138  m_ssl_socket(io_service), m_ssl_flag(false),
139 #endif
140  m_lifecycle(LIFECYCLE_CLOSE)
141  {
142  save_read_pos(NULL, NULL);
143  }
144 
146  inline bool is_open(void) const {
147  return const_cast<ssl_socket_type&>(m_ssl_socket).lowest_layer().is_open();
148  }
149 
151  inline void close(void) {
152  if (is_open()) {
153  try {
154 
155  // shutting down SSL will wait forever for a response from the remote end,
156  // which causes it to hang indefinitely if the other end died unexpectedly
157  // if (get_ssl_flag()) m_ssl_socket.shutdown();
158 
159  // windows seems to require this otherwise it doesn't
160  // recognize that connections have been closed
161  m_ssl_socket.next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both);
162 
163  } catch (...) {} // ignore exceptions
164 
165  // close the underlying socket (ignore errors)
166  boost::system::error_code ec;
167  m_ssl_socket.next_layer().close(ec);
168  }
169  }
170 
176  inline void cancel(void) {
177 #if !defined(_MSC_VER) || (_WIN32_WINNT >= 0x0600)
178  boost::system::error_code ec;
179  m_ssl_socket.next_layer().cancel(ec);
180 #endif
181  }
182 
184  virtual ~connection() { close(); }
185 
194  template <typename AcceptHandler>
195  inline void async_accept(boost::asio::ip::tcp::acceptor& tcp_acceptor,
196  AcceptHandler handler)
197  {
198  tcp_acceptor.async_accept(m_ssl_socket.lowest_layer(), handler);
199  }
200 
209  inline boost::system::error_code accept(boost::asio::ip::tcp::acceptor& tcp_acceptor)
210  {
211  boost::system::error_code ec;
212  tcp_acceptor.accept(m_ssl_socket.lowest_layer(), ec);
213  return ec;
214  }
215 
224  template <typename ConnectHandler>
225  inline void async_connect(const boost::asio::ip::tcp::endpoint& tcp_endpoint,
226  ConnectHandler handler)
227  {
228  m_ssl_socket.lowest_layer().async_connect(tcp_endpoint, handler);
229  }
230 
240  template <typename ConnectHandler>
241  inline void async_connect(const boost::asio::ip::address& remote_addr,
242  const unsigned int remote_port,
243  ConnectHandler handler)
244  {
245  boost::asio::ip::tcp::endpoint tcp_endpoint(remote_addr, remote_port);
246  async_connect(tcp_endpoint, handler);
247  }
248 
257  inline boost::system::error_code connect(boost::asio::ip::tcp::endpoint& tcp_endpoint)
258  {
259  boost::system::error_code ec;
260  m_ssl_socket.lowest_layer().connect(tcp_endpoint, ec);
261  return ec;
262  }
263 
273  inline boost::system::error_code connect(const boost::asio::ip::address& remote_addr,
274  const unsigned int remote_port)
275  {
276  boost::asio::ip::tcp::endpoint tcp_endpoint(remote_addr, remote_port);
277  return connect(tcp_endpoint);
278  }
279 
289  inline boost::system::error_code connect(const std::string& remote_server,
290  const unsigned int remote_port)
291  {
292  // query a list of matching endpoints
293  boost::system::error_code ec;
294  boost::asio::ip::tcp::resolver resolver(m_ssl_socket.lowest_layer().get_io_service());
295  boost::asio::ip::tcp::resolver::query query(remote_server,
296  boost::lexical_cast<std::string>(remote_port),
297  boost::asio::ip::tcp::resolver::query::numeric_service);
298  boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query, ec);
299  if (ec)
300  return ec;
301 
302  // try each one until we are successful
303  ec = boost::asio::error::host_not_found;
304  boost::asio::ip::tcp::resolver::iterator end;
305  while (ec && endpoint_iterator != end) {
306  boost::asio::ip::tcp::endpoint ep(endpoint_iterator->endpoint());
307  ++endpoint_iterator;
308  ec = connect(ep);
309  if (ec)
310  close();
311  }
312 
313  return ec;
314  }
315 
323  template <typename SSLHandshakeHandler>
324  inline void async_handshake_client(SSLHandshakeHandler handler) {
325 #ifdef PION_HAVE_SSL
326  m_ssl_socket.async_handshake(boost::asio::ssl::stream_base::client, handler);
327  m_ssl_flag = true;
328 #endif
329  }
330 
338  template <typename SSLHandshakeHandler>
339  inline void async_handshake_server(SSLHandshakeHandler handler) {
340 #ifdef PION_HAVE_SSL
341  m_ssl_socket.async_handshake(boost::asio::ssl::stream_base::server, handler);
342  m_ssl_flag = true;
343 #endif
344  }
345 
353  inline boost::system::error_code handshake_client(void) {
354  boost::system::error_code ec;
355 #ifdef PION_HAVE_SSL
356  m_ssl_socket.handshake(boost::asio::ssl::stream_base::client, ec);
357  m_ssl_flag = true;
358 #endif
359  return ec;
360  }
361 
369  inline boost::system::error_code handshake_server(void) {
370  boost::system::error_code ec;
371 #ifdef PION_HAVE_SSL
372  m_ssl_socket.handshake(boost::asio::ssl::stream_base::server, ec);
373  m_ssl_flag = true;
374 #endif
375  return ec;
376  }
377 
385  template <typename ReadHandler>
386  inline void async_read_some(ReadHandler handler) {
387 #ifdef PION_HAVE_SSL
388  if (get_ssl_flag())
389  m_ssl_socket.async_read_some(boost::asio::buffer(m_read_buffer),
390  handler);
391  else
392 #endif
393  m_ssl_socket.next_layer().async_read_some(boost::asio::buffer(m_read_buffer),
394  handler);
395  }
396 
405  template <typename ReadBufferType, typename ReadHandler>
406  inline void async_read_some(ReadBufferType read_buffer,
407  ReadHandler handler) {
408 #ifdef PION_HAVE_SSL
409  if (get_ssl_flag())
410  m_ssl_socket.async_read_some(read_buffer, handler);
411  else
412 #endif
413  m_ssl_socket.next_layer().async_read_some(read_buffer, handler);
414  }
415 
424  inline std::size_t read_some(boost::system::error_code& ec) {
425 #ifdef PION_HAVE_SSL
426  if (get_ssl_flag())
427  return m_ssl_socket.read_some(boost::asio::buffer(m_read_buffer), ec);
428  else
429 #endif
430  return m_ssl_socket.next_layer().read_some(boost::asio::buffer(m_read_buffer), ec);
431  }
432 
442  template <typename ReadBufferType>
443  inline std::size_t read_some(ReadBufferType read_buffer,
444  boost::system::error_code& ec)
445  {
446 #ifdef PION_HAVE_SSL
447  if (get_ssl_flag())
448  return m_ssl_socket.read_some(read_buffer, ec);
449  else
450 #endif
451  return m_ssl_socket.next_layer().read_some(read_buffer, ec);
452  }
453 
463  template <typename CompletionCondition, typename ReadHandler>
464  inline void async_read(CompletionCondition completion_condition,
465  ReadHandler handler)
466  {
467 #ifdef PION_HAVE_SSL
468  if (get_ssl_flag())
469  boost::asio::async_read(m_ssl_socket, boost::asio::buffer(m_read_buffer),
470  completion_condition, handler);
471  else
472 #endif
473  boost::asio::async_read(m_ssl_socket.next_layer(), boost::asio::buffer(m_read_buffer),
474  completion_condition, handler);
475  }
476 
487  template <typename MutableBufferSequence, typename CompletionCondition, typename ReadHandler>
488  inline void async_read(const MutableBufferSequence& buffers,
489  CompletionCondition completion_condition,
490  ReadHandler handler)
491  {
492 #ifdef PION_HAVE_SSL
493  if (get_ssl_flag())
494  boost::asio::async_read(m_ssl_socket, buffers,
495  completion_condition, handler);
496  else
497 #endif
498  boost::asio::async_read(m_ssl_socket.next_layer(), buffers,
499  completion_condition, handler);
500  }
501 
512  template <typename CompletionCondition>
513  inline std::size_t read(CompletionCondition completion_condition,
514  boost::system::error_code& ec)
515  {
516 #ifdef PION_HAVE_SSL
517  if (get_ssl_flag())
518  return boost::asio::async_read(m_ssl_socket, boost::asio::buffer(m_read_buffer),
519  completion_condition, ec);
520  else
521 #endif
522  return boost::asio::async_read(m_ssl_socket.next_layer(), boost::asio::buffer(m_read_buffer),
523  completion_condition, ec);
524  }
525 
537  template <typename MutableBufferSequence, typename CompletionCondition>
538  inline std::size_t read(const MutableBufferSequence& buffers,
539  CompletionCondition completion_condition,
540  boost::system::error_code& ec)
541  {
542 #ifdef PION_HAVE_SSL
543  if (get_ssl_flag())
544  return boost::asio::read(m_ssl_socket, buffers,
545  completion_condition, ec);
546  else
547 #endif
548  return boost::asio::read(m_ssl_socket.next_layer(), buffers,
549  completion_condition, ec);
550  }
551 
560  template <typename ConstBufferSequence, typename write_handler_t>
561  inline void async_write(const ConstBufferSequence& buffers, write_handler_t handler) {
562 #ifdef PION_HAVE_SSL
563  if (get_ssl_flag())
564  boost::asio::async_write(m_ssl_socket, buffers, handler);
565  else
566 #endif
567  boost::asio::async_write(m_ssl_socket.next_layer(), buffers, handler);
568  }
569 
579  template <typename ConstBufferSequence>
580  inline std::size_t write(const ConstBufferSequence& buffers,
581  boost::system::error_code& ec)
582  {
583 #ifdef PION_HAVE_SSL
584  if (get_ssl_flag())
585  return boost::asio::write(m_ssl_socket, buffers,
586  boost::asio::transfer_all(), ec);
587  else
588 #endif
589  return boost::asio::write(m_ssl_socket.next_layer(), buffers,
590  boost::asio::transfer_all(), ec);
591  }
592 
593 
596  inline void finish(void) { if (m_finished_handler) m_finished_handler(shared_from_this()); }
597 
599  inline bool get_ssl_flag(void) const { return m_ssl_flag; }
600 
602  inline void set_lifecycle(lifecycle_type t) { m_lifecycle = t; }
603 
605  inline lifecycle_type get_lifecycle(void) const { return m_lifecycle; }
606 
608  inline bool get_keep_alive(void) const { return m_lifecycle != LIFECYCLE_CLOSE; }
609 
611  inline bool get_pipelined(void) const { return m_lifecycle == LIFECYCLE_PIPELINED; }
612 
614  inline read_buffer_type& get_read_buffer(void) { return m_read_buffer; }
615 
622  inline void save_read_pos(const char *read_ptr, const char *read_end_ptr) {
623  m_read_position.first = read_ptr;
624  m_read_position.second = read_end_ptr;
625  }
626 
633  inline void load_read_pos(const char *&read_ptr, const char *&read_end_ptr) const {
634  read_ptr = m_read_position.first;
635  read_end_ptr = m_read_position.second;
636  }
637 
639  inline boost::asio::ip::tcp::endpoint get_remote_endpoint(void) const {
640  boost::asio::ip::tcp::endpoint remote_endpoint;
641  try {
642  // const_cast is required since lowest_layer() is only defined non-const in asio
643  remote_endpoint = const_cast<ssl_socket_type&>(m_ssl_socket).lowest_layer().remote_endpoint();
644  } catch (boost::system::system_error& /* e */) {
645  // do nothing
646  }
647  return remote_endpoint;
648  }
649 
651  inline boost::asio::ip::address get_remote_ip(void) const {
652  return get_remote_endpoint().address();
653  }
654 
656  inline unsigned short get_remote_port(void) const {
657  return get_remote_endpoint().port();
658  }
659 
661  inline boost::asio::io_service& get_io_service(void) {
662  return m_ssl_socket.lowest_layer().get_io_service();
663  }
664 
666  inline socket_type& get_socket(void) { return m_ssl_socket.next_layer(); }
667 
669  inline ssl_socket_type& get_ssl_socket(void) { return m_ssl_socket; }
670 
672  inline const socket_type& get_socket(void) const { return const_cast<ssl_socket_type&>(m_ssl_socket).next_layer(); }
673 
675  inline const ssl_socket_type& get_ssl_socket(void) const { return m_ssl_socket; }
676 
677 
678 protected:
679 
689  connection(boost::asio::io_service& io_service,
690  ssl_context_type& ssl_context,
691  const bool ssl_flag,
692  connection_handler finished_handler)
693  :
694 #ifdef PION_HAVE_SSL
695  m_ssl_context(boost::asio::ssl::context::sslv23),
696  m_ssl_socket(io_service, ssl_context), m_ssl_flag(ssl_flag),
697 #else
698  m_ssl_context(0),
699  m_ssl_socket(io_service), m_ssl_flag(false),
700 #endif
701  m_lifecycle(LIFECYCLE_CLOSE),
702  m_finished_handler(finished_handler)
703  {
704  save_read_pos(NULL, NULL);
705  }
706 
707 
708 private:
709 
711  typedef std::pair<const char*, const char*> read_pos_type;
712 
713 
715  ssl_context_type m_ssl_context;
716 
718  ssl_socket_type m_ssl_socket;
719 
721  bool m_ssl_flag;
722 
724  read_buffer_type m_read_buffer;
725 
727  read_pos_type m_read_position;
728 
730  lifecycle_type m_lifecycle;
731 
733  connection_handler m_finished_handler;
734 };
735 
736 
738 typedef boost::shared_ptr<connection> connection_ptr;
739 
740 
741 } // end namespace tcp
742 } // end namespace pion
743 
744 #endif
void save_read_pos(const char *read_ptr, const char *read_end_ptr)
Definition: connection.hpp:622
lifecycle_type get_lifecycle(void) const
returns the lifecycle type for the connection
Definition: connection.hpp:605
boost::system::error_code handshake_client(void)
Definition: connection.hpp:353
connection(boost::asio::io_service &io_service, ssl_context_type &ssl_context, const bool ssl_flag, connection_handler finished_handler)
Definition: connection.hpp:689
boost::asio::ip::tcp::endpoint get_remote_endpoint(void) const
returns an ASIO endpoint for the client connection
Definition: connection.hpp:639
boost::system::error_code handshake_server(void)
Definition: connection.hpp:369
static boost::shared_ptr< connection > create(boost::asio::io_service &io_service, ssl_context_type &ssl_context, const bool ssl_flag, connection_handler finished_handler)
Definition: connection.hpp:94
boost::function1< void, boost::shared_ptr< connection > > connection_handler
data type for a function that handles TCP connection objects
Definition: connection.hpp:55
void async_handshake_client(SSLHandshakeHandler handler)
Definition: connection.hpp:324
boost::asio::ip::tcp::socket socket_type
data type for a socket connection
Definition: connection.hpp:61
std::size_t read(const MutableBufferSequence &buffers, CompletionCondition completion_condition, boost::system::error_code &ec)
Definition: connection.hpp:538
void async_read(CompletionCondition completion_condition, ReadHandler handler)
Definition: connection.hpp:464
boost::asio::ip::address get_remote_ip(void) const
returns the client&#39;s IP address
Definition: connection.hpp:651
std::size_t read(CompletionCondition completion_condition, boost::system::error_code &ec)
Definition: connection.hpp:513
void async_read(const MutableBufferSequence &buffers, CompletionCondition completion_condition, ReadHandler handler)
Definition: connection.hpp:488
boost::asio::io_service & get_io_service(void)
returns reference to the io_service used for async operations
Definition: connection.hpp:661
void async_handshake_server(SSLHandshakeHandler handler)
Definition: connection.hpp:339
std::size_t read_some(ReadBufferType read_buffer, boost::system::error_code &ec)
Definition: connection.hpp:443
virtual ~connection()
virtual destructor
Definition: connection.hpp:184
void close(void)
closes the tcp socket and cancels any pending asynchronous operations
Definition: connection.hpp:151
unsigned short get_remote_port(void) const
returns the client&#39;s port number
Definition: connection.hpp:656
std::size_t read_some(boost::system::error_code &ec)
Definition: connection.hpp:424
read_buffer_type & get_read_buffer(void)
returns the buffer used for reading data from the TCP connection
Definition: connection.hpp:614
void async_connect(const boost::asio::ip::address &remote_addr, const unsigned int remote_port, ConnectHandler handler)
Definition: connection.hpp:241
void async_read_some(ReadBufferType read_buffer, ReadHandler handler)
Definition: connection.hpp:406
boost::array< char, READ_BUFFER_SIZE > read_buffer_type
data type for an I/O read buffer
Definition: connection.hpp:58
socket_type & get_socket(void)
returns non-const reference to underlying TCP socket object
Definition: connection.hpp:666
bool get_pipelined(void) const
returns true if the HTTP requests are pipelined
Definition: connection.hpp:611
void async_accept(boost::asio::ip::tcp::acceptor &tcp_acceptor, AcceptHandler handler)
Definition: connection.hpp:195
void async_read_some(ReadHandler handler)
Definition: connection.hpp:386
const ssl_socket_type & get_ssl_socket(void) const
returns const reference to underlying SSL socket object
Definition: connection.hpp:675
bool get_keep_alive(void) const
returns true if the connection should be kept alive
Definition: connection.hpp:608
void set_lifecycle(lifecycle_type t)
sets the lifecycle type for the connection
Definition: connection.hpp:602
void load_read_pos(const char *&read_ptr, const char *&read_end_ptr) const
Definition: connection.hpp:633
ssl_socket_type & get_ssl_socket(void)
returns non-const reference to underlying SSL socket object
Definition: connection.hpp:669
boost::system::error_code connect(const boost::asio::ip::address &remote_addr, const unsigned int remote_port)
Definition: connection.hpp:273
lifecycle_type
data type for the connection&#39;s lifecycle state
Definition: connection.hpp:47
void async_write(const ConstBufferSequence &buffers, write_handler_t handler)
Definition: connection.hpp:561
void async_connect(const boost::asio::ip::tcp::endpoint &tcp_endpoint, ConnectHandler handler)
Definition: connection.hpp:225
bool get_ssl_flag(void) const
returns true if the connection is encrypted using SSL
Definition: connection.hpp:599
connection(boost::asio::io_service &io_service, ssl_context_type &ssl_context)
Definition: connection.hpp:131
std::size_t write(const ConstBufferSequence &buffers, boost::system::error_code &ec)
Definition: connection.hpp:580
const socket_type & get_socket(void) const
returns const reference to underlying TCP socket object
Definition: connection.hpp:672
boost::system::error_code accept(boost::asio::ip::tcp::acceptor &tcp_acceptor)
Definition: connection.hpp:209
bool is_open(void) const
returns true if the connection is currently open
Definition: connection.hpp:146
boost::system::error_code connect(const std::string &remote_server, const unsigned int remote_port)
Definition: connection.hpp:289
boost::system::error_code connect(boost::asio::ip::tcp::endpoint &tcp_endpoint)
Definition: connection.hpp:257
connection(boost::asio::io_service &io_service, const bool ssl_flag=false)
Definition: connection.hpp:109