rippled
Loading...
Searching...
No Matches
WorkBase.h
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2016 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#ifndef RIPPLE_APP_MISC_DETAIL_WORKBASE_H_INCLUDED
21#define RIPPLE_APP_MISC_DETAIL_WORKBASE_H_INCLUDED
22
23#include <xrpld/app/misc/detail/Work.h>
24#include <xrpl/basics/random.h>
25#include <xrpl/protocol/BuildInfo.h>
26
27#include <boost/asio.hpp>
28#include <boost/beast/core/multi_buffer.hpp>
29#include <boost/beast/http/empty_body.hpp>
30#include <boost/beast/http/read.hpp>
31#include <boost/beast/http/write.hpp>
32
33namespace ripple {
34
35namespace detail {
36
37template <class Impl>
38class WorkBase : public Work
39{
40protected:
41 using error_code = boost::system::error_code;
42 using endpoint_type = boost::asio::ip::tcp::endpoint;
43
44public:
46 void(error_code const&, endpoint_type const&, response_type&&)>;
47
48protected:
49 using socket_type = boost::asio::ip::tcp::socket;
50 using resolver_type = boost::asio::ip::tcp::resolver;
51 using results_type = boost::asio::ip::tcp::resolver::results_type;
53 boost::beast::http::request<boost::beast::http::empty_body>;
54
59 boost::asio::io_service& ios_;
60 boost::asio::io_service::strand strand_;
65 boost::beast::multi_buffer readBuf_;
68
69public:
71 std::string const& host,
72 std::string const& path,
73 std::string const& port,
74 boost::asio::io_service& ios,
75 endpoint_type const& lastEndpoint,
76 bool lastStatus,
77 callback_type cb);
79
80 Impl&
82 {
83 return *static_cast<Impl*>(this);
84 }
85
86 void
87 run() override;
88
89 void
90 cancel() override;
91
92 void
93 fail(error_code const& ec);
94
95 void
96 onResolve(error_code const& ec, results_type results);
97
98 void
100
101 void
103
104 void
106
107private:
108 void
110};
111
112//------------------------------------------------------------------------------
113
114template <class Impl>
116 std::string const& host,
117 std::string const& path,
118 std::string const& port,
119 boost::asio::io_service& ios,
120 endpoint_type const& lastEndpoint,
121 bool lastStatus,
122 callback_type cb)
123 : host_(host)
124 , path_(path)
125 , port_(port)
126 , cb_(std::move(cb))
127 , ios_(ios)
128 , strand_(ios)
129 , resolver_(ios)
130 , socket_(ios)
131 , lastEndpoint_{lastEndpoint}
132 , lastStatus_(lastStatus)
133{
134}
135
136template <class Impl>
138{
139 if (cb_)
140 cb_(make_error_code(boost::system::errc::not_a_socket),
141 lastEndpoint_,
142 std::move(res_));
143 close();
144}
145
146template <class Impl>
147void
149{
150 if (!strand_.running_in_this_thread())
151 return ios_.post(
152 strand_.wrap(std::bind(&WorkBase::run, impl().shared_from_this())));
153
154 resolver_.async_resolve(
155 host_,
156 port_,
157 strand_.wrap(std::bind(
159 impl().shared_from_this(),
160 std::placeholders::_1,
161 std::placeholders::_2)));
162}
163
164template <class Impl>
165void
167{
168 if (!strand_.running_in_this_thread())
169 {
170 return ios_.post(strand_.wrap(
171 std::bind(&WorkBase::cancel, impl().shared_from_this())));
172 }
173
174 error_code ec;
175 resolver_.cancel();
176 socket_.cancel(ec);
177}
178
179template <class Impl>
180void
182{
183 if (cb_)
184 {
185 cb_(ec, lastEndpoint_, std::move(res_));
186 cb_ = nullptr;
187 }
188}
189
190template <class Impl>
191void
193{
194 if (ec)
195 return fail(ec);
196
197 // Use last endpoint if it is successfully connected
198 // and is in the list, otherwise pick a random endpoint
199 // from the list (excluding last endpoint). If there is
200 // only one endpoint and it is the last endpoint then
201 // use the last endpoint.
202 lastEndpoint_ = [&]() -> endpoint_type {
203 int foundIndex = 0;
204 auto const foundIt = std::find_if(
205 results.begin(), results.end(), [&](endpoint_type const& e) {
206 if (e == lastEndpoint_)
207 return true;
208 foundIndex++;
209 return false;
210 });
211 if (foundIt != results.end() && lastStatus_)
212 return lastEndpoint_;
213 else if (results.size() == 1)
214 return *results.begin();
215 else if (foundIt == results.end())
216 return *std::next(results.begin(), rand_int(results.size() - 1));
217
218 // lastEndpoint_ is part of the collection
219 // Pick a random number from the n-1 valid choices, if we use
220 // this as an index, note the last element will never be chosen
221 // and the `lastEndpoint_` index may be chosen. So when the
222 // `lastEndpoint_` index is chosen, that is treated as if the
223 // last element was chosen.
224 auto randIndex =
225 (results.size() > 2) ? rand_int(results.size() - 2) : 0;
226 if (randIndex == foundIndex)
227 randIndex = results.size() - 1;
228 return *std::next(results.begin(), randIndex);
229 }();
230
231 socket_.async_connect(
232 lastEndpoint_,
233 strand_.wrap(std::bind(
234 &Impl::onConnect,
235 impl().shared_from_this(),
236 std::placeholders::_1)));
237}
238
239template <class Impl>
240void
242{
243 req_.method(boost::beast::http::verb::get);
244 req_.target(path_.empty() ? "/" : path_);
245 req_.version(11);
246 req_.set("Host", host_ + ":" + port_);
247 req_.set("User-Agent", BuildInfo::getFullVersionString());
248 req_.prepare_payload();
249 boost::beast::http::async_write(
250 impl().stream(),
251 req_,
252 strand_.wrap(std::bind(
254 impl().shared_from_this(),
255 std::placeholders::_1)));
256}
257
258template <class Impl>
259void
261{
262 if (ec)
263 return fail(ec);
264
265 boost::beast::http::async_read(
266 impl().stream(),
267 readBuf_,
268 res_,
269 strand_.wrap(std::bind(
271 impl().shared_from_this(),
272 std::placeholders::_1)));
273}
274
275template <class Impl>
276void
278{
279 if (ec)
280 return fail(ec);
281
282 close();
283 XRPL_ASSERT(cb_, "ripple::detail::WorkBase::onResponse : callback is set");
284 cb_(ec, lastEndpoint_, std::move(res_));
285 cb_ = nullptr;
286}
287
288template <class Impl>
289void
291{
292 if (socket_.is_open())
293 {
294 error_code ec;
295 socket_.shutdown(boost::asio::socket_base::shutdown_send, ec);
296 if (ec)
297 return;
298 socket_.close(ec);
299 }
300}
301
302} // namespace detail
303
304} // namespace ripple
305
306#endif
T bind(T... args)
boost::asio::io_service & ios_
Definition: WorkBase.h:59
endpoint_type lastEndpoint_
Definition: WorkBase.h:66
callback_type cb_
Definition: WorkBase.h:58
boost::asio::ip::tcp::resolver::results_type results_type
Definition: WorkBase.h:51
response_type res_
Definition: WorkBase.h:64
boost::beast::http::request< boost::beast::http::empty_body > request_type
Definition: WorkBase.h:53
void onResolve(error_code const &ec, results_type results)
Definition: WorkBase.h:192
void onRequest(error_code const &ec)
Definition: WorkBase.h:260
boost::asio::ip::tcp::socket socket_type
Definition: WorkBase.h:49
boost::asio::io_service::strand strand_
Definition: WorkBase.h:60
socket_type socket_
Definition: WorkBase.h:62
void cancel() override
Definition: WorkBase.h:166
request_type req_
Definition: WorkBase.h:63
void onResponse(error_code const &ec)
Definition: WorkBase.h:277
boost::asio::ip::tcp::endpoint endpoint_type
Definition: WorkBase.h:42
boost::asio::ip::tcp::resolver resolver_type
Definition: WorkBase.h:50
boost::system::error_code error_code
Definition: WorkBase.h:41
boost::beast::multi_buffer readBuf_
Definition: WorkBase.h:65
WorkBase(std::string const &host, std::string const &path, std::string const &port, boost::asio::io_service &ios, endpoint_type const &lastEndpoint, bool lastStatus, callback_type cb)
Definition: WorkBase.h:115
void fail(error_code const &ec)
Definition: WorkBase.h:181
void run() override
Definition: WorkBase.h:148
resolver_type resolver_
Definition: WorkBase.h:61
T find_if(T... args)
std::string const & getFullVersionString()
Full server version string.
Definition: BuildInfo.cpp:81
boost::beast::http::response< boost::beast::http::string_body > response_type
Definition: Work.h:31
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: algorithm.h:26
std::enable_if_t< std::is_integral< Integral >::value, Integral > rand_int()
std::error_code make_error_code(ripple::TokenCodecErrc e)
Definition: token_errors.h:97
STL namespace.
T next(T... args)