The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
wesnothd_connection.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2011 - 2016 by Sergey Popov <[email protected]>
3  Part of the Battle for Wesnoth Project http://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 #include <deque>
16 #include "utils/functional.hpp"
17 #include <boost/ref.hpp>
18 #include <boost/cstdint.hpp>
19 #include <boost/version.hpp>
20 #include "log.hpp"
21 #include "wesnothd_connection.hpp"
22 #include "serialization/parser.hpp"
23 
24 static lg::log_domain log_network("network");
25 #define DBG_NW LOG_STREAM(debug, log_network)
26 #define LOG_NW LOG_STREAM(info, log_network)
27 #define WRN_NW LOG_STREAM(warn, log_network)
28 #define ERR_NW LOG_STREAM(err, log_network)
29 
30 using boost::system::system_error;
31 using boost::system::error_code;
32 
34  : io_service_()
35  , resolver_(io_service_)
36  , socket_(io_service_)
37  , handshake_finished_(false)
38  , read_buf_()
39  , handshake_response_()
40  , payload_size_(0)
41  , bytes_to_write_(0)
42  , bytes_written_(0)
43  , bytes_to_read_(0)
44  , bytes_read_(0)
45 {
46  resolver_.async_resolve(
48  std::bind(&twesnothd_connection::handle_resolve, this, _1, _2)
49  );
50  LOG_NW << "Resolving hostname: " << host << '\n';
51 }
52 
54 {
55  if (ec) {
56  throw system_error(ec);
57  }
58  connect(iterator);
59 }
60 
62 {
63  socket_.async_connect(*iterator, std::bind(
64  &twesnothd_connection::handle_connect, this, _1, iterator)
65  );
66  LOG_NW << "Connecting to " << iterator->endpoint().address() << '\n';
67 }
68 
70  const boost::system::error_code& ec,
72  )
73 {
74  if(ec) {
75  WRN_NW << "Failed to connect to " <<
76  iterator->endpoint().address() << ": " <<
77  ec.message() << '\n';
78  socket_.close();
79  if(++iterator == resolver::iterator()) {
80  ERR_NW << "Tried all IPs. Giving up" << std::endl;
81  throw system_error(ec);
82  }
83  else {
84  connect(iterator);
85  }
86  } else {
87  LOG_NW << "Connected to " << iterator->endpoint().address() << '\n';
88  handshake();
89  }
90 }
91 
93 {
94  static const boost::uint32_t handshake = 0;
95  boost::asio::async_write(socket_,
96  boost::asio::buffer(reinterpret_cast<const char*>(&handshake), 4),
97  [](const error_code& ec, std::size_t) { if (ec) throw system_error(ec); }
98  );
99  boost::asio::async_read(socket_,
101  std::bind(&twesnothd_connection::handle_handshake, this, _1)
102  );
103 }
104 
105 void twesnothd_connection::handle_handshake(const error_code& ec)
106 {
107  if (ec) {
108  throw system_error(ec);
109  }
110  handshake_finished_ = true;
111  recv();
112 }
113 
115 {
116  poll();
117  send_queue_.emplace_back();
118 
119  std::ostream os(&send_queue_.back());
120  write_gz(os, request);
121  if (send_queue_.size() == 1) {
122  send();
123  }
124 }
125 
127 {
128  if(socket_.is_open()) {
129  boost::system::error_code ec;
130  socket_.cancel(ec);
131  if(ec) {
132  WRN_NW << "Failed to cancel network operations: " << ec.message() << std::endl;
133  }
134  }
135 }
136 
137 std::size_t twesnothd_connection::is_write_complete(const boost::system::error_code& ec, size_t bytes_transferred)
138 {
139  if(ec)
140  throw system_error(ec);
141  bytes_written_ = bytes_transferred;
142  return bytes_to_write_ - bytes_transferred;
143 }
144 
146  const boost::system::error_code& ec,
147  std::size_t bytes_transferred
148  )
149 {
150  DBG_NW << "Written " << bytes_transferred << " bytes.\n";
151  send_queue_.pop_front();
152  if (ec) {
153  throw system_error(ec);
154  }
155  if (!send_queue_.empty()) {
156  send();
157  }
158 }
159 
161  const boost::system::error_code& ec,
162  std::size_t bytes_transferred
163  )
164 {
165  if(ec) {
166  throw system_error(ec);
167  }
168  bytes_read_ = bytes_transferred;
169  if(bytes_transferred < 4) {
170  return 4;
171  } else {
172  if(!bytes_to_read_) {
173  std::istream is(&read_buf_);
174  union { char binary[4]; boost::uint32_t num; } data_size;
175  is.read(data_size.binary, 4);
176  bytes_to_read_ = ntohl(data_size.num) + 4;
177  //Close immediately if we receive an invalid length
178  if (bytes_to_read_ < 4)
179  bytes_to_read_ = bytes_transferred;
180  }
181  return bytes_to_read_ - bytes_transferred;
182  }
183 }
184 
186  const boost::system::error_code& ec,
187  std::size_t bytes_transferred
188  )
189 {
190  DBG_NW << "Read " << bytes_transferred << " bytes.\n";
191  bytes_to_read_ = 0;
192  if(ec && ec != boost::asio::error::eof)
193  throw system_error(ec);
194  std::istream is(&read_buf_);
195  recv_queue_.push_back(config());
196  read_gz(recv_queue_.back(), is);
197  DBG_NW << "Received " << recv_queue_.back() << " bytes.\n";
198 
199  recv();
200 }
201 
203 {
204  auto& buf = send_queue_.front();
205  size_t buf_size = buf.size();
206  bytes_to_write_ = buf_size + 4;
207  bytes_written_ = 0;
208  payload_size_ = htonl(buf_size);
209 
210  boost::asio::streambuf::const_buffers_type gzipped_data = buf.data();
211  std::deque<boost::asio::const_buffer> bufs(gzipped_data.begin(), gzipped_data.end());
212  bufs.push_front(boost::asio::buffer(reinterpret_cast<const char*>(&payload_size_), 4));
213  boost::asio::async_write(socket_, bufs,
214  std::bind(&twesnothd_connection::is_write_complete, this, _1, _2),
215  std::bind(&twesnothd_connection::handle_write, this, _1, _2)
216  );
217 }
218 
220 {
221  boost::asio::async_read(socket_, read_buf_,
222  std::bind(&twesnothd_connection::is_read_complete, this, _1, _2),
223  std::bind(&twesnothd_connection::handle_read, this, _1, _2)
224  );
225 }
226 
228 {
229  try {
230  return io_service_.poll();
231  }
232  catch (const boost::system::system_error& err) {
233  if (err.code() == boost::asio::error::operation_aborted)
234  return 1;
235  throw error(err.code());
236  }
237 }
239 {
240  poll();
241  if (recv_queue_.empty()) {
242  return false;
243  }
244  else {
245  result.swap(recv_queue_.front());
246  recv_queue_.pop_front();
247  return true;
248  }
249 }
void write_gz(std::ostream &out, configr_of const &cfg)
Definition: parser.cpp:639
void connect(resolver::iterator iterator)
boost::uint32_t uint32_t
Definition: xbrz.hpp:45
void handle_handshake(const boost::system::error_code &ec)
void handle_resolve(const boost::system::error_code &ec, resolver::iterator iterator)
bool receive_data(config &result)
void cancel()
Run asio's event loop.
std::size_t is_read_complete(const boost::system::error_code &error, std::size_t bytes_transferred)
#define ERR_NW
void read_gz(config &cfg, std::istream &file, abstract_validator *validator)
might throw a std::ios_base::failure especially a gzip_error
Definition: parser.cpp:454
#define DBG_NW
void send_data(const configr_of &request)
void handle_read(const boost::system::error_code &ec, std::size_t bytes_transferred)
void swap(config &cfg)
Definition: config.cpp:1518
GLuint64EXT * result
Definition: glew.h:10727
std::size_t poll()
Handle all pending asynchonous events and return.
const GLuint GLenum const GLvoid * binary
Definition: glew.h:3027
#define WRN_NW
GLenum GLuint GLsizei const char * buf
Definition: glew.h:2498
std::list< config > recv_queue_
boost::asio::io_service io_service_
GLuint buffer
Definition: glew.h:1648
logger & err()
Definition: log.cpp:79
void handle_write(const boost::system::error_code &ec, std::size_t bytes_transferred)
boost::asio::streambuf read_buf_
const GLenum * bufs
Definition: glew.h:1791
twesnothd_connection(const std::string &host, const std::string &service)
Constructor.
static lg::log_domain log_network("network")
std::list< boost::asio::streambuf > send_queue_
Standard logging facilities (interface).
wesnothd_connection_error error
void handle_connect(const boost::system::error_code &ec, resolver::iterator iterator)
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
union twesnothd_connection::@26 handshake_response_
#define LOG_NW
std::size_t is_write_complete(const boost::system::error_code &error, std::size_t bytes_transferred)
GLenum query
Definition: glew.h:4185
GLsizei const GLcharARB ** string
Definition: glew.h:4503