CppWAMP
C++11 client library for the WAMP protocol
asioendpoint.hpp
1 /*------------------------------------------------------------------------------
2  Copyright Butterfly Energy Systems 2014-2015, 2022.
3  Distributed under the Boost Software License, Version 1.0.
4  http://www.boost.org/LICENSE_1_0.txt
5 ------------------------------------------------------------------------------*/
6 
7 #ifndef CPPWAMP_INTERNAL_ASIOENDPOINT_HPP
8 #define CPPWAMP_INTERNAL_ASIOENDPOINT_HPP
9 
10 #include <cstdint>
11 #include <functional>
12 #include <utility>
13 #include <boost/asio/buffer.hpp>
14 #include <boost/asio/read.hpp>
15 #include <boost/asio/strand.hpp>
16 #include <boost/asio/write.hpp>
17 #include "../error.hpp"
18 #include "asiotransport.hpp"
19 #include "rawsockhandshake.hpp"
20 
21 namespace wamp
22 {
23 
24 // Forward declaration to mock objects used in testing
25 class FakeMsgTypeAsioConnector;
26 class FakeMsgTypeAsioListener;
27 
28 namespace internal
29 {
30 
31 //------------------------------------------------------------------------------
32 template <typename TEstablisher,
33  template<typename> class TTransport = AsioTransport>
34 class AsioEndpoint
35 {
36 public:
37  using Establisher = TEstablisher;
38  using Socket = typename Establisher::Socket;
39  using Transport = TTransport<Socket>;
40  using TransportPtr = std::shared_ptr<Transport>;
41  using Handler = std::function<void (std::error_code, int codecId,
42  TransportPtr)>;
43 
44  explicit AsioEndpoint(Establisher&& est)
45  : strand_(est.strand()),
46  est_(std::move(est)),
47  handshake_(0)
48  {}
49 
50  virtual ~AsioEndpoint() {}
51 
52  void establish(Handler&& handler)
53  {
54  handler_ = std::move(handler);
55  try
56  {
57  // The Connector that initiated 'establish' must keep this object
58  // alive until completion.
59  est_.establish( [this](AsioErrorCode ec, SocketPtr&& socket)
60  {
61  if (check(ec))
62  {
63  socket_ = std::move(socket);
64  onEstablished();
65  }
66  });
67  }
68  catch (const boost::system::system_error& e)
69  {
70  check(e.code());
71  }
72  }
73 
74  void cancel()
75  {
76  if (socket_)
77  socket_->close();
78  else
79  est_.cancel();
80  }
81 
82 protected:
83  using Handshake = internal::RawsockHandshake;
84  using SocketPtr = typename Establisher::SocketPtr;
85 
86  void sendHandshake(Handshake hs)
87  {
88  handshake_ = hs.toBigEndian();
89  using boost::asio::buffer;
90  using boost::asio::async_write;
91  async_write(*socket_, buffer(&handshake_, sizeof(handshake_)),
92  [this, hs](AsioErrorCode ec, size_t)
93  {
94  if (check(ec))
95  onHandshakeSent(hs);
96  });
97  }
98 
99  void receiveHandshake()
100  {
101  handshake_ = 0;
102  using boost::asio::buffer;
103  using boost::asio::async_read;
104  async_read(*socket_, buffer(&handshake_, sizeof(handshake_)),
105  [this](AsioErrorCode ec, size_t)
106  {
107  if (check(ec))
108  onHandshakeReceived(Handshake::fromBigEndian(handshake_));
109  });
110  }
111 
112  void complete(int codecId, size_t maxTxLength, size_t maxRxLength)
113  {
114  auto transport = Transport::create(std::move(socket_), maxTxLength,
115  maxRxLength);
116  std::error_code ec = make_error_code(TransportErrc::success);
117  postHandler(ec, codecId, std::move(transport));
118  socket_.reset();
119  handler_ = nullptr;
120  }
121 
122  void fail(RawsockErrc errc)
123  {
124  socket_.reset();
125  postHandler(make_error_code(errc), 0, nullptr);
126  handler_ = nullptr;
127  }
128 
129  bool check(AsioErrorCode asioEc)
130  {
131  if (asioEc)
132  {
133  auto ec = make_error_code(static_cast<std::errc>(asioEc.value()));
134  socket_.reset();
135  postHandler(ec, 0, nullptr);
136  handler_ = nullptr;
137  }
138  return !asioEc;
139  }
140 
141  virtual void onEstablished() = 0;
142 
143  virtual void onHandshakeReceived(Handshake hs) = 0;
144 
145  virtual void onHandshakeSent(Handshake hs) = 0;
146 
147 private:
148  template <typename... TArgs>
149  void postHandler(TArgs&&... args)
150  {
151  boost::asio::post(strand_, std::bind(std::move(handler_),
152  std::forward<TArgs>(args)...));
153  }
154 
155  IoStrand strand_;
156  SocketPtr socket_;
157  Handler handler_;
158  Establisher est_;
159  uint32_t handshake_;
160 
161  // Grant friendship to mock objects used in testing
162  friend class wamp::FakeMsgTypeAsioConnector;
163  friend class wamp::FakeMsgTypeAsioListener;
164 };
165 
166 } // namespace internal
167 
168 } // namespace wamp
169 
170 #endif // CPPWAMP_INTERNAL_ASIOENDPOINT_HPP
wamp::IoStrand
boost::asio::strand< AnyIoExecutor > IoStrand
Serializes I/O operations.
Definition: asiodefs.hpp:41
wamp::TransportErrc::success
@ success
Operation successful.
wamp
Definition: anyhandler.hpp:36
wamp::AsioErrorCode
boost::system::error_code AsioErrorCode
Type used by Boost.Asio for reporting errors.
Definition: asiodefs.hpp:44
wamp::RawsockErrc
RawsockErrc
Error code values used with the RawsockCategory error category.
Definition: error.hpp:426