Provide last error from http client to UI

This commit is contained in:
Lee Clagett 2020-12-02 19:07:02 -05:00 committed by Lee *!* Clagett
parent 059028a30a
commit 5736862baf
13 changed files with 109 additions and 93 deletions

View File

@ -25,6 +25,7 @@
#pragma once
#include <boost/system/error_code.hpp>
#include <string>
#include <boost/optional/optional.hpp>
#include "http_auth.h"
@ -69,6 +70,7 @@ namespace http
virtual bool connect(std::chrono::milliseconds timeout) = 0;
virtual bool disconnect() = 0;
virtual bool is_connected(bool *ssl = NULL) = 0;
virtual boost::system::error_code last_error();
virtual bool invoke(const boost::string_ref uri, const boost::string_ref method, const boost::string_ref body, std::chrono::milliseconds timeout, const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) = 0;
virtual bool invoke_get(const boost::string_ref uri, std::chrono::milliseconds timeout, const std::string& body = std::string(), const http_response_info** ppresponse_info = NULL, const fields_list& additional_params = fields_list()) = 0;
virtual uint64_t get_bytes_sent() const = 0;

View File

@ -135,7 +135,7 @@ class connection_basic { // not-templated base class for rapid developmet of som
bool handshake(boost::asio::ssl::stream_base::handshake_type type, boost::asio::const_buffer buffer = {})
{
//m_state != nullptr verified in constructor
return m_state->ssl_options().handshake(socket_, type, buffer);
return !bool(m_state->ssl_options().handshake(socket_, type, buffer));
}
template<typename MutableBufferSequence, typename ReadHandler>

View File

@ -161,6 +161,12 @@ namespace net_utils
return m_net_client.is_connected(ssl);
}
//---------------------------------------------------------------------------
boost::system::error_code last_error() override final
{
CRITICAL_REGION_LOCAL(m_lock);
return m_net_client.last_error();
}
//---------------------------------------------------------------------------
virtual bool handle_target_data(std::string& piece_of_transfer) override
{
CRITICAL_REGION_LOCAL(m_lock);

View File

@ -63,15 +63,6 @@ namespace net_utils
class blocked_mode_client
{
enum try_connect_result_t
{
CONNECT_SUCCESS,
CONNECT_FAILURE,
CONNECT_NO_SSL,
};
struct handler_obj
{
handler_obj(boost::system::error_code& error, size_t& bytes_transferred):ref_error(error), ref_bytes_transferred(bytes_transferred)
@ -91,6 +82,13 @@ namespace net_utils
}
};
static boost::system::error_code guarantee_error(const boost::system::error_code error)
{
if (error)
return error;
return make_error_code(boost::system::errc::not_connected);
}
public:
inline
blocked_mode_client() :
@ -99,7 +97,7 @@ namespace net_utils
m_ssl_socket(new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(m_io_service, m_ctx)),
m_connector(direct_connect{}),
m_ssl_options(epee::net_utils::ssl_support_t::e_ssl_support_autodetect),
m_connected(false),
m_stream_error(make_error_code(boost::system::errc::not_connected)),
m_deadline(m_io_service, std::chrono::steady_clock::time_point::max()),
m_shutdowned(false),
m_bytes_sent(0),
@ -142,7 +140,7 @@ namespace net_utils
}
inline
try_connect_result_t try_connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout)
boost::system::error_code try_connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout)
{
m_deadline.expires_from_now(timeout);
boost::unique_future<boost::asio::ip::tcp::socket> connection = m_connector(addr, port, m_deadline);
@ -159,79 +157,65 @@ namespace net_utils
m_deadline.cancel();
if (m_ssl_socket->next_layer().is_open())
{
m_connected = true;
boost::system::error_code error{};
m_deadline.expires_at(std::chrono::steady_clock::time_point::max());
// SSL Options
if (m_ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_enabled || m_ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
{
if (!m_ssl_options.handshake(*m_ssl_socket, boost::asio::ssl::stream_base::client, {}, addr, timeout))
if ((error = m_ssl_options.handshake(*m_ssl_socket, boost::asio::ssl::stream_base::client, {}, addr, timeout)))
{
if (m_ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
{
boost::system::error_code ignored_ec;
m_ssl_socket->next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
m_ssl_socket->next_layer().close();
m_connected = false;
return CONNECT_NO_SSL;
}
else
{
MWARNING("Failed to establish SSL connection");
m_connected = false;
return CONNECT_FAILURE;
}
MWARNING("Failed to establish SSL connection");
boost::system::error_code ignored_ec;
m_ssl_socket->next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
m_ssl_socket->next_layer().close();
}
}
return CONNECT_SUCCESS;
}else
{
MWARNING("Some problems at connect, expected open socket");
return CONNECT_FAILURE;
return error;
}
MWARNING("Some problems at connect, expected open socket");
return boost::asio::error::not_connected;
}
inline
bool connect(const std::string& addr, const std::string& port, std::chrono::milliseconds timeout)
{
m_connected = false;
try
{
m_ssl_socket->next_layer().close();
disconnect();
// Set SSL options
// disable sslv2
m_ssl_socket.reset(new boost::asio::ssl::stream<boost::asio::ip::tcp::socket>(m_io_service, m_ctx));
// Get a list of endpoints corresponding to the server name.
try_connect_result_t try_connect_result = try_connect(addr, port, timeout);
if (try_connect_result == CONNECT_FAILURE)
return false;
if (m_ssl_options.support == epee::net_utils::ssl_support_t::e_ssl_support_autodetect)
boost::system::error_code error = try_connect(addr, port, timeout);
if (error && error != boost::asio::error::not_connected && m_ssl_options.support == ssl_support_t::e_ssl_support_autodetect)
{
if (try_connect_result == CONNECT_NO_SSL)
{
MERROR("SSL handshake failed on an autodetect connection, reconnecting without SSL");
m_ssl_options.support = epee::net_utils::ssl_support_t::e_ssl_support_disabled;
if (try_connect(addr, port, timeout) != CONNECT_SUCCESS)
return false;
}
MERROR("SSL handshake failed on an autodetect connection, reconnecting without SSL");
m_ssl_options.support = ssl_support_t::e_ssl_support_disabled;
error = try_connect(addr, port, timeout);
}
m_stream_error = error;
}
catch(const boost::system::system_error& er)
catch (const boost::system::system_error& er)
{
m_stream_error = guarantee_error(er.code());
MDEBUG("Some problems at connect, message: " << er.what());
}
catch(const std::exception& er)
{
m_stream_error = make_error_code(boost::system::errc::not_connected);
MDEBUG("Some problems at connect, message: " << er.what());
return false;
}
catch(...)
{
m_stream_error = make_error_code(boost::system::errc::not_connected);
MDEBUG("Some fatal problems.");
return false;
}
return true;
return !m_stream_error;
}
//! Change the connection routine (proxy, etc.)
void set_connector(std::function<connect_func> connector)
{
@ -243,9 +227,9 @@ namespace net_utils
{
try
{
if(m_connected)
if(!last_error())
{
m_connected = false;
m_stream_error = make_error_code(boost::system::errc::not_connected);
if(m_ssl_options)
shutdown_ssl();
m_ssl_socket->next_layer().shutdown(boost::asio::ip::tcp::socket::shutdown_both);
@ -295,8 +279,8 @@ namespace net_utils
if (ec)
{
m_stream_error = ec;
LOG_PRINT_L3("Problems at write: " << ec.message());
m_connected = false;
return false;
}else
{
@ -307,11 +291,13 @@ namespace net_utils
catch(const boost::system::system_error& er)
{
m_stream_error = guarantee_error(er.code());
LOG_ERROR("Some problems at connect, message: " << er.what());
return false;
}
catch(...)
{
m_stream_error = make_error_code(boost::system::errc::not_connected);
LOG_ERROR("Some fatal problems.");
return false;
}
@ -319,9 +305,11 @@ namespace net_utils
return true;
}
const boost::system::error_code& last_error() const noexcept { return m_stream_error; }
bool is_connected(bool *ssl = NULL)
{
if (!m_connected || !m_ssl_socket->next_layer().is_open())
if (last_error() || !m_ssl_socket->next_layer().is_open())
return false;
if (ssl)
*ssl = m_ssl_options.support != ssl_support_t::e_ssl_support_disabled;
@ -371,6 +359,7 @@ namespace net_utils
if (ec)
{
m_stream_error = ec;
MTRACE("READ ENDS: Connection err_code " << ec.value());
if(ec == boost::asio::error::eof)
{
@ -381,7 +370,6 @@ namespace net_utils
}
MDEBUG("Problems at read: " << ec.message());
m_connected = false;
return false;
}else
{
@ -399,24 +387,22 @@ namespace net_utils
catch(const boost::system::system_error& er)
{
m_stream_error = guarantee_error(er.code());
LOG_ERROR("Some problems at read, message: " << er.what());
m_connected = false;
return false;
}
catch(...)
{
m_stream_error = make_error_code(boost::system::errc::not_connected);
LOG_ERROR("Some fatal problems at read.");
return false;
}
return false;
}
bool shutdown()
{
m_stream_error = make_error_code(boost::system::errc::not_connected);
m_deadline.cancel();
boost::system::error_code ec;
if(m_ssl_options)
@ -431,7 +417,6 @@ namespace net_utils
if(ec)
MDEBUG("Problems at close: " << ec.message());
m_shutdowned = true;
m_connected = false;
return true;
}
@ -457,8 +442,8 @@ namespace net_utils
// The deadline has passed. The socket is closed so that any outstanding
// asynchronous operations are cancelled. This allows the blocked
// connect(), read_line() or write_line() functions to return.
m_stream_error = make_error_code(boost::system::errc::timed_out);
LOG_PRINT_L3("Timed out socket");
m_connected = false;
m_ssl_socket->next_layer().close();
// There is no longer an active deadline. The expiry is set to positive
@ -516,7 +501,7 @@ namespace net_utils
std::shared_ptr<boost::asio::ssl::stream<boost::asio::ip::tcp::socket>> m_ssl_socket;
std::function<connect_func> m_connector;
ssl_options_t m_ssl_options;
bool m_connected;
boost::system::error_code m_stream_error;
boost::asio::steady_timer m_deadline;
std::atomic<bool> m_shutdowned;
std::atomic<uint64_t> m_bytes_sent;

View File

@ -133,9 +133,8 @@ namespace net_utils
`verification == system_ca` the client also does a rfc2818 check to
ensure that the server certificate is to the provided hostname.
\return True if the SSL handshake completes with peer verification
settings. */
bool handshake(
\return Non-empty error_code on failure. */
boost::system::error_code handshake(
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &socket,
boost::asio::ssl::stream_base::handshake_type type,
boost::asio::const_buffer buffer = {},

View File

@ -133,6 +133,13 @@ namespace http
{
return false;
}
boost::system::error_code epee::net_utils::http::abstract_http_client::last_error()
{
if (is_connected())
return boost::system::error_code{};
return make_error_code(boost::system::errc::not_connected);
}
}
}
}

View File

@ -544,7 +544,7 @@ void ssl_options_t::configure(
}
}
bool ssl_options_t::handshake(
boost::system::error_code ssl_options_t::handshake(
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> &socket,
boost::asio::ssl::stream_base::handshake_type type,
boost::asio::const_buffer buffer,
@ -636,12 +636,10 @@ bool ssl_options_t::handshake(
const auto ec = start_handshake();
if (ec)
{
MERROR("SSL handshake failed, connection dropped: " << ec.message());
return false;
}
MDEBUG("SSL handshake success");
return true;
else
MDEBUG("SSL handshake success");
return ec;
}
std::string get_hr_ssl_fingerprint(const X509 *cert, const EVP_MD *fdig)

View File

@ -4554,7 +4554,8 @@ bool simple_wallet::try_connect_to_daemon(bool silent, uint32_t* version)
if (!version)
version = &version_;
bool wallet_is_outdated = false, daemon_is_outdated = false;
if (!m_wallet->check_connection(version, NULL, 200000, &wallet_is_outdated, &daemon_is_outdated))
boost::system::error_code err{};
if (!m_wallet->check_connection(version, NULL, 200000, &wallet_is_outdated, &daemon_is_outdated, std::addressof(err)))
{
if (!silent)
{
@ -4568,7 +4569,7 @@ bool simple_wallet::try_connect_to_daemon(bool silent, uint32_t* version)
tr("Daemon is not up to date. "
"Please make sure the daemon is running the latest version or change the daemon address using the 'set_daemon' command.");
else
fail_msg_writer() << tr("wallet failed to connect to daemon: ") << m_wallet->get_daemon_address() << ". " <<
fail_msg_writer() << tr("wallet failed to connect to daemon: ") << m_wallet->get_daemon_address() << " : " << err.message() << ". " <<
tr("Daemon either is not started or wrong port was passed. "
"Please make sure daemon is running or change the daemon address using the 'set_daemon' command.");
}
@ -9652,9 +9653,10 @@ bool simple_wallet::status(const std::vector<std::string> &args)
uint64_t local_height = m_wallet->get_blockchain_current_height();
uint32_t version = 0;
bool ssl = false;
if (!m_wallet->check_connection(&version, &ssl))
boost::system::error_code error{};
if (!m_wallet->check_connection(&version, &ssl, 200000, nullptr, nullptr, std::addressof(error)))
{
success_msg_writer() << "Refreshed " << local_height << "/?, no daemon connected";
success_msg_writer() << "Refreshed " << local_height << "/?, no daemon connected: " << error.message();
return true;
}

View File

@ -2095,14 +2095,15 @@ bool WalletImpl::verifyMessageWithPublicKey(const std::string &message, const st
bool WalletImpl::connectToDaemon()
{
bool result = m_wallet->check_connection(NULL, NULL, DEFAULT_CONNECTION_TIMEOUT_MILLIS);
boost::system::error_code err{};
bool result = m_wallet->check_connection(NULL, NULL, DEFAULT_CONNECTION_TIMEOUT_MILLIS, NULL, NULL, std::addressof(err));
if (!result) {
setStatusError("Error connecting to daemon at " + m_wallet->get_daemon_address());
setStatusError("Error connecting to daemon at " + m_wallet->get_daemon_address() + " : " + err.message());
} else {
clearStatus();
// start refreshing here
}
return result;
return !err;
}
Wallet::ConnectionStatus WalletImpl::connected() const

View File

@ -5863,7 +5863,7 @@ bool wallet2::prepare_file_names(const std::string& file_path)
return true;
}
//----------------------------------------------------------------------------------------------------
bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout, bool *wallet_is_outdated, bool *daemon_is_outdated)
bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout, bool *wallet_is_outdated, bool *daemon_is_outdated, boost::system::error_code* error)
{
THROW_WALLET_EXCEPTION_IF(!m_is_initialized, error::wallet_not_initialized);
@ -5874,6 +5874,8 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout, b
*version = 0;
if (ssl)
*ssl = false;
if (error)
*error = make_error_code(boost::system::errc::not_connected);
return false;
}
@ -5883,18 +5885,26 @@ bool wallet2::check_connection(uint32_t *version, bool *ssl, uint32_t timeout, b
{
m_rpc_version = 0;
m_node_rpc_proxy.invalidate();
if (!m_http_client->connect(std::chrono::milliseconds(timeout)))
return false;
if(!m_http_client->is_connected(ssl))
if (!m_http_client->connect(std::chrono::milliseconds(timeout)) || !m_http_client->is_connected(ssl))
{
if (error)
*error = m_http_client->last_error();
return false;
}
}
}
if (!m_rpc_version && !check_version(version, wallet_is_outdated, daemon_is_outdated))
{
if (error)
*error = make_error_code(boost::system::errc::protocol_error);
return false;
}
if (version)
*version = m_rpc_version;
if (error)
*error = boost::system::error_code{};
return true;
}
//----------------------------------------------------------------------------------------------------
@ -11534,7 +11544,8 @@ void wallet2::check_tx_key_helper(const cryptonote::transaction &tx, const crypt
void wallet2::check_tx_key_helper(const crypto::hash &txid, const crypto::key_derivation &derivation, const std::vector<crypto::key_derivation> &additional_derivations, const cryptonote::account_public_address &address, uint64_t &received, bool &in_pool, uint64_t &confirmations)
{
uint32_t rpc_version;
THROW_WALLET_EXCEPTION_IF(!check_connection(&rpc_version), error::wallet_internal_error, "Failed to connect to daemon: " + get_daemon_address());
boost::system::error_code error{};
THROW_WALLET_EXCEPTION_IF(!check_connection(&rpc_version, nullptr, 200000, nullptr, nullptr, std::addressof(error)), error::wallet_internal_error, "Failed to connect to daemon (" + get_daemon_address() + "): " + error.message());
COMMAND_RPC_GET_TRANSACTIONS::request req;
COMMAND_RPC_GET_TRANSACTIONS::response res;
@ -12059,7 +12070,8 @@ std::string wallet2::get_reserve_proof(const boost::optional<std::pair<uint32_t,
bool wallet2::check_reserve_proof(const cryptonote::account_public_address &address, const std::string &message, const std::string &sig_str, uint64_t &total, uint64_t &spent)
{
uint32_t rpc_version;
THROW_WALLET_EXCEPTION_IF(!check_connection(&rpc_version), error::wallet_internal_error, "Failed to connect to daemon: " + get_daemon_address());
boost::system::error_code error{};
THROW_WALLET_EXCEPTION_IF(!check_connection(&rpc_version, nullptr, 200000, nullptr, nullptr, std::addressof(error)), error::wallet_internal_error, "Failed to connect to daemon (" + get_daemon_address() + "): " + error.message());
THROW_WALLET_EXCEPTION_IF(rpc_version < MAKE_CORE_RPC_VERSION(1, 0), error::wallet_internal_error, "Daemon RPC version is too old");
static constexpr char header_v1[] = "ReserveProofV1";
@ -13987,9 +13999,10 @@ bool wallet2::parse_uri(const std::string &uri, std::string &address, std::strin
uint64_t wallet2::get_blockchain_height_by_date(uint16_t year, uint8_t month, uint8_t day)
{
uint32_t version;
if (!check_connection(&version))
boost::system::error_code error{};
if (!check_connection(&version, nullptr, 200000, nullptr, nullptr, std::addressof(error)))
{
throw std::runtime_error("failed to connect to daemon: " + get_daemon_address());
throw std::runtime_error("failed to connect to daemon (" + get_daemon_address() + "): " + error.message());
}
if (version < MAKE_CORE_RPC_VERSION(1, 6))
{

View File

@ -40,6 +40,7 @@
#include <boost/serialization/list.hpp>
#include <boost/serialization/vector.hpp>
#include <boost/serialization/deque.hpp>
#include <boost/system/error_code.hpp>
#include <boost/thread/lock_guard.hpp>
#include <atomic>
#include <random>
@ -1113,7 +1114,7 @@ private:
bool sign_multisig_tx_to_file(multisig_tx_set &exported_txs, const std::string &filename, std::vector<crypto::hash> &txids);
std::vector<pending_tx> create_unmixable_sweep_transactions();
void discard_unmixable_outputs();
bool check_connection(uint32_t *version = NULL, bool *ssl = NULL, uint32_t timeout = 200000, bool *wallet_is_outdated = NULL, bool *daemon_is_outdated = NULL);
bool check_connection(uint32_t *version = NULL, bool *ssl = NULL, uint32_t timeout = 200000, bool *wallet_is_outdated = NULL, bool *daemon_is_outdated = NULL, boost::system::error_code* error = nullptr);
bool check_version(uint32_t *version, bool *wallet_is_outdated, bool *daemon_is_outdated);
bool check_hard_fork_version(cryptonote::network_type nettype, const std::vector<std::pair<uint8_t, uint64_t>> &daemon_hard_forks, const uint64_t height, const uint64_t target_height, bool *wallet_is_outdated, bool *daemon_is_outdated);
void get_transfers(wallet2::transfer_container& incoming_transfers) const;

View File

@ -26,6 +26,7 @@
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <boost/system/error_code.hpp>
#include <boost/utility/string_ref.hpp>
#include "include_base_utils.h"
#include "file_io_utils.h"
@ -42,6 +43,7 @@ public:
bool send(const boost::string_ref buff, std::chrono::milliseconds timeout) { return true; }
bool send(const void* data, size_t sz) { return true; }
bool is_connected() { return true; }
static boost::system::error_code last_error() noexcept { return {}; }
bool recv(std::string& buff, std::chrono::milliseconds timeout)
{
buff = data;

View File

@ -567,7 +567,7 @@ TEST(test_epee_connection, ssl_handshake)
std::this_thread::sleep_for(std::chrono::milliseconds(50));
});
}
EXPECT_EQ(
EXPECT_NE(
ssl_options.handshake(
*ssl_socket,
ssl_socket_t::server,
@ -575,7 +575,7 @@ TEST(test_epee_connection, ssl_handshake)
{},
std::chrono::milliseconds(0)
),
false
boost::system::error_code{}
);
ssl_socket->next_layer().close();
ssl_socket.reset();