Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
fc::http_client_impl Class Reference

Classes

struct  check_closed_visitor
 
struct  read_response_visitor
 
struct  write_request_visitor
 

Public Types

using host_key = std::tuple<std::string, std::string, uint16_t>
 
using raw_socket_ptr = std::unique_ptr<tcp::socket>
 
using ssl_socket_ptr = std::unique_ptr<ssl::stream<tcp::socket>>
 
using unix_socket_ptr = std::unique_ptr<local::stream_protocol::socket>
 
using connection = std::variant<raw_socket_ptr, ssl_socket_ptr, unix_socket_ptr>
 
using connection_map = std::map<host_key, connection>
 
using unix_url_split_map = std::map<string, fc::url>
 
using error_code = boost::system::error_code
 
using deadline_type = boost::posix_time::ptime
 

Public Member Functions

 http_client_impl ()
 
void add_cert (const std::string &cert_pem_string)
 
void set_verify_peers (bool enabled)
 
template<typename SyncReadStream , typename Fn , typename CancelFn >
error_code sync_do_with_deadline (SyncReadStream &s, deadline_type deadline, Fn f, CancelFn cf)
 
template<typename SyncReadStream , typename Fn >
error_code sync_do_with_deadline (SyncReadStream &s, deadline_type deadline, Fn f)
 
template<typename SyncReadStream >
error_code sync_connect_with_timeout (SyncReadStream &s, const std::string &host, const std::string &port, const deadline_type &deadline)
 
template<typename SyncReadStream >
error_code sync_write_with_timeout (SyncReadStream &s, http::request< http::string_body > &req, const deadline_type &deadline)
 
template<typename SyncReadStream >
error_code sync_read_with_timeout (SyncReadStream &s, boost::beast::flat_buffer &buffer, http::response< http::string_body > &res, const deadline_type &deadline)
 
host_key url_to_host_key (const url &dest)
 
connection_map::iterator create_unix_connection (const url &dest, const deadline_type &deadline)
 
connection_map::iterator create_raw_connection (const url &dest, const deadline_type &deadline)
 
connection_map::iterator create_ssl_connection (const url &dest, const deadline_type &deadline)
 
connection_map::iterator create_connection (const url &dest, const deadline_type &deadline)
 
bool check_closed (const connection_map::iterator &conn_itr)
 
connection_map::iterator get_connection (const url &dest, const deadline_type &deadline)
 
variant post_sync (const url &dest, const variant &payload, const fc::time_point &_deadline)
 
const fc::urlget_unix_url (const std::string &full_url)
 

Public Attributes

boost::asio::io_context _ioc
 
ssl::context _sslc
 
connection_map _connections
 
unix_url_split_map _unix_url_paths
 

Detailed Description

Definition at line 32 of file http_client.cpp.

Member Typedef Documentation

◆ connection

◆ connection_map

Definition at line 39 of file http_client.cpp.

◆ deadline_type

using fc::http_client_impl::deadline_type = boost::posix_time::ptime

Definition at line 42 of file http_client.cpp.

◆ error_code

using fc::http_client_impl::error_code = boost::system::error_code

Definition at line 41 of file http_client.cpp.

◆ host_key

using fc::http_client_impl::host_key = std::tuple<std::string, std::string, uint16_t>

Definition at line 34 of file http_client.cpp.

◆ raw_socket_ptr

using fc::http_client_impl::raw_socket_ptr = std::unique_ptr<tcp::socket>

Definition at line 35 of file http_client.cpp.

◆ ssl_socket_ptr

using fc::http_client_impl::ssl_socket_ptr = std::unique_ptr<ssl::stream<tcp::socket>>

Definition at line 36 of file http_client.cpp.

◆ unix_socket_ptr

using fc::http_client_impl::unix_socket_ptr = std::unique_ptr<local::stream_protocol::socket>

Definition at line 37 of file http_client.cpp.

◆ unix_url_split_map

Definition at line 40 of file http_client.cpp.

Constructor & Destructor Documentation

◆ http_client_impl()

fc::http_client_impl::http_client_impl ( )
inline

Definition at line 44 of file http_client.cpp.

45 :_ioc()
46 ,_sslc(ssl::context::sslv23_client)
47 {
48 set_verify_peers(true);
49 }
void set_verify_peers(bool enabled)
boost::asio::io_context _ioc
Here is the call graph for this function:

Member Function Documentation

◆ add_cert()

void fc::http_client_impl::add_cert ( const std::string & cert_pem_string)
inline

Definition at line 51 of file http_client.cpp.

51 {
52 error_code ec;
53 _sslc.add_certificate_authority(boost::asio::buffer(cert_pem_string.data(), cert_pem_string.size()), ec);
54 FC_ASSERT(!ec, "Failed to add cert: ${msg}", ("msg", ec.message()));
55 }
#define FC_ASSERT(TEST,...)
Checks a condition and throws an assert_exception if the test is FALSE.

◆ check_closed()

bool fc::http_client_impl::check_closed ( const connection_map::iterator & conn_itr)
inline

Definition at line 248 of file http_client.cpp.

248 {
249 if (std::visit(check_closed_visitor(), conn_itr->second)) {
250 _connections.erase(conn_itr);
251 return true;
252 } else {
253 return false;
254 }
255 }
connection_map _connections

◆ create_connection()

connection_map::iterator fc::http_client_impl::create_connection ( const url & dest,
const deadline_type & deadline )
inline

Definition at line 222 of file http_client.cpp.

222 {
223 if (dest.proto() == "http") {
224 return create_raw_connection(dest, deadline);
225 } else if (dest.proto() == "https") {
226 return create_ssl_connection(dest, deadline);
227 } else if (dest.proto() == "unix") {
228 return create_unix_connection(dest, deadline);
229 } else {
230 FC_THROW("Unknown protocol ${proto}", ("proto", dest.proto()));
231 }
232 }
connection_map::iterator create_raw_connection(const url &dest, const deadline_type &deadline)
connection_map::iterator create_ssl_connection(const url &dest, const deadline_type &deadline)
connection_map::iterator create_unix_connection(const url &dest, const deadline_type &deadline)
#define FC_THROW( ...)
Here is the call graph for this function:

◆ create_raw_connection()

connection_map::iterator fc::http_client_impl::create_raw_connection ( const url & dest,
const deadline_type & deadline )
inline

Definition at line 178 of file http_client.cpp.

178 {
179 auto key = url_to_host_key(dest);
180 auto socket = std::make_unique<tcp::socket>(_ioc);
181
182 error_code ec = sync_connect_with_timeout(*socket, *dest.host(), dest.port() ? std::to_string(*dest.port()) : "80", deadline);
183 FC_ASSERT(!ec, "Failed to connect: ${message}", ("message",ec.message()));
184
185 auto res = _connections.emplace(std::piecewise_construct,
186 std::forward_as_tuple(key),
187 std::forward_as_tuple(std::move(socket)));
188
189 return res.first;
190 }
error_code sync_connect_with_timeout(SyncReadStream &s, const std::string &host, const std::string &port, const deadline_type &deadline)
host_key url_to_host_key(const url &dest)
Here is the call graph for this function:

◆ create_ssl_connection()

connection_map::iterator fc::http_client_impl::create_ssl_connection ( const url & dest,
const deadline_type & deadline )
inline

Definition at line 192 of file http_client.cpp.

192 {
193 auto key = url_to_host_key(dest);
194 auto ssl_socket = std::make_unique<ssl::stream<tcp::socket>>(_ioc, _sslc);
195
196 // Set SNI Hostname (many hosts need this to handshake successfully)
197 if(!SSL_set_tlsext_host_name(ssl_socket->native_handle(), dest.host()->c_str()))
198 {
199 error_code ec{static_cast<int>(::ERR_get_error()), boost::asio::error::get_ssl_category()};
200 FC_THROW("Unable to set SNI Host Name: ${msg}", ("msg", ec.message()));
201 }
202
203 ssl_socket->set_verify_callback(boost::asio::ssl::rfc2818_verification(*dest.host()));
204
205 error_code ec = sync_connect_with_timeout(ssl_socket->next_layer(), *dest.host(), dest.port() ? std::to_string(*dest.port()) : "443", deadline);
206 if (!ec) {
207 ec = sync_do_with_deadline(ssl_socket->next_layer(), deadline, [&ssl_socket](std::optional<error_code>& final_ec) {
208 ssl_socket->async_handshake(ssl::stream_base::client, [&final_ec](const error_code& ec) {
209 final_ec.emplace(ec);
210 });
211 });
212 }
213 FC_ASSERT(!ec, "Failed to connect: ${message}", ("message",ec.message()));
214
215 auto res = _connections.emplace(std::piecewise_construct,
216 std::forward_as_tuple(key),
217 std::forward_as_tuple(std::move(ssl_socket)));
218
219 return res.first;
220 }
error_code sync_do_with_deadline(SyncReadStream &s, deadline_type deadline, Fn f, CancelFn cf)
Here is the call graph for this function:

◆ create_unix_connection()

connection_map::iterator fc::http_client_impl::create_unix_connection ( const url & dest,
const deadline_type & deadline )
inline

Definition at line 163 of file http_client.cpp.

163 {
164 auto key = url_to_host_key(dest);
165 auto socket = std::make_unique<local::stream_protocol::socket>(_ioc);
166
167 error_code ec;
168 socket->connect(local::stream_protocol::endpoint(*dest.host()), ec);
169 FC_ASSERT(!ec, "Failed to connect: ${message}", ("message",ec.message()));
170
171 auto res = _connections.emplace(std::piecewise_construct,
172 std::forward_as_tuple(key),
173 std::forward_as_tuple(std::move(socket)));
174
175 return res.first;
176 }
Here is the call graph for this function:

◆ get_connection()

connection_map::iterator fc::http_client_impl::get_connection ( const url & dest,
const deadline_type & deadline )
inline

Definition at line 257 of file http_client.cpp.

257 {
258 auto key = url_to_host_key(dest);
259 auto conn_itr = _connections.find(key);
260 if (conn_itr == _connections.end() || check_closed(conn_itr)) {
261 return create_connection(dest, deadline);
262 } else {
263 return conn_itr;
264 }
265 }
bool check_closed(const connection_map::iterator &conn_itr)
connection_map::iterator create_connection(const url &dest, const deadline_type &deadline)

◆ get_unix_url()

const fc::url & fc::http_client_impl::get_unix_url ( const std::string & full_url)
inline

Definition at line 399 of file http_client.cpp.

399 {
400 unix_url_split_map::const_iterator found = _unix_url_paths.find(full_url);
401 if(found != _unix_url_paths.end())
402 return found->second;
403
404 boost::filesystem::path socket_file(full_url);
405 if(socket_file.is_relative())
406 FC_THROW_EXCEPTION( parse_error_exception, "socket url cannot be relative (${url})", ("url", socket_file.string()));
407 if(socket_file.empty())
408 FC_THROW_EXCEPTION( parse_error_exception, "missing socket url");
409 boost::filesystem::path url_path;
410 do {
411 if(boost::filesystem::status(socket_file).type() == boost::filesystem::socket_file)
412 break;
413 url_path = socket_file.filename() / url_path;
414 socket_file = socket_file.remove_filename();
415 } while(!socket_file.empty());
416 if(socket_file.empty())
417 FC_THROW_EXCEPTION( parse_error_exception, "couldn't discover socket path");
418 url_path = "/" / url_path;
419 return _unix_url_paths.emplace(full_url, fc::url("unix", socket_file.string(), ostring(), ostring(), url_path.string(), ostring(), ovariant_object(), std::optional<uint16_t>())).first->second;
420 }
unix_url_split_map _unix_url_paths
#define FC_THROW_EXCEPTION(EXCEPTION, FORMAT,...)
std::optional< fc::string > ostring
Definition url.hpp:10
std::optional< fc::variant_object > ovariant_object
Definition url.hpp:12
yh_object_type type
Definition yubihsm.h:672

◆ post_sync()

variant fc::http_client_impl::post_sync ( const url & dest,
const variant & payload,
const fc::time_point & _deadline )
inline

Definition at line 303 of file http_client.cpp.

303 {
304 static const deadline_type epoch(boost::gregorian::date(1970, 1, 1));
305 auto deadline = epoch + boost::posix_time::microseconds(_deadline.time_since_epoch().count());
306 FC_ASSERT(dest.host(), "No host set on URL");
307
308 string path = dest.path() ? dest.path()->generic_string() : "/";
309 if (dest.query()) {
310 path = path + "?" + *dest.query();
311 }
312
313 string host_str = *dest.host();
314 if (dest.port()) {
315 auto port = *dest.port();
316 auto proto_iter = default_proto_ports.find(dest.proto());
317 if (proto_iter != default_proto_ports.end() && proto_iter->second != port) {
318 host_str = host_str + ":" + std::to_string(port);
319 }
320 }
321
322 http::request<http::string_body> req{http::verb::post, path, 11};
323 req.set(http::field::host, host_str);
324 req.set(http::field::user_agent, BOOST_BEAST_VERSION_STRING);
325 req.set(http::field::content_type, "application/json");
326 req.keep_alive(true);
327 req.body() = json::to_string(payload, _deadline);
328 req.prepare_payload();
329
330 auto conn_iter = get_connection(dest, deadline);
331 auto eraser = make_scoped_exit([this, &conn_iter](){
332 _connections.erase(conn_iter);
333 });
334
335 // Send the HTTP request to the remote host
336 error_code ec = std::visit(write_request_visitor(this, req, deadline), conn_iter->second);
337 FC_ASSERT(!ec, "Failed to send request: ${message}", ("message",ec.message()));
338
339 // This buffer is used for reading and must be persisted
340 boost::beast::flat_buffer buffer;
341
342 // Declare a container to hold the response
343 http::response<http::string_body> res;
344
345 // Receive the HTTP response
346 ec = std::visit(read_response_visitor(this, buffer, res, deadline), conn_iter->second);
347 FC_ASSERT(!ec, "Failed to read response: ${message}", ("message",ec.message()));
348
349 // if the connection can be kept open, keep it open
350 if (res.keep_alive()) {
351 eraser.cancel();
352 }
353
354 fc::variant result;
355 if( !res.body().empty() ) {
356 try {
357 result = json::from_string( res.body() );
358 } catch( ... ) {}
359 }
360 if (res.result() == http::status::internal_server_error) {
362 try {
363 auto err_var = result.get_object()["error"].get_object();
364 excp = std::make_shared<fc::exception>(err_var["code"].as_int64(), err_var["name"].as_string(), err_var["what"].as_string());
365
366 if (err_var.contains("details")) {
367 for (const auto& dvar : err_var["details"].get_array()) {
368 excp->append_log(FC_LOG_MESSAGE(error, dvar.get_object()["message"].as_string()));
369 }
370 }
371 } catch( ... ) {
372
373 }
374
375 if (excp) {
376 throw *excp;
377 } else {
378 FC_THROW("Request failed with 500 response, but response was not parseable");
379 }
380 } else if (res.result() == http::status::not_found) {
381 FC_THROW("URL not found: ${url}", ("url", (std::string)dest));
382 }
383
384 return result;
385 }
connection_map::iterator get_connection(const url &dest, const deadline_type &deadline)
boost::posix_time::ptime deadline_type
static string to_string(const variant &v, const yield_function_t &yield, const output_formatting format=output_formatting::stringify_large_ints_and_doubles)
Definition json.cpp:674
static variant from_string(const string &utf8_str, const parse_type ptype=parse_type::legacy_parser, uint32_t max_depth=DEFAULT_MAX_RECURSION_DEPTH)
Definition json.cpp:442
constexpr int64_t count() const
Definition time.hpp:26
constexpr const microseconds & time_since_epoch() const
Definition time.hpp:52
stores null, int64, uint64, double, bool, string, std::vector<variant>, and variant_object's.
Definition variant.hpp:191
#define FC_LOG_MESSAGE(LOG_LEVEL, FORMAT,...)
A helper method for generating log messages.
std::shared_ptr< exception > exception_ptr
scoped_exit< Callback > make_scoped_exit(Callback &&c)
Here is the call graph for this function:

◆ set_verify_peers()

void fc::http_client_impl::set_verify_peers ( bool enabled)
inline

Definition at line 57 of file http_client.cpp.

57 {
58 if (enabled) {
59 _sslc.set_verify_mode(ssl::verify_peer);
60 } else {
61 _sslc.set_verify_mode(ssl::verify_none);
62 }
63 }
Here is the caller graph for this function:

◆ sync_connect_with_timeout()

template<typename SyncReadStream >
error_code fc::http_client_impl::sync_connect_with_timeout ( SyncReadStream & s,
const std::string & host,
const std::string & port,
const deadline_type & deadline )
inline

Definition at line 110 of file http_client.cpp.

110 {
111 tcp::resolver local_resolver(_ioc);
112 bool cancelled = false;
113
114 auto res = sync_do_with_deadline(s, deadline, [&local_resolver, &cancelled, &s, &host, &port](std::optional<error_code>& final_ec){
115 local_resolver.async_resolve(host, port, [&cancelled, &s, &final_ec](const error_code& ec, tcp::resolver::results_type resolved ){
116 if (ec) {
117 final_ec.emplace(ec);
118 return;
119 }
120
121 if (!cancelled) {
122 boost::asio::async_connect(s, resolved.begin(), resolved.end(), [&final_ec](const error_code& ec, tcp::resolver::iterator ){
123 final_ec.emplace(ec);
124 });
125 }
126 });
127 },[&local_resolver, &cancelled](){
128 cancelled = true;
129 local_resolver.cancel();
130 });
131
132 return res;
133 };
char * s
Here is the call graph for this function:
Here is the caller graph for this function:

◆ sync_do_with_deadline() [1/2]

template<typename SyncReadStream , typename Fn >
error_code fc::http_client_impl::sync_do_with_deadline ( SyncReadStream & s,
deadline_type deadline,
Fn f )
inline

Definition at line 103 of file http_client.cpp.

103 {
104 return sync_do_with_deadline(s, deadline, f, [&s](){
105 s.lowest_layer().cancel();
106 });
107 };
Here is the call graph for this function:

◆ sync_do_with_deadline() [2/2]

template<typename SyncReadStream , typename Fn , typename CancelFn >
error_code fc::http_client_impl::sync_do_with_deadline ( SyncReadStream & s,
deadline_type deadline,
Fn f,
CancelFn cf )
inline

Definition at line 66 of file http_client.cpp.

66 {
67 bool timer_expired = false;
68 boost::asio::deadline_timer timer(_ioc);
69
70 timer.expires_at(deadline);
71 bool timer_cancelled = false;
72 timer.async_wait([&timer_expired, &timer_cancelled] (const error_code&) {
73 // the only non-success error_code this is called with is operation_aborted but since
74 // we could have queued "success" when we cancelled the timer, we set a flag at the
75 // safer scope and only respect that.
76 if (!timer_cancelled) {
77 timer_expired = true;
78 }
79 });
80
81 std::optional<error_code> f_result;
82 f(f_result);
83
84 _ioc.restart();
85 while (_ioc.run_one())
86 {
87 if (f_result) {
88 timer_cancelled = true;
89 timer.cancel();
90 } else if (timer_expired) {
91 cf();
92 }
93 }
94
95 if (!timer_expired) {
96 return *f_result;
97 } else {
98 return error_code(boost::system::errc::timed_out, boost::system::system_category());
99 }
100 }
boost::system::error_code error_code
Here is the caller graph for this function:

◆ sync_read_with_timeout()

template<typename SyncReadStream >
error_code fc::http_client_impl::sync_read_with_timeout ( SyncReadStream & s,
boost::beast::flat_buffer & buffer,
http::response< http::string_body > & res,
const deadline_type & deadline )
inline

Definition at line 145 of file http_client.cpp.

145 {
146 return sync_do_with_deadline(s, deadline, [&s, &buffer, &res](std::optional<error_code>& final_ec){
147 http::async_read(s, buffer, res, [&final_ec]( const error_code& ec, std::size_t ) {
148 final_ec.emplace(ec);
149 });
150 });
151 }
Here is the call graph for this function:

◆ sync_write_with_timeout()

template<typename SyncReadStream >
error_code fc::http_client_impl::sync_write_with_timeout ( SyncReadStream & s,
http::request< http::string_body > & req,
const deadline_type & deadline )
inline

Definition at line 136 of file http_client.cpp.

136 {
137 return sync_do_with_deadline(s, deadline, [&s, &req](std::optional<error_code>& final_ec){
138 http::async_write(s, req, [&final_ec]( const error_code& ec, std::size_t ) {
139 final_ec.emplace(ec);
140 });
141 });
142 }
Here is the call graph for this function:

◆ url_to_host_key()

host_key fc::http_client_impl::url_to_host_key ( const url & dest)
inline

Definition at line 153 of file http_client.cpp.

153 {
154 FC_ASSERT(dest.host(), "Provided URL has no host");
155 uint16_t port = 80;
156 if (dest.port()) {
157 port = *dest.port();
158 }
159
160 return std::make_tuple(dest.proto(), *dest.host(), port);
161 }
unsigned short uint16_t
Definition stdint.h:125
Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ _connections

connection_map fc::http_client_impl::_connections

Definition at line 424 of file http_client.cpp.

◆ _ioc

boost::asio::io_context fc::http_client_impl::_ioc

Definition at line 422 of file http_client.cpp.

◆ _sslc

ssl::context fc::http_client_impl::_sslc

Definition at line 423 of file http_client.cpp.

◆ _unix_url_paths

unix_url_split_map fc::http_client_impl::_unix_url_paths

Definition at line 425 of file http_client.cpp.


The documentation for this class was generated from the following file: