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