The Battle for Wesnoth  1.19.5+dev
wesnothd_connection.hpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2011 - 2024
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 <future>
44 #include <list>
45 #include <mutex>
46 #include <queue>
47 #include <thread>
48 
49 
50 /** A class that represents a TCP/IP connection to the wesnothd server. */
52 {
53 public:
55 
58 
60 
61  /**
62  * Constructor.
63  *
64  * @param host Name of the host to connect to
65  * @param service Service identifier such as "80" or "http"
66  */
67  wesnothd_connection(const std::string& host, const std::string& service);
68 
69  /**
70  * Queues the given data to be sent to the server.
71  *
72  * @param request The data to send
73  */
74  void send_data(const configr_of& request);
75 
76  /**
77  * Receives the next pending data pack from the server, if available.
78  *
79  * @param result The object to which the received data will be written.
80  * @returns True if any data was available, false otherwise.
81  */
82  bool receive_data(config& result);
83 
84  /**
85  * Unlike @ref receive_data, waits until data is available instead of returning immediately.
86  *
87  * @param data Config object passed to @ref receive_data
88  * @returns True, since data will always be available.
89  */
91 
92  /** Waits until the server handshake is complete. */
93  void wait_for_handshake();
94 
95  /** True if connection is currently using TLS and thus is allowed to send cleartext passwords or auth tokens */
96  bool using_tls() const
97  {
98  return utils::holds_alternative<tls_socket>(socket_);
99  }
100 
101  void cancel();
102 
103  void stop();
104 
105  std::size_t bytes_to_write() const
106  {
107  return bytes_to_write_;
108  }
109 
110  std::size_t bytes_written() const
111  {
112  return bytes_written_;
113  }
114 
115  std::size_t bytes_to_read() const
116  {
117  return bytes_to_read_;
118  }
119 
120  std::size_t bytes_read() const
121  {
122  return bytes_read_;
123  }
124 
125  bool has_data_received() const
126  {
127  return !recv_queue_.empty();
128  }
129 
130  bool is_sending_data() const
131  {
132  return !send_queue_.empty();
133  }
134 
135 private:
136  std::thread worker_thread_;
137 
138  boost::asio::io_context io_context_;
139 
140  typedef boost::asio::ip::tcp::resolver resolver;
142 
143  boost::asio::ssl::context tls_context_;
144 
145  std::string host_;
146  std::string service_;
147  typedef std::unique_ptr<boost::asio::ip::tcp::socket> raw_socket;
148  typedef std::unique_ptr<boost::asio::ssl::stream<raw_socket::element_type>> tls_socket;
149  typedef utils::variant<raw_socket, tls_socket> any_socket;
150  bool use_tls_;
152 
153  boost::system::error_code last_error_;
154 
155  std::mutex last_error_mutex_;
156 
157  std::promise<void> handshake_finished_;
158 
159  boost::asio::streambuf read_buf_;
160 
161  using results_type = resolver::results_type;
162  using endpoint = const boost::asio::ip::tcp::endpoint&;
163 
164  void handle_resolve(const boost::system::error_code& ec, results_type results);
165  void handle_connect(const boost::system::error_code& ec, endpoint endpoint);
166 
167  void handshake();
168  void handle_handshake(const boost::system::error_code& ec);
169 
171 
173 
174  std::size_t is_write_complete(const boost::system::error_code& error, std::size_t bytes_transferred);
175  void handle_write(const boost::system::error_code& ec, std::size_t bytes_transferred);
176 
177  std::size_t is_read_complete(const boost::system::error_code& error, std::size_t bytes_transferred);
178  void handle_read(const boost::system::error_code& ec, std::size_t bytes_transferred);
179 
180  void send();
181  void recv();
182 
183  void set_keepalive(int seconds);
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 };
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
A class that represents a TCP/IP connection to the wesnothd server.
bool using_tls() const
True if connection is currently using TLS and thus is allowed to send cleartext passwords or auth tok...
boost::asio::ssl::context tls_context_
void handle_handshake(const boost::system::error_code &ec)
const boost::asio::ip::tcp::endpoint & endpoint
std::size_t bytes_to_read() const
void handle_connect(const boost::system::error_code &ec, endpoint endpoint)
void handle_resolve(const boost::system::error_code &ec, results_type results)
data_queue< std::unique_ptr< boost::asio::streambuf > > send_queue_
std::condition_variable recv_queue_lock_
std::size_t is_write_complete(const boost::system::error_code &error, std::size_t bytes_transferred)
std::size_t bytes_to_write() const
wesnothd_connection(const wesnothd_connection &)=delete
data_queue< config > recv_queue_
void wait_for_handshake()
Waits until the server handshake is complete.
boost::asio::io_context io_context_
wesnothd_connection & operator=(const wesnothd_connection &)=delete
bool receive_data(config &result)
Receives the next pending data pack from the server, if available.
void handle_write(const boost::system::error_code &ec, std::size_t bytes_transferred)
void send_data(const configr_of &request)
Queues the given data to be sent to the server.
void set_keepalive(int seconds)
resolver::results_type results_type
boost::asio::streambuf read_buf_
bool wait_and_receive_data(config &data)
Unlike receive_data, waits until data is available instead of returning immediately.
void handle_read(const boost::system::error_code &ec, std::size_t bytes_transferred)
std::size_t bytes_written() const
std::queue< T, std::list< T > > data_queue
boost::asio::ip::tcp::resolver resolver
std::size_t bytes_read() const
std::unique_ptr< boost::asio::ip::tcp::socket > raw_socket
boost::system::error_code last_error_
std::unique_ptr< boost::asio::ssl::stream< raw_socket::element_type > > tls_socket
std::promise< void > handshake_finished_
std::size_t is_read_complete(const boost::system::error_code &error, std::size_t bytes_transferred)
utils::variant< raw_socket, tls_socket > any_socket
std::string_view data
Definition: picture.cpp:178
An error occurred inside the underlying network communication code (boost asio) TODO: find a short na...