The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
network_asio.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 "network_asio.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 namespace network_asio {
31 
32 using boost::system::system_error;
33 
34 connection::connection(const std::string& host, const std::string& service)
35  : io_service_()
36  , resolver_(io_service_)
37  , socket_(io_service_)
38  , done_(false)
39  , write_buf_()
40  , read_buf_()
41  , handshake_response_()
42  , payload_size_(0)
43  , bytes_to_write_(0)
44  , bytes_written_(0)
45  , bytes_to_read_(0)
46  , bytes_read_(0)
47 {
48  resolver_.async_resolve(
50  std::bind(&connection::handle_resolve, this, _1, _2)
51  );
52  LOG_NW << "Resolving hostname: " << host << '\n';
53 }
54 
56  const boost::system::error_code& ec,
58  )
59 {
60  if(ec)
61  throw system_error(ec);
62 
63  connect(iterator);
64 }
65 
67 {
68  socket_.async_connect(*iterator, std::bind(
69  &connection::handle_connect, this, _1, iterator)
70  );
71  LOG_NW << "Connecting to " << iterator->endpoint().address() << '\n';
72 }
73 
75  const boost::system::error_code& ec,
77  )
78 {
79  if(ec) {
80  WRN_NW << "Failed to connect to " <<
81  iterator->endpoint().address() << ": " <<
82  ec.message() << '\n';
83  socket_.close();
84  if(++iterator == resolver::iterator()) {
85  ERR_NW << "Tried all IPs. Giving up" << std::endl;
86  throw system_error(ec);
87  } else
88  connect(iterator);
89  } else {
90  LOG_NW << "Connected to " << iterator->endpoint().address() << '\n';
91  handshake();
92  }
93 }
94 
96 {
97  static const boost::uint32_t handshake = 0;
98  boost::asio::async_write(socket_,
99  boost::asio::buffer(reinterpret_cast<const char*>(&handshake), 4),
100  std::bind(&connection::handle_write, this, _1, _2)
101  );
102  boost::asio::async_read(socket_,
104  std::bind(&connection::handle_handshake, this, _1)
105  );
106 }
107 
109  const boost::system::error_code& ec
110  )
111 {
112  if(ec)
113  throw system_error(ec);
114  done_ = true;
115 }
116 
117 void connection::transfer(const config& request, config& response)
118 {
119  io_service_.reset();
120  done_ = false;
121 
122  std::ostream os(&write_buf_);
123  write_gz(os, request);
124  bytes_to_write_ = write_buf_.size() + 4;
125  bytes_written_ = 0;
126  payload_size_ = htonl(bytes_to_write_ - 4);
127  boost::asio::streambuf::const_buffers_type gzipped_data = write_buf_.data();
128  std::deque<boost::asio::const_buffer> bufs(gzipped_data.begin(), gzipped_data.end());
129  bufs.push_front(boost::asio::buffer(reinterpret_cast<const char*>(&payload_size_), 4));
130  boost::asio::async_write(socket_, bufs,
131  std::bind(&connection::is_write_complete, this, _1, _2),
132  std::bind(&connection::handle_write, this, _1, _2)
133  );
134  boost::asio::async_read(socket_, read_buf_,
135  std::bind(&connection::is_read_complete, this, _1, _2),
136  std::bind(&connection::handle_read, this, _1, _2, std::ref(response))
137  );
138 }
139 
141 {
142  if(socket_.is_open()) {
143  boost::system::error_code ec;
144  socket_.cancel(ec);
145  if(ec) {
146  WRN_NW << "Failed to cancel network operations: " << ec.message() << std::endl;
147  }
148  }
149 }
150 
152  const boost::system::error_code& ec,
153  std::size_t bytes_transferred
154  )
155 {
156  if(ec)
157  throw system_error(ec);
158  bytes_written_ = bytes_transferred;
159 #if BOOST_VERSION >= 103700
160  return bytes_to_write_ - bytes_transferred;
161 #else
162  return bytes_to_write_ == bytes_transferred;
163 #endif
164 }
165 
167  const boost::system::error_code& ec,
168  std::size_t bytes_transferred
169  )
170 {
171  DBG_NW << "Written " << bytes_transferred << " bytes.\n";
172  write_buf_.consume(bytes_transferred);
173  if(ec)
174  throw system_error(ec);
175 }
176 
178  const boost::system::error_code& ec,
179  std::size_t bytes_transferred
180  )
181 {
182  if(ec) {
183  throw system_error(ec);
184  }
185  bytes_read_ = bytes_transferred;
186  if(bytes_transferred < 4) {
187  return 4;
188  } else {
189  if(!bytes_to_read_) {
190  std::istream is(&read_buf_);
191  union { char binary[4]; boost::uint32_t num; } data_size;
192  is.read(data_size.binary, 4);
193  bytes_to_read_ = ntohl(data_size.num) + 4;
194  //Close immediately if we receive an invalid length
195  if (bytes_to_read_ < 4)
196  bytes_to_read_ = bytes_transferred;
197  }
198 #if BOOST_VERSION >= 103700
199  return bytes_to_read_ - bytes_transferred;
200 #else
201  return bytes_to_read_ == bytes_transferred;
202 #endif
203  }
204 }
205 
207  const boost::system::error_code& ec,
208  std::size_t bytes_transferred,
209  config& response
210  )
211 {
212  DBG_NW << "Read " << bytes_transferred << " bytes.\n";
213  bytes_to_read_ = 0;
214  bytes_to_write_ = 0;
215  done_ = true;
216  if(ec && ec != boost::asio::error::eof)
217  throw system_error(ec);
218  std::istream is(&read_buf_);
219  read_gz(response, is);
220 }
221 
222 }
void write_gz(std::ostream &out, configr_of const &cfg)
Definition: parser.cpp:639
void handle_read(const boost::system::error_code &ec, std::size_t bytes_transferred, config &response)
boost::asio::streambuf write_buf_
connection(const std::string &host, const std::string &service)
Constructor.
boost::uint32_t uint32_t
Definition: xbrz.hpp:45
boost::asio::streambuf read_buf_
#define ERR_NW
void handle_connect(const boost::system::error_code &ec, resolver::iterator iterator)
std::size_t is_write_complete(const boost::system::error_code &error, std::size_t bytes_transferred)
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
union network_asio::connection::@21 handshake_response_
const GLuint GLenum const GLvoid * binary
Definition: glew.h:3027
#define WRN_NW
void handle_write(const boost::system::error_code &ec, std::size_t bytes_transferred)
std::size_t is_read_complete(const boost::system::error_code &error, std::size_t bytes_transferred)
void connect(resolver::iterator iterator)
static lg::log_domain log_network("network")
void transfer(const config &request, config &response)
GLuint buffer
Definition: glew.h:1648
boost::uint32_t payload_size_
boost::asio::io_service io_service_
#define LOG_NW
const GLenum * bufs
Definition: glew.h:1791
GLenum GLint ref
Definition: glew.h:1813
Standard logging facilities (interface).
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
void handle_resolve(const boost::system::error_code &ec, resolver::iterator iterator)
GLenum query
Definition: glew.h:4185
GLsizei const GLcharARB ** string
Definition: glew.h:4503
void handle_handshake(const boost::system::error_code &ec)
#define DBG_NW