staticlib_pion  2.0.2
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Friends Macros
tcp_connection.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2015, alex at staticlibs.net
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 // ---------------------------------------------------------------------
18 // pion: a Boost C++ framework for building lightweight HTTP interfaces
19 // ---------------------------------------------------------------------
20 // Copyright (C) 2007-2014 Splunk Inc. (https://github.com/splunk/pion)
21 //
22 // Distributed under the Boost Software License, Version 1.0.
23 // See http://www.boost.org/LICENSE_1_0.txt
24 //
25 
26 #ifndef STATICLIB_PION_TCP_CONNECTION_HPP
27 #define STATICLIB_PION_TCP_CONNECTION_HPP
28 
29 #include <array>
30 #include <functional>
31 #include <memory>
32 #include <string>
33 
34 #include "asio.hpp"
35 #include "asio/ssl.hpp"
36 
37 #include "staticlib/config.hpp"
38 
40 
41 namespace staticlib {
42 namespace pion {
43 
47 class tcp_connection : public std::enable_shared_from_this<tcp_connection> {
48 
49 public:
50 
54  enum class lifecycle {
55  close,
56  keepalive,
57  pipelined
58  };
59 
63  using connection_handler = std::function<void(std::shared_ptr<tcp_connection>&)>;
64 
68  using read_buffer_type = std::array<char, 8192>;
69 
73  using socket_type = asio::ip::tcp::socket;
74 
78  using ssl_socket_type = asio::ssl::stream<asio::ip::tcp::socket>;
79 
83  using ssl_context_type = asio::ssl::context;
84 
85 private:
86 
90  ssl_socket_type ssl_socket;
91 
95  bool ssl_flag;
96 
100  read_buffer_type read_buffer;
101 
105  std::pair<const char*, const char*> read_position;
106 
110  lifecycle current_lifecycle;
111 
115  connection_handler finished_handler;
116 
120  asio::io_service::strand strand;
121 
125  asio::steady_timer timer;
126 
127 public:
128 
138  tcp_connection(asio::io_service& io_service, ssl_context_type& ssl_context, const bool ssl_flag_in,
139  connection_handler finished_handler_in) :
140  ssl_socket(io_service, ssl_context),
141  ssl_flag(ssl_flag_in),
142  current_lifecycle(lifecycle::close),
143  finished_handler(finished_handler_in),
144  strand(io_service),
145  timer(io_service) {
146  save_read_pos(nullptr, nullptr);
147  }
148 
152  tcp_connection(const tcp_connection&) = delete;
153 
157  tcp_connection& operator=(const tcp_connection&) = delete;
158 
162  virtual ~tcp_connection() {
163  close();
164  }
165 
171  bool is_open() const {
172  return const_cast<ssl_socket_type&> (ssl_socket).lowest_layer().is_open();
173  }
174 
178  void close() {
179  if (is_open()) {
180  try {
181  // shutting down SSL will wait forever for a response from the remote end,
182  // which causes it to hang indefinitely if the other end died unexpectedly
183  // if (get_ssl_flag()) ssl_socket.shutdown();
184 
185  // windows seems to require this otherwise it doesn't
186  // recognize that connections have been closed
187  ssl_socket.next_layer().shutdown(asio::ip::tcp::socket::shutdown_both);
188  } catch (...) {
189  } // ignore exceptions
190 
191  // close the underlying socket (ignore errors)
192  std::error_code ec;
193  ssl_socket.next_layer().close(ec);
194  }
195  }
196 
200  void cancel() {
201  // there is no good way to do this on windows until vista or later (0x0600)
202  // http://www.boost.org/doc/libs/1_53_0/doc/html/boost_asio/reference/basic_stream_socket/cancel/overload2.html
203  // note that the asio docs are misleading because close() is not thread-safe,
204  // and the suggested #define statements cause WAY too much trouble and heartache
205  #if !defined(_MSC_VER) || (_WIN32_WINNT >= 0x0600)
206  std::error_code ec;
207  ssl_socket.next_layer().cancel(ec);
208  #endif // !WINXP
209  }
210 
214  void cancel_timer() {
215  #if !defined(_MSC_VER) || (_WIN32_WINNT >= 0x0600)
216  std::error_code ec;
217  timer.cancel(ec);
218  #endif // !WINXP
219  }
220 
229  template <typename AcceptHandler>
230  void async_accept(asio::ip::tcp::acceptor& tcp_acceptor, AcceptHandler handler) {
231  tcp_acceptor.async_accept(ssl_socket.lowest_layer(), handler);
232  }
233 
241  template <typename SSLHandshakeHandler>
242  void async_handshake_server(SSLHandshakeHandler handler) {
243  ssl_socket.async_handshake(asio::ssl::stream_base::server, handler);
244  ssl_flag = true;
245  }
246 
254  template <typename ReadHandler>
255  void async_read_some(ReadHandler handler) {
256  if (get_ssl_flag()) {
257  ssl_socket.async_read_some(asio::buffer(read_buffer), handler);
258  } else {
259  ssl_socket.next_layer().async_read_some(asio::buffer(read_buffer), handler);
260  }
261  }
262 
271  template <typename ConstBufferSequence, typename write_handler_t>
272  void async_write(const ConstBufferSequence& buffers, write_handler_t handler) {
273  if (get_ssl_flag()) {
274  asio::async_write(ssl_socket, buffers, handler);
275  } else {
276  asio::async_write(ssl_socket.next_layer(), buffers, handler);
277  }
278  }
279 
283  void finish() {
284  auto conn = shared_from_this();
285  if (finished_handler) {
286  finished_handler(conn);
287  }
288  }
289 
295  bool get_ssl_flag() const {
296  return ssl_flag;
297  }
298 
304  void set_lifecycle(lifecycle lcycle) {
305  current_lifecycle = lcycle;
306  }
307 
313  bool get_keep_alive() const {
314  return current_lifecycle != lifecycle::close;
315  }
316 
322  bool is_pipelined() const {
323  return current_lifecycle == lifecycle::pipelined;
324  }
325 
331  read_buffer_type& get_read_buffer() {
332  return read_buffer;
333  }
334 
341  void save_read_pos(const char *read_ptr, const char *read_end_ptr) {
342  read_position.first = read_ptr;
343  read_position.second = read_end_ptr;
344  }
345 
352  void load_read_pos(const char *&read_ptr, const char *&read_end_ptr) const {
353  read_ptr = read_position.first;
354  read_end_ptr = read_position.second;
355  }
356 
362  asio::ip::tcp::endpoint get_remote_endpoint() const {
363  asio::ip::tcp::endpoint remote_endpoint;
364  try {
365  // const_cast is required since lowest_layer() is only defined non-const in asio
366  remote_endpoint = const_cast<ssl_socket_type&> (ssl_socket).lowest_layer().remote_endpoint();
367  } catch (asio::system_error& /* e */) {
368  // do nothing
369  }
370  return remote_endpoint;
371  }
372 
378  asio::ip::address get_remote_ip() const {
379  return get_remote_endpoint().address();
380  }
381 
387  unsigned short get_remote_port() const {
388  return get_remote_endpoint().port();
389  }
390 
396  asio::io_service& get_io_service() {
397  return ssl_socket.lowest_layer().get_io_service();
398  }
399 
405  socket_type& get_socket() {
406  return ssl_socket.next_layer();
407  }
408 
414  ssl_socket_type& get_ssl_socket() {
415  return ssl_socket;
416  }
417 
423  asio::io_service::strand& get_strand() {
424  return strand;
425  }
426 
432  asio::steady_timer& get_timer() {
433  return timer;
434  }
435 
436 };
437 
441 using tcp_connection_ptr = std::shared_ptr<tcp_connection>;
442 
443 } // namespace
444 }
445 
446 #endif // STATICLIB_PION_TCP_CONNECTION_HPP