26 #include <boost/algorithm/string/join.hpp> 31 static size_t curl_write_function(
void *contents,
size_t size,
size_t nmemb,
void *userp)
33 ((std::string*)userp)->append((
char*)contents, size * nmemb);
39 static bool handle_bulk_response( uint16_t http_code,
const std::string& curl_read_buffer )
45 bool errors = j[
"errors"].
as_bool();
48 elog(
"ES returned 200 but with errors: ${e}", (
"e", curl_read_buffer) );
56 elog(
"413 error: Request too large. Can be low disk space. ${e}", (
"e", curl_read_buffer) );
60 elog(
"401 error: Unauthorized. ${e}", (
"e", curl_read_buffer) );
64 elog(
"${code} error: ${e}", (
"code",
std::to_string(http_code)) (
"e", curl_read_buffer) );
71 std::vector<std::string> bulk;
73 final_bulk_header[
"index"] = bulk_header;
75 bulk.emplace_back(std::move(data));
85 CURL* curl_wrapper::init_curl()
87 CURL* curl = curl_easy_init();
90 curl_easy_setopt( curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2 );
96 curl_slist* curl_wrapper::init_request_headers()
98 curl_slist* request_headers = curl_slist_append( NULL,
"Content-Type: application/json" );
99 FC_ASSERT( request_headers,
"Unable to init cURL request headers" );
100 return request_headers;
105 curl_easy_setopt( curl.get(), CURLOPT_HTTPHEADER, request_headers.get() );
106 curl_easy_setopt( curl.get(), CURLOPT_USERAGENT,
"bitshares-core/6.1" );
109 void curl_wrapper::curl_deleter::operator()( CURL* p_curl )
const 112 curl_easy_cleanup( p_curl );
115 void curl_wrapper::curl_slist_deleter::operator()( curl_slist* slist )
const 118 curl_slist_free_all( slist );
122 const std::string& url,
123 const std::string& auth,
124 const std::string& query )
const 132 curl_easy_setopt( curl.get(), CURLOPT_URL, url.c_str() );
134 curl_easy_setopt( curl.get(), CURLOPT_USERPWD, auth.c_str() );
137 static const std::vector<std::string> http_request_method_custom_str = {
146 const auto& custom_request = http_request_method_custom_str[
static_cast<size_t>(method)];
147 const auto* p_custom_request = custom_request.empty() ? NULL : custom_request.c_str();
148 curl_easy_setopt( curl.get(), CURLOPT_CUSTOMREQUEST, p_custom_request );
153 curl_easy_setopt( curl.get(), CURLOPT_HTTPGET, false );
154 curl_easy_setopt( curl.get(), CURLOPT_POST, true );
155 curl_easy_setopt( curl.get(), CURLOPT_POSTFIELDS, query.c_str() );
159 curl_easy_setopt( curl.get(), CURLOPT_POSTFIELDS, NULL );
160 curl_easy_setopt( curl.get(), CURLOPT_POST, false );
161 curl_easy_setopt( curl.get(), CURLOPT_HTTPGET, true );
164 curl_easy_setopt( curl.get(), CURLOPT_WRITEFUNCTION, curl_write_function );
165 curl_easy_setopt( curl.get(), CURLOPT_WRITEDATA, (
void *)(&resp.
content) );
166 curl_easy_perform( curl.get() );
169 curl_easy_getinfo( curl.get(), CURLINFO_RESPONSE_CODE, &
code );
170 resp.
code =
static_cast<uint16_t
>(
code );
186 const std::string& query )
const 192 const std::string& query )
const 199 const auto response = curl.get( base_url +
"_nodes", auth );
202 return !response.content.empty();
207 const auto response = curl.get( base_url, auth );
208 if( !response.is_200() )
209 FC_THROW(
"Error on es_client::get_version(): code = ${code}, message = ${message} ",
210 (
"code", response.code) (
"message", response.content) );
213 return content[
"version"][
"number"].
as_string();
218 static const int64_t version_7 = 7;
220 const auto es_version = get_version();
221 auto dot_pos = es_version.find(
'.');
222 result = ( std::stoi(es_version.substr(0,dot_pos)) >= version_7 );
226 wlog(
"Unable to get ES version, assuming it is 7 or above" );
233 auto bulk_str = boost::algorithm::join( bulk_lines,
"\n" ) +
"\n";
234 const auto response = curl.post( base_url +
"_bulk", auth, bulk_str );
236 return handle_bulk_response( response.code, response.content );
241 const auto response = curl.del( base_url + path, auth );
244 return !response.content.empty();
249 const auto response = curl.get( base_url + path, auth );
252 return response.content;
257 const auto response = curl.post( base_url + path, auth, query );
260 return response.content;
275 static const std::unordered_set<std::string> flattened_fields = {
"account_auths",
"address_auths",
"key_auths" };
281 static const std::unordered_map<std::string, data_type> to_string_fields = {
282 {
"parameters", data_type::array_type },
283 {
"op", data_type::static_variant_type },
284 {
"proposed_ops", data_type::array_type },
285 {
"operations", data_type::array_type },
286 {
"initializer", data_type::static_variant_type },
287 {
"policy", data_type::static_variant_type },
288 {
"predicates", data_type::array_type },
289 {
"active_special_authority", data_type::static_variant_type },
290 {
"owner_special_authority", data_type::static_variant_type },
291 {
"htlc_preimage_hash", data_type::static_variant_type },
292 {
"argument", data_type::static_variant_type },
293 {
"feeds", data_type::map_type },
294 {
"acceptable_collateral", data_type::map_type },
295 {
"acceptable_borrowers", data_type::map_type }
297 std::vector<std::pair<std::string, fc::variants>> original_arrays;
298 std::vector<std::string> keys_to_rename;
301 const std::string& name = i.key();
302 auto& element = i.value();
303 if( element.is_object() )
305 const auto& vo = element.get_object();
306 if( vo.contains(name.c_str()) )
307 keys_to_rename.emplace_back(name);
308 element = adapt( vo, max_depth - 1 );
312 if( !element.is_array() )
315 auto& array = element.get_array();
316 if( to_string_fields.find(name) != to_string_fields.end() )
320 original_arrays.emplace_back( name, array );
323 else if( flattened_fields.find(name) != flattened_fields.end() )
329 original_arrays.emplace_back( name, std::move( backup ) );
331 in_situ_adapt( array, max_depth - 1 );
334 in_situ_adapt( array, max_depth - 1 );
337 for(
const auto& i : keys_to_rename )
339 std::string new_name = i +
"_";
344 if( o.find(
"nonce") != o.end() )
346 o[
"nonce"] = o[
"nonce"].as_string();
349 if( o.find(
"owner") != o.end() && o[
"owner"].is_string() )
351 o[
"owner_"] = o[
"owner"].as_string();
355 for(
const auto& pair : original_arrays )
357 const auto& name = pair.first;
358 auto& value = pair.second;
359 auto type = data_type::map_type;
360 if( to_string_fields.find(name) != to_string_fields.end() )
361 type = to_string_fields.at(name);
362 o[name +
"_object"] = adapt( value, type, max_depth - 1 );
372 if( data_type::static_variant_type == type )
373 return adapt_static_variant( v, max_depth );
377 vs.reserve( v.size() );
378 for(
const auto& item : v )
380 if( item.is_array() )
382 if( data_type::map_type == type )
383 vs.push_back( adapt_map_item( item.get_array(), max_depth ) );
385 vs.push_back( adapt_static_variant( item.get_array(), max_depth ) );
387 else if( item.is_object() )
388 vs.push_back( adapt( item.get_object(), max_depth ) );
390 wlog(
"Type of item is unexpected: ${item}", (
"item", item) );
401 FC_ASSERT( max_depth > 0,
"Internal error" );
403 mv[prefix +
"_object"] = adapt( v.
get_object(), max_depth - 1 );
405 mv[prefix +
"_int"] = v;
407 mv[prefix +
"_bool"] = v;
426 FC_ASSERT( v.size() == 2,
"Internal error" );
429 extract_data_from_variant( v[0], mv,
"key", max_depth );
430 extract_data_from_variant( v[1], mv,
"data", max_depth );
446 FC_ASSERT( v.size() == 2,
"Internal error" );
450 extract_data_from_variant( v[1], mv,
"data", max_depth );
459 for(
auto& array_element : v )
461 if( array_element.is_object() )
462 array_element = adapt( array_element.get_object(), max_depth );
463 else if( array_element.is_array() )
464 in_situ_adapt( array_element.get_array(), max_depth );
466 array_element = array_element.as_string();
static string to_string(const variant &v, output_formatting format=stringify_large_ints_and_doubles, uint32_t max_depth=DEFAULT_MAX_RECURSION_DEPTH)
static fc::variant adapt_static_variant(const fc::variants &v, uint16_t max_depth)
http_response request(http_request_method method, const std::string &url, const std::string &auth, const std::string &query) const
http_response del(const std::string &url, const std::string &auth) const
An order-perserving dictionary of variant's.
bool check_status() const
std::vector< variant > variants
bool del(const std::string &path) const
std::string query(const std::string &path, const std::string &query) const
std::vector< std::string > createBulk(const fc::mutable_variant_object &bulk_header, std::string &&data)
http_response post(const std::string &url, const std::string &auth, const std::string &query) const
void check_version_7_or_above(bool &result) const noexcept
variant_object & get_object()
static fc::variant adapt_map_item(const fc::variants &v, uint16_t max_depth)
http_response put(const std::string &url, const std::string &auth, const std::string &query) const
void to_variant(const flat_set< T, A... > &var, variant &vo, uint32_t _max_depth)
std::string get_version() const
static constexpr uint16_t HTTP_200
#define FC_CAPTURE_AND_RETHROW(...)
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.
static fc::variant adapt(const fc::variant_object &op, uint16_t max_depth)
static constexpr uint16_t HTTP_413
stores null, int64, uint64, double, bool, string, std::vector<variant>, and variant_object's.
http_response get(const std::string &url, const std::string &auth) const
Defines exception's used by fc.
static void extract_data_from_variant(const fc::variant &v, fc::mutable_variant_object &mv, const std::string &prefix, uint16_t max_depth)
Extract data from v into mv.
std::string to_string(double)
static variant from_string(const string &utf8_str, parse_type ptype=legacy_parser, uint32_t max_depth=DEFAULT_MAX_RECURSION_DEPTH)
const std::string & get_string() const
#define FC_PACK_MAX_DEPTH
bool send_bulk(const std::vector< std::string > &bulk_lines) const
static constexpr uint16_t HTTP_401
static void in_situ_adapt(fc::variants &v, uint16_t max_depth)
Update directly, no return.
An order-perserving dictionary of variant's.
std::string as_string() const
std::string get(const std::string &path) const