The Battle for Wesnoth  1.17.0-dev
wesnothd_connection.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2011 - 2018 by Sergey Popov <loonycyborg@gmail.com>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #pragma once
16 
17 #ifdef _WIN32
18 
19 #ifdef INADDR_ANY
20 #undef INADDR_ANY
21 #endif
22 
23 #ifdef INADDR_BROADCAST
24 #undef INADDR_BROADCAST
25 #endif
26 
27 #ifdef INADDR_NONE
28 #undef INADDR_NONE
29 #endif
30 
31 #endif // endif _WIN32
32 
33 #include "configr_assign.hpp"
35 
36 #include <boost/asio/io_context.hpp>
37 #include <boost/asio/ip/tcp.hpp>
38 #include <boost/asio/streambuf.hpp>
39 #include <boost/asio/ssl.hpp>
40 
41 #include <condition_variable>
42 #include <deque>
43 #include <future>
44 #include <list>
45 #include <mutex>
46 #include <queue>
47 #include <thread>
48 
49 class config;
50 
51 /** A class that represents a TCP/IP connection to the wesnothd server. */
53 {
54 public:
56 
59 
61 
62  /**
63  * Constructor.
64  *
65  * @param host Name of the host to connect to
66  * @param service Service identifier such as "80" or "http"
67  */
68  wesnothd_connection(const std::string& host, const std::string& service);
69 
70  /**
71  * Queues the given data to be sent to the server.
72  *
73  * @param request The data to send
74  */
75  void send_data(const configr_of& request);
76 
77  /**
78  * Receives the next pending data pack from the server, if available.
79  *
80  * @param result The object to which the received data will be written.
81  * @returns True if any data was available, false otherwise.
82  */
83  bool receive_data(config& result);
84 
85  /**
86  * Unlike @ref receive_data, waits until data is available instead of returning immediately.
87  *
88  * @param data Config object passed to @ref receive_data
89  * @returns True, since data will always be available.
90  */
91  bool wait_and_receive_data(config& data);
92 
93  /** Waits until the server handshake is complete. */
94  void wait_for_handshake();
95 
96  /** True if connection is currently using TLS and thus is allowed to send cleartext passwords or auth tokens */
97  bool using_tls() const
98  {
99  return utils::holds_alternative<tls_socket>(socket_);
100  }
101 
102  void cancel();
103 
104  void stop();
105 
106  std::size_t bytes_to_write() const
107  {
108  return bytes_to_write_;
109  }
110 
111  std::size_t bytes_written() const
112  {
113  return bytes_written_;
114  }
115 
116  std::size_t bytes_to_read() const
117  {
118  return bytes_to_read_;
119  }
120 
121  std::size_t bytes_read() const
122  {
123  return bytes_read_;
124  }
125 
126  bool has_data_received() const
127  {
128  return !recv_queue_.empty();
129  }
130 
131  bool is_sending_data() const
132  {
133  return !send_queue_.empty();
134  }
135 
136 private:
137  std::thread worker_thread_;
138 
139  boost::asio::io_context io_context_;
140 
141  typedef boost::asio::ip::tcp::resolver resolver;
142  resolver resolver_;
143 
144  boost::asio::ssl::context tls_context_;
145 
146  std::string host_;
147  std::string service_;
148  typedef std::unique_ptr<boost::asio::ip::tcp::socket> raw_socket;
149  typedef std::unique_ptr<boost::asio::ssl::stream<raw_socket::element_type>> tls_socket;
150  typedef utils::variant<raw_socket, tls_socket> any_socket;
151  bool use_tls_;
152  any_socket socket_;
153 
154  boost::system::error_code last_error_;
155 
156  std::mutex last_error_mutex_;
157 
158  std::promise<void> handshake_finished_;
159 
160  boost::asio::streambuf read_buf_;
161 
162  using results_type = resolver::results_type;
163  using endpoint = const boost::asio::ip::tcp::endpoint&;
164 
165  void handle_resolve(const boost::system::error_code& ec, results_type results);
166  void handle_connect(const boost::system::error_code& ec, endpoint endpoint);
167 
168  void handshake();
169  void handle_handshake(const boost::system::error_code& ec);
170 
172 
174 
175  std::size_t is_write_complete(const boost::system::error_code& error, std::size_t bytes_transferred);
176  void handle_write(const boost::system::error_code& ec, std::size_t bytes_transferred);
177 
178  std::size_t is_read_complete(const boost::system::error_code& error, std::size_t bytes_transferred);
179  void handle_read(const boost::system::error_code& ec, std::size_t bytes_transferred);
180 
181  void send();
182  void recv();
183 
184  template<typename T>
185  using data_queue = std::queue<T, std::list<T>>;
186 
189 
190  std::mutex recv_queue_mutex_;
191 
192  std::condition_variable recv_queue_lock_;
193 
194  uint32_t payload_size_;
195 
196  // TODO: do i need to guard the following 4 values with a mutex?
197  std::size_t bytes_to_write_;
198  std::size_t bytes_written_;
199  std::size_t bytes_to_read_;
200  std::size_t bytes_read_;
201 };
void send_data(const configr_of &request)
Queues the given data to be sent to the server.
An error occurred inside the underlying network communication code (boost asio) TODO: find a short na...
std::size_t is_read_complete(const boost::system::error_code &error, std::size_t bytes_transferred)
std::condition_variable recv_queue_lock_
std::promise< void > handshake_finished_
boost::asio::ssl::context tls_context_
void handle_write(const boost::system::error_code &ec, std::size_t bytes_transferred)
resolver::results_type results_type
boost::asio::io_context io_context_
std::size_t is_write_complete(const boost::system::error_code &error, std::size_t bytes_transferred)
void handle_connect(const boost::system::error_code &ec, endpoint endpoint)
std::size_t bytes_to_write() const
bool receive_data(config &result)
Receives the next pending data pack from the server, if available.
wesnothd_connection & operator=(const wesnothd_connection &)=delete
std::unique_ptr< boost::asio::ssl::stream< raw_socket::element_type > > tls_socket
utils::variant< raw_socket, tls_socket > any_socket
std::queue< T, std::list< T > > data_queue
A class that represents a TCP/IP connection to the wesnothd server.
std::size_t bytes_read() const
void handle_handshake(const boost::system::error_code &ec)
boost::asio::streambuf read_buf_
bool using_tls() const
True if connection is currently using TLS and thus is allowed to send cleartext passwords or auth tok...
boost::system::error_code last_error_
std::size_t bytes_to_read() const
std::size_t bytes_written() const
boost::asio::ip::tcp::resolver resolver
wesnothd_connection(const wesnothd_connection &)=delete
data_queue< config > recv_queue_
void handle_read(const boost::system::error_code &ec, std::size_t bytes_transferred)
const boost::asio::ip::tcp::endpoint & endpoint
data_queue< std::unique_ptr< boost::asio::streambuf > > send_queue_
void handle_resolve(const boost::system::error_code &ec, results_type results)
bool wait_and_receive_data(config &data)
Unlike receive_data, waits until data is available instead of returning immediately.
std::unique_ptr< boost::asio::ip::tcp::socket > raw_socket
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:59
void wait_for_handshake()
Waits until the server handshake is complete.