• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // third_party_lib.cpp
3 // ~~~~~~~~~~~~~~~~~~~
4 //
5 // Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com)
6 //
7 // Distributed under the Boost Software License, Version 1.0. (See accompanying
8 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9 //
10 
11 #include <boost/asio.hpp>
12 #include <array>
13 #include <iostream>
14 #include <memory>
15 
16 using boost::asio::ip::tcp;
17 
18 namespace third_party_lib {
19 
20 // Simulation of a third party library that wants to perform read and write
21 // operations directly on a socket. It needs to be polled to determine whether
22 // it requires a read or write operation, and notified when the socket is ready
23 // for reading or writing.
24 class session
25 {
26 public:
session(tcp::socket & socket)27   session(tcp::socket& socket)
28     : socket_(socket)
29   {
30   }
31 
32   // Returns true if the third party library wants to be notified when the
33   // socket is ready for reading.
want_read() const34   bool want_read() const
35   {
36     return state_ == reading;
37   }
38 
39   // Notify that third party library that it should perform its read operation.
do_read(boost::system::error_code & ec)40   void do_read(boost::system::error_code& ec)
41   {
42     if (std::size_t len = socket_.read_some(boost::asio::buffer(data_), ec))
43     {
44       write_buffer_ = boost::asio::buffer(data_, len);
45       state_ = writing;
46     }
47   }
48 
49   // Returns true if the third party library wants to be notified when the
50   // socket is ready for writing.
want_write() const51   bool want_write() const
52   {
53     return state_ == writing;
54   }
55 
56   // Notify that third party library that it should perform its write operation.
do_write(boost::system::error_code & ec)57   void do_write(boost::system::error_code& ec)
58   {
59     if (std::size_t len = socket_.write_some(
60           boost::asio::buffer(write_buffer_), ec))
61     {
62       write_buffer_ = write_buffer_ + len;
63       state_ = boost::asio::buffer_size(write_buffer_) > 0 ? writing : reading;
64     }
65   }
66 
67 private:
68   tcp::socket& socket_;
69   enum { reading, writing } state_ = reading;
70   std::array<char, 128> data_;
71   boost::asio::const_buffer write_buffer_;
72 };
73 
74 } // namespace third_party_lib
75 
76 // The glue between asio's sockets and the third party library.
77 class connection
78   : public std::enable_shared_from_this<connection>
79 {
80 public:
connection(tcp::socket socket)81   connection(tcp::socket socket)
82     : socket_(std::move(socket))
83   {
84   }
85 
start()86   void start()
87   {
88     // Put the socket into non-blocking mode.
89     socket_.non_blocking(true);
90 
91     do_operations();
92   }
93 
94 private:
do_operations()95   void do_operations()
96   {
97     auto self(shared_from_this());
98 
99     // Start a read operation if the third party library wants one.
100     if (session_impl_.want_read() && !read_in_progress_)
101     {
102       read_in_progress_ = true;
103       socket_.async_wait(tcp::socket::wait_read,
104           [this, self](boost::system::error_code ec)
105           {
106             read_in_progress_ = false;
107 
108             // Notify third party library that it can perform a read.
109             if (!ec)
110               session_impl_.do_read(ec);
111 
112             // The third party library successfully performed a read on the
113             // socket. Start new read or write operations based on what it now
114             // wants.
115             if (!ec || ec == boost::asio::error::would_block)
116               do_operations();
117 
118             // Otherwise, an error occurred. Closing the socket cancels any
119             // outstanding asynchronous read or write operations. The
120             // connection object will be destroyed automatically once those
121             // outstanding operations complete.
122             else
123               socket_.close();
124           });
125     }
126 
127     // Start a write operation if the third party library wants one.
128     if (session_impl_.want_write() && !write_in_progress_)
129     {
130       write_in_progress_ = true;
131       socket_.async_wait(tcp::socket::wait_write,
132           [this, self](boost::system::error_code ec)
133           {
134             write_in_progress_ = false;
135 
136             // Notify third party library that it can perform a write.
137             if (!ec)
138               session_impl_.do_write(ec);
139 
140             // The third party library successfully performed a write on the
141             // socket. Start new read or write operations based on what it now
142             // wants.
143             if (!ec || ec == boost::asio::error::would_block)
144               do_operations();
145 
146             // Otherwise, an error occurred. Closing the socket cancels any
147             // outstanding asynchronous read or write operations. The
148             // connection object will be destroyed automatically once those
149             // outstanding operations complete.
150             else
151               socket_.close();
152           });
153     }
154   }
155 
156 private:
157   tcp::socket socket_;
158   third_party_lib::session session_impl_{socket_};
159   bool read_in_progress_ = false;
160   bool write_in_progress_ = false;
161 };
162 
163 class server
164 {
165 public:
server(boost::asio::io_context & io_context,unsigned short port)166   server(boost::asio::io_context& io_context, unsigned short port)
167     : acceptor_(io_context, {tcp::v4(), port})
168   {
169     do_accept();
170   }
171 
172 private:
do_accept()173   void do_accept()
174   {
175     acceptor_.async_accept(
176         [this](boost::system::error_code ec, tcp::socket socket)
177         {
178           if (!ec)
179           {
180             std::make_shared<connection>(std::move(socket))->start();
181           }
182 
183           do_accept();
184         });
185   }
186 
187   tcp::acceptor acceptor_;
188 };
189 
main(int argc,char * argv[])190 int main(int argc, char* argv[])
191 {
192   try
193   {
194     if (argc != 2)
195     {
196       std::cerr << "Usage: third_party_lib <port>\n";
197       return 1;
198     }
199 
200     boost::asio::io_context io_context;
201 
202     server s(io_context, std::atoi(argv[1]));
203 
204     io_context.run();
205   }
206   catch (std::exception& e)
207   {
208     std::cerr << "Exception: " << e.what() << "\n";
209   }
210 
211   return 0;
212 }
213