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
25#include <xrpl/basics/random.h>
26#include <xrpl/protocol/BuildInfo.h>
27
28#include <boost/asio.hpp>
29#include <boost/beast/core/multi_buffer.hpp>
30#include <boost/beast/http/empty_body.hpp>
31#include <boost/beast/http/read.hpp>
32#include <boost/beast/http/write.hpp>
33
34namespace ripple {
35
36namespace detail {
37
38template <class Impl>
39class WorkBase : public Work
40{
41protected:
42 using error_code = boost::system::error_code;
43 using endpoint_type = boost::asio::ip::tcp::endpoint;
44
45public:
47 void(error_code const&, endpoint_type const&, response_type&&)>;
48
49protected:
50 using socket_type = boost::asio::ip::tcp::socket;
51 using resolver_type = boost::asio::ip::tcp::resolver;
52 using results_type = boost::asio::ip::tcp::resolver::results_type;
54 boost::beast::http::request<boost::beast::http::empty_body>;
55
60 boost::asio::io_service& ios_;
61 boost::asio::io_service::strand strand_;
66 boost::beast::multi_buffer readBuf_;
69
70public:
72 std::string const& host,
73 std::string const& path,
74 std::string const& port,
75 boost::asio::io_service& ios,
76 endpoint_type const& lastEndpoint,
77 bool lastStatus,
78 callback_type cb);
80
81 Impl&
83 {
84 return *static_cast<Impl*>(this);
85 }
86
87 void
88 run() override;
89
90 void
91 cancel() override;
92
93 void
94 fail(error_code const& ec);
95
96 void
97 onResolve(error_code const& ec, results_type results);
98
99 void
101
102 void
104
105 void
107
108private:
109 void
111};
112
113//------------------------------------------------------------------------------
114
115template <class Impl>
117 std::string const& host,
118 std::string const& path,
119 std::string const& port,
120 boost::asio::io_service& ios,
121 endpoint_type const& lastEndpoint,
122 bool lastStatus,
123 callback_type cb)
124 : host_(host)
125 , path_(path)
126 , port_(port)
127 , cb_(std::move(cb))
128 , ios_(ios)
129 , strand_(ios)
130 , resolver_(ios)
131 , socket_(ios)
132 , lastEndpoint_{lastEndpoint}
133 , lastStatus_(lastStatus)
134{
135}
136
137template <class Impl>
139{
140 if (cb_)
141 cb_(make_error_code(boost::system::errc::not_a_socket),
142 lastEndpoint_,
143 std::move(res_));
144 close();
145}
146
147template <class Impl>
148void
150{
151 if (!strand_.running_in_this_thread())
152 return ios_.post(
153 strand_.wrap(std::bind(&WorkBase::run, impl().shared_from_this())));
154
155 resolver_.async_resolve(
156 host_,
157 port_,
158 strand_.wrap(std::bind(
160 impl().shared_from_this(),
161 std::placeholders::_1,
162 std::placeholders::_2)));
163}
164
165template <class Impl>
166void
168{
169 if (!strand_.running_in_this_thread())
170 {
171 return ios_.post(strand_.wrap(
172 std::bind(&WorkBase::cancel, impl().shared_from_this())));
173 }
174
175 error_code ec;
176 resolver_.cancel();
177 socket_.cancel(ec);
178}
179
180template <class Impl>
181void
183{
184 if (cb_)
185 {
186 cb_(ec, lastEndpoint_, std::move(res_));
187 cb_ = nullptr;
188 }
189}
190
191template <class Impl>
192void
194{
195 if (ec)
196 return fail(ec);
197
198 // Use last endpoint if it is successfully connected
199 // and is in the list, otherwise pick a random endpoint
200 // from the list (excluding last endpoint). If there is
201 // only one endpoint and it is the last endpoint then
202 // use the last endpoint.
203 lastEndpoint_ = [&]() -> endpoint_type {
204 int foundIndex = 0;
205 auto const foundIt = std::find_if(
206 results.begin(), results.end(), [&](endpoint_type const& e) {
207 if (e == lastEndpoint_)
208 return true;
209 foundIndex++;
210 return false;
211 });
212 if (foundIt != results.end() && lastStatus_)
213 return lastEndpoint_;
214 else if (results.size() == 1)
215 return *results.begin();
216 else if (foundIt == results.end())
217 return *std::next(results.begin(), rand_int(results.size() - 1));
218
219 // lastEndpoint_ is part of the collection
220 // Pick a random number from the n-1 valid choices, if we use
221 // this as an index, note the last element will never be chosen
222 // and the `lastEndpoint_` index may be chosen. So when the
223 // `lastEndpoint_` index is chosen, that is treated as if the
224 // last element was chosen.
225 auto randIndex =
226 (results.size() > 2) ? rand_int(results.size() - 2) : 0;
227 if (randIndex == foundIndex)
228 randIndex = results.size() - 1;
229 return *std::next(results.begin(), randIndex);
230 }();
231
232 socket_.async_connect(
233 lastEndpoint_,
234 strand_.wrap(std::bind(
235 &Impl::onConnect,
236 impl().shared_from_this(),
237 std::placeholders::_1)));
238}
239
240template <class Impl>
241void
243{
244 req_.method(boost::beast::http::verb::get);
245 req_.target(path_.empty() ? "/" : path_);
246 req_.version(11);
247 req_.set("Host", host_ + ":" + port_);
248 req_.set("User-Agent", BuildInfo::getFullVersionString());
249 req_.prepare_payload();
250 boost::beast::http::async_write(
251 impl().stream(),
252 req_,
253 strand_.wrap(std::bind(
255 impl().shared_from_this(),
256 std::placeholders::_1)));
257}
258
259template <class Impl>
260void
262{
263 if (ec)
264 return fail(ec);
265
266 boost::beast::http::async_read(
267 impl().stream(),
268 readBuf_,
269 res_,
270 strand_.wrap(std::bind(
272 impl().shared_from_this(),
273 std::placeholders::_1)));
274}
275
276template <class Impl>
277void
279{
280 if (ec)
281 return fail(ec);
282
283 close();
284 XRPL_ASSERT(cb_, "ripple::detail::WorkBase::onResponse : callback is set");
285 cb_(ec, lastEndpoint_, std::move(res_));
286 cb_ = nullptr;
287}
288
289template <class Impl>
290void
292{
293 if (socket_.is_open())
294 {
295 error_code ec;
296 socket_.shutdown(boost::asio::socket_base::shutdown_send, ec);
297 if (ec)
298 return;
299 socket_.close(ec);
300 }
301}
302
303} // namespace detail
304
305} // namespace ripple
306
307#endif
T bind(T... args)
boost::asio::io_service & ios_
Definition: WorkBase.h:60
endpoint_type lastEndpoint_
Definition: WorkBase.h:67
callback_type cb_
Definition: WorkBase.h:59
boost::asio::ip::tcp::resolver::results_type results_type
Definition: WorkBase.h:52
response_type res_
Definition: WorkBase.h:65
boost::beast::http::request< boost::beast::http::empty_body > request_type
Definition: WorkBase.h:54
void onResolve(error_code const &ec, results_type results)
Definition: WorkBase.h:193
void onRequest(error_code const &ec)
Definition: WorkBase.h:261
boost::asio::ip::tcp::socket socket_type
Definition: WorkBase.h:50
boost::asio::io_service::strand strand_
Definition: WorkBase.h:61
socket_type socket_
Definition: WorkBase.h:63
void cancel() override
Definition: WorkBase.h:167
request_type req_
Definition: WorkBase.h:64
void onResponse(error_code const &ec)
Definition: WorkBase.h:278
boost::asio::ip::tcp::endpoint endpoint_type
Definition: WorkBase.h:43
boost::asio::ip::tcp::resolver resolver_type
Definition: WorkBase.h:51
boost::system::error_code error_code
Definition: WorkBase.h:42
boost::beast::multi_buffer readBuf_
Definition: WorkBase.h:66
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:116
void fail(error_code const &ec)
Definition: WorkBase.h:182
void run() override
Definition: WorkBase.h:149
resolver_type resolver_
Definition: WorkBase.h:62
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)