Merge commit '1f41494'
This commit is contained in:
commit
8eb2ccc180
10 changed files with 30 additions and 686 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -48,5 +48,4 @@ fc_automoc.cpp
|
|||
git_revision.cpp
|
||||
GitSHA3.cpp
|
||||
|
||||
ntp_test
|
||||
task_cancel_test
|
||||
|
|
|
|||
|
|
@ -237,12 +237,10 @@ set( fc_sources
|
|||
src/network/http/http_connection.cpp
|
||||
src/network/http/http_server.cpp
|
||||
src/network/http/websocket.cpp
|
||||
src/network/ntp.cpp
|
||||
src/network/ip.cpp
|
||||
src/network/rate_limiting.cpp
|
||||
src/network/resolve.cpp
|
||||
src/network/url.cpp
|
||||
src/network/gntp.cpp
|
||||
src/compress/smaz.cpp
|
||||
src/compress/zlib.cpp
|
||||
vendor/cyoencode-1.0.2/src/CyoDecode.c
|
||||
|
|
@ -350,6 +348,20 @@ SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWEBSOCKETPP_STRICT_MASKING")
|
|||
|
||||
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DBOOST_ASIO_HAS_STD_CHRONO")
|
||||
|
||||
OPTION( LOG_LONG_API "Log long API calls over websocket (ON OR OFF)" ON )
|
||||
MESSAGE( STATUS "LOG_LONG_API: ${LOG_LONG_API}" )
|
||||
if( LOG_LONG_API )
|
||||
SET( LOG_LONG_API_MAX_MS 1000 CACHE STRING "Max API execution time in ms" )
|
||||
SET( LOG_LONG_API_WARN_MS 750 CACHE STRING "API execution time in ms at which to warn" )
|
||||
MESSAGE( STATUS " " )
|
||||
MESSAGE( STATUS " LOGGING LONG API CALLS" )
|
||||
MESSAGE( STATUS " MAX MS: ${LOG_LONG_API_MAX_MS}" )
|
||||
MESSAGE( STATUS " WARN MS: ${LOG_LONG_API_WARN_MS}" )
|
||||
MESSAGE( STATUS " " )
|
||||
SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DLOG_LONG_API -DLOG_LONG_API_MAX_MS=${LOG_LONG_API_MAX_MS} -DLOG_LONG_API_WARN_MS=${LOG_LONG_API_WARN_MS}" )
|
||||
SET( CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DLOG_LONG_API -DLOG_LONG_API_MAX_MS=${LOG_LONG_API_MAX_MS} -DLOG_LONG_API_WARN_MS=${LOG_LONG_API_WARN_MS}" )
|
||||
endif()
|
||||
|
||||
target_include_directories(fc
|
||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
${Boost_INCLUDE_DIR}
|
||||
|
|
|
|||
|
|
@ -218,8 +218,8 @@ namespace fc {
|
|||
|
||||
struct range_proof_info
|
||||
{
|
||||
int exp;
|
||||
int mantissa;
|
||||
int64_t exp;
|
||||
int64_t mantissa;
|
||||
uint64_t min_value;
|
||||
uint64_t max_value;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,57 +0,0 @@
|
|||
#pragma once
|
||||
#include <fc/crypto/ripemd160.hpp>
|
||||
#include <fc/crypto/rand.hpp>
|
||||
#include <fc/optional.hpp>
|
||||
#include <fc/network/ip.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace fc
|
||||
{
|
||||
namespace detail {
|
||||
class gntp_icon_impl;
|
||||
}
|
||||
class gntp_notifier;
|
||||
|
||||
class gntp_icon {
|
||||
public:
|
||||
gntp_icon(const char* buffer, size_t length);
|
||||
~gntp_icon();
|
||||
private:
|
||||
std::unique_ptr<detail::gntp_icon_impl> my;
|
||||
friend class gntp_notifier;
|
||||
};
|
||||
typedef std::shared_ptr<gntp_icon> gntp_icon_ptr;
|
||||
|
||||
class gntp_notification_type {
|
||||
public:
|
||||
std::string name;
|
||||
std::string display_name;
|
||||
bool enabled;
|
||||
gntp_icon_ptr icon;
|
||||
};
|
||||
typedef std::vector<gntp_notification_type> gntp_notification_type_list;
|
||||
|
||||
namespace detail {
|
||||
class gntp_notifier_impl;
|
||||
}
|
||||
|
||||
typedef uint160_t gntp_guid;
|
||||
|
||||
class gntp_notifier {
|
||||
public:
|
||||
gntp_notifier(const std::string& host_to_notify = "127.0.0.1", uint16_t port = 23053,
|
||||
const optional<std::string>& password = optional<std::string>());
|
||||
~gntp_notifier();
|
||||
void set_application_name(std::string application_name);
|
||||
void set_application_icon(const gntp_icon_ptr& icon);
|
||||
void register_notifications();
|
||||
gntp_guid send_notification(std::string name, std::string title, std::string text, const gntp_icon_ptr& icon = gntp_icon_ptr(), optional<gntp_guid> coalescingId = optional<gntp_guid>());
|
||||
void add_notification_type(const gntp_notification_type& notificationType);
|
||||
private:
|
||||
std::unique_ptr<detail::gntp_notifier_impl> my;
|
||||
};
|
||||
|
||||
|
||||
} // namespace fc
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <fc/time.hpp>
|
||||
#include <fc/optional.hpp>
|
||||
|
||||
|
||||
namespace fc {
|
||||
|
||||
namespace detail { class ntp_impl; }
|
||||
|
||||
class ntp
|
||||
{
|
||||
public:
|
||||
ntp();
|
||||
~ntp();
|
||||
|
||||
void add_server( const std::string& hostname, uint16_t port = 123 );
|
||||
void set_request_interval( uint32_t interval_sec );
|
||||
void request_now();
|
||||
optional<time_point> get_time()const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<detail::ntp_impl> my;
|
||||
};
|
||||
|
||||
} // namespace fc
|
||||
|
|
@ -1,291 +0,0 @@
|
|||
#include <fc/network/gntp.hpp>
|
||||
#include <fc/exception/exception.hpp>
|
||||
|
||||
#include <fc/log/logger.hpp>
|
||||
#include <fc/asio.hpp>
|
||||
#include <fc/network/tcp_socket.hpp>
|
||||
#include <fc/crypto/sha1.hpp>
|
||||
#include <fc/crypto/base32.hpp>
|
||||
#include <fc/crypto/sha256.hpp>
|
||||
#include <fc/crypto/rand.hpp>
|
||||
#include <fc/crypto/hex.hpp>
|
||||
|
||||
#include <set>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/algorithm/string/case_conv.hpp>
|
||||
|
||||
namespace fc
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
static std::string calc_sha1_base32_of_buffer(const std::string& buffer)
|
||||
{
|
||||
sha1::encoder sha1_encoder;
|
||||
sha1_encoder.write(buffer.c_str(), buffer.size());
|
||||
sha1 sha1_result = sha1_encoder.result();
|
||||
string sha1_result_base32 = to_base32((char*)&sha1_result, sizeof(sha1_result));
|
||||
return sha1_result_base32.c_str();
|
||||
}
|
||||
|
||||
|
||||
class gntp_icon_impl
|
||||
{
|
||||
public:
|
||||
std::string _icon_bytes;
|
||||
std::string _sha1_hash;
|
||||
|
||||
gntp_icon_impl(const char* buffer, size_t length) :
|
||||
_icon_bytes(buffer, length),
|
||||
_sha1_hash(calc_sha1_base32_of_buffer(_icon_bytes))
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class gntp_notifier_impl
|
||||
{
|
||||
public:
|
||||
gntp_notifier_impl(const std::string& host_to_notify = "127.0.0.1", uint16_t port = 23053, const optional<std::string>& password = optional<std::string>());
|
||||
|
||||
// there's no API to change these right now, it will always notify localhost at the default GNTP port
|
||||
std::string hostname;
|
||||
uint16_t port;
|
||||
optional<std::string> password;
|
||||
|
||||
std::string application_name;
|
||||
gntp_icon_ptr application_icon;
|
||||
|
||||
gntp_notification_type_list notification_types; // list of all notification types we're registered to send
|
||||
|
||||
optional<boost::asio::ip::tcp::endpoint> endpoint; // cache the last endpoint we've connected to
|
||||
|
||||
bool connection_failed; // true after we've tried to connect and failed
|
||||
bool is_registered; // true after we've registered
|
||||
|
||||
void send_gntp_message(const std::string& message);
|
||||
};
|
||||
|
||||
gntp_notifier_impl::gntp_notifier_impl(const std::string& host_to_notify /* = "127.0.0.1" */, uint16_t port /* = 23053 */,
|
||||
const optional<std::string>& password /* = optional<std::string>() */) :
|
||||
hostname(host_to_notify),
|
||||
port(port),
|
||||
password(password),
|
||||
connection_failed(false),
|
||||
is_registered(false)
|
||||
{
|
||||
}
|
||||
|
||||
void gntp_notifier_impl::send_gntp_message(const std::string& message)
|
||||
{
|
||||
std::shared_ptr<boost::asio::ip::tcp::socket> sock(new boost::asio::ip::tcp::socket(asio::default_io_service()));
|
||||
|
||||
bool connected = false;
|
||||
if (endpoint)
|
||||
{
|
||||
// we've successfully connected before, connect to the same endpoint that worked last time
|
||||
try
|
||||
{
|
||||
asio::tcp::connect(*sock, *endpoint);
|
||||
connected = true;
|
||||
}
|
||||
catch (exception& er)
|
||||
{
|
||||
ilog("Failed to connect to GNTP service using an endpoint that previously worked: ${error_report}",
|
||||
("error_report", er.to_detail_string()));
|
||||
sock->close();
|
||||
// clear the cached endpoint and fall through to the full connection procedure
|
||||
endpoint = optional<boost::asio::ip::tcp::endpoint>();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
ilog("Failed to connect to GNTP service using an endpoint that previously worked");
|
||||
sock->close();
|
||||
// clear the cached endpoint and fall through to the full connection procedure
|
||||
endpoint = optional<boost::asio::ip::tcp::endpoint>();
|
||||
}
|
||||
}
|
||||
if (!connected)
|
||||
{
|
||||
// do the full connection procedure
|
||||
auto eps = asio::tcp::resolve(hostname, boost::lexical_cast<std::string>(port));
|
||||
if (eps.size() == 0)
|
||||
FC_THROW("Unable to resolve host '${host}'", ("host", hostname));
|
||||
|
||||
for (uint32_t i = 0; i < eps.size(); ++i)
|
||||
{
|
||||
try
|
||||
{
|
||||
boost::system::error_code ec;
|
||||
ilog("Attempting to connect to GNTP srvice");
|
||||
asio::tcp::connect(*sock, eps[i]);
|
||||
endpoint = eps[i];
|
||||
connected = true;
|
||||
break;
|
||||
}
|
||||
catch (const exception& er)
|
||||
{
|
||||
ilog("Failed to connect to GNTP service: ${error_reprot}",
|
||||
("error_report", er.to_detail_string()) );
|
||||
sock->close();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
ilog("Failed to connect to GNTP service");
|
||||
sock->close();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!connected)
|
||||
FC_THROW("Unable to connect to any resolved endpoint for ${host}:${port}",
|
||||
("host", hostname)("port", port));
|
||||
try
|
||||
{
|
||||
asio::ostream<boost::asio::ip::tcp::socket> write_stream(sock);
|
||||
write_stream.write(message.c_str(), message.size());
|
||||
write_stream.flush();
|
||||
write_stream.close();
|
||||
}
|
||||
catch (exception& er)
|
||||
{
|
||||
FC_RETHROW_EXCEPTION(er, warn, "Caught an exception while sending data to GNTP service");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
FC_THROW("Caught an exception while sending data to GNTP service");
|
||||
}
|
||||
}
|
||||
} // end namespace detail
|
||||
|
||||
gntp_icon::gntp_icon(const char* buffer, size_t length) :
|
||||
my(new detail::gntp_icon_impl(buffer, length))
|
||||
{
|
||||
}
|
||||
gntp_icon::~gntp_icon()
|
||||
{
|
||||
}
|
||||
|
||||
gntp_notifier::gntp_notifier(const std::string& host_to_notify /* = "127.0.0.1" */, uint16_t port /* = 23053 */,
|
||||
const optional<std::string>& password /* = optional<std::string>() */) :
|
||||
my(new detail::gntp_notifier_impl(host_to_notify, port, password))
|
||||
{
|
||||
}
|
||||
|
||||
gntp_notifier::~gntp_notifier()
|
||||
{
|
||||
}
|
||||
|
||||
void gntp_notifier::set_application_name(std::string appName)
|
||||
{
|
||||
my->application_name = appName;
|
||||
}
|
||||
void gntp_notifier::set_application_icon(const gntp_icon_ptr& icon)
|
||||
{
|
||||
my->application_icon = icon;
|
||||
}
|
||||
void gntp_notifier::add_notification_type(const gntp_notification_type& notification_type)
|
||||
{
|
||||
my->notification_types.push_back(notification_type);
|
||||
}
|
||||
|
||||
void gntp_notifier::register_notifications()
|
||||
{
|
||||
// this call will reset any errors
|
||||
my->connection_failed = false;
|
||||
my->is_registered = false;
|
||||
|
||||
std::ostringstream message;
|
||||
std::set<gntp_icon_ptr> icons_used;
|
||||
|
||||
message << "GNTP/1.0 REGISTER NONE\r\n";
|
||||
message << "Application-Name: " << my->application_name << "\r\n";
|
||||
if (my->application_icon)
|
||||
{
|
||||
message << "Application-Icon: x-growl-resource://" << my->application_icon->my->_sha1_hash << "\r\n";
|
||||
icons_used.insert(my->application_icon);
|
||||
}
|
||||
|
||||
message << "Notifications-Count: " << my->notification_types.size() << "\r\n";
|
||||
for (const gntp_notification_type& notification_type : my->notification_types)
|
||||
{
|
||||
message << "\r\n";
|
||||
message << "Notification-Name: " << notification_type.name << "\r\n";
|
||||
if (!notification_type.display_name.empty())
|
||||
message << "Notification-Display-Name: " << notification_type.display_name << "\r\n";
|
||||
if (notification_type.icon)
|
||||
{
|
||||
message << "Notification-Icon: x-growl-resource://" << notification_type.icon->my->_sha1_hash << "\r\n";
|
||||
icons_used.insert(notification_type.icon);
|
||||
}
|
||||
message << "Notification-Enabled: " << (notification_type.enabled ? "True" : "False") << "\r\n";
|
||||
}
|
||||
if (!icons_used.empty())
|
||||
{
|
||||
message << "\r\n";
|
||||
for (const gntp_icon_ptr& icon : icons_used)
|
||||
{
|
||||
message << "Identifier: " << icon->my->_sha1_hash << "\r\n";
|
||||
message << "Length: " << icon->my->_icon_bytes.size() << "\r\n";
|
||||
message << "\r\n";
|
||||
message << icon->my->_icon_bytes;
|
||||
message << "\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
message << "\r\n\r\n";
|
||||
try
|
||||
{
|
||||
my->send_gntp_message(message.str());
|
||||
my->is_registered = true;
|
||||
}
|
||||
catch (const exception&)
|
||||
{
|
||||
my->connection_failed = true;
|
||||
}
|
||||
}
|
||||
gntp_guid gntp_notifier::send_notification(std::string name, std::string title, std::string text,
|
||||
const gntp_icon_ptr& icon, optional<gntp_guid> coalescingId /* = optional<gntp_guid>() */)
|
||||
{
|
||||
if (my->connection_failed)
|
||||
return gntp_guid();
|
||||
if (!my->is_registered)
|
||||
return gntp_guid();
|
||||
|
||||
gntp_guid notification_id;
|
||||
rand_pseudo_bytes(notification_id.data(), 20);
|
||||
|
||||
std::ostringstream message;
|
||||
message << "GNTP/1.0 NOTIFY NONE";
|
||||
if (my->password)
|
||||
{
|
||||
char salt[16];
|
||||
rand_pseudo_bytes(salt, sizeof(salt));
|
||||
std::string salted_password = *my->password + std::string(salt, 16);
|
||||
sha256 key = sha256::hash(salted_password);
|
||||
sha256 keyhash = sha256::hash(key.data(), 32);
|
||||
message << " SHA256:" << boost::to_upper_copy(to_hex(keyhash.data(), 32)) << "." << boost::to_upper_copy(to_hex(salt, sizeof(salt)));
|
||||
}
|
||||
message << "\r\n";
|
||||
message << "Application-Name: " << my->application_name << "\r\n";
|
||||
message << "Notification-Name: " << name << "\r\n";
|
||||
message << "Notification-ID: " << notification_id.str() << "\r\n";
|
||||
message << "Notification-Coalescing-ID: " << (coalescingId ? coalescingId->str() : notification_id.str()) << "\r\n";
|
||||
message << "Notification-Title: " << title << "\r\n";
|
||||
message << "Notification-Text: " << text << "\r\n";
|
||||
if (icon)
|
||||
message << "Notification-Icon: x-growl-resource://" << icon->my->_sha1_hash << "\r\n";
|
||||
|
||||
if (icon)
|
||||
{
|
||||
message << "\r\n";
|
||||
message << "Identifier: " << icon->my->_sha1_hash << "\r\n";
|
||||
message << "Length: " << icon->my->_icon_bytes.size() << "\r\n";
|
||||
message << "\r\n";
|
||||
message << icon->my->_icon_bytes;
|
||||
message << "\r\n";
|
||||
}
|
||||
message << "\r\n\r\n";
|
||||
my->send_gntp_message(message.str());
|
||||
return notification_id;
|
||||
}
|
||||
|
||||
} // namespace fc
|
||||
|
|
@ -1,272 +0,0 @@
|
|||
#include <fc/network/ntp.hpp>
|
||||
#include <fc/network/udp_socket.hpp>
|
||||
#include <fc/network/resolve.hpp>
|
||||
#include <fc/network/ip.hpp>
|
||||
#include <fc/thread/thread.hpp>
|
||||
|
||||
#include <stdint.h>
|
||||
#include "../byteswap.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <array>
|
||||
|
||||
namespace fc
|
||||
{
|
||||
namespace detail {
|
||||
|
||||
class ntp_impl
|
||||
{
|
||||
public:
|
||||
/** vector < host, port > */
|
||||
fc::thread _ntp_thread;
|
||||
std::vector< std::pair< std::string, uint16_t> > _ntp_hosts;
|
||||
fc::future<void> _read_loop_done;
|
||||
udp_socket _sock;
|
||||
uint32_t _request_interval_sec;
|
||||
uint32_t _retry_failed_request_interval_sec;
|
||||
fc::time_point _last_valid_ntp_reply_received_time;
|
||||
|
||||
std::atomic_bool _last_ntp_delta_initialized;
|
||||
std::atomic<int64_t> _last_ntp_delta_microseconds;
|
||||
|
||||
|
||||
fc::future<void> _request_time_task_done;
|
||||
|
||||
ntp_impl() :
|
||||
_ntp_thread("ntp"),
|
||||
_request_interval_sec( 60*60 /* 1 hr */),
|
||||
_retry_failed_request_interval_sec(60 * 5),
|
||||
_last_ntp_delta_microseconds(0)
|
||||
{
|
||||
_last_ntp_delta_initialized = false;
|
||||
_ntp_hosts.push_back( std::make_pair( "pool.ntp.org",123 ) );
|
||||
}
|
||||
|
||||
~ntp_impl()
|
||||
{
|
||||
}
|
||||
|
||||
fc::time_point ntp_timestamp_to_fc_time_point(uint64_t ntp_timestamp_net_order)
|
||||
{
|
||||
uint64_t ntp_timestamp_host = bswap_64(ntp_timestamp_net_order);
|
||||
uint32_t fractional_seconds = ntp_timestamp_host & 0xffffffff;
|
||||
uint32_t microseconds = (uint32_t)((((uint64_t)fractional_seconds * 1000000) + (uint64_t(1) << 31)) >> 32);
|
||||
uint32_t seconds_since_1900 = ntp_timestamp_host >> 32;
|
||||
uint32_t seconds_since_epoch = seconds_since_1900 - 2208988800;
|
||||
return fc::time_point() + fc::seconds(seconds_since_epoch) + fc::microseconds(microseconds);
|
||||
}
|
||||
|
||||
uint64_t fc_time_point_to_ntp_timestamp(const fc::time_point& fc_timestamp)
|
||||
{
|
||||
uint64_t microseconds_since_epoch = (uint64_t)fc_timestamp.time_since_epoch().count();
|
||||
uint32_t seconds_since_epoch = (uint32_t)(microseconds_since_epoch / 1000000);
|
||||
uint32_t seconds_since_1900 = seconds_since_epoch + 2208988800;
|
||||
uint32_t microseconds = microseconds_since_epoch % 1000000;
|
||||
uint32_t fractional_seconds = (uint32_t)((((uint64_t)microseconds << 32) + (uint64_t(1) << 31)) / 1000000);
|
||||
uint64_t ntp_timestamp_net_order = ((uint64_t)seconds_since_1900 << 32) + fractional_seconds;
|
||||
return bswap_64(ntp_timestamp_net_order);
|
||||
}
|
||||
|
||||
void request_now()
|
||||
{
|
||||
assert(_ntp_thread.is_current());
|
||||
for( auto item : _ntp_hosts )
|
||||
{
|
||||
try
|
||||
{
|
||||
//wlog( "resolving... ${r}", ("r", item) );
|
||||
auto eps = resolve( item.first, item.second );
|
||||
for( auto ep : eps )
|
||||
{
|
||||
// wlog( "sending request to ${ep}", ("ep",ep) );
|
||||
std::shared_ptr<char> send_buffer(new char[48], [](char* p){ delete[] p; });
|
||||
std::array<unsigned char, 48> packet_to_send { {010,0,0,0,0,0,0,0,0} };
|
||||
memcpy(send_buffer.get(), packet_to_send.data(), packet_to_send.size());
|
||||
uint64_t* send_buf_as_64_array = (uint64_t*)send_buffer.get();
|
||||
send_buf_as_64_array[5] = fc_time_point_to_ntp_timestamp(fc::time_point::now()); // 5 = Transmit Timestamp
|
||||
_sock.send_to(send_buffer, packet_to_send.size(), ep);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (const fc::canceled_exception&)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
// this could fail to resolve but we want to go on to other hosts..
|
||||
catch ( const fc::exception& e )
|
||||
{
|
||||
elog( "${e}", ("e",e.to_detail_string() ) );
|
||||
}
|
||||
}
|
||||
} // request_now
|
||||
|
||||
// started for first time in ntp() constructor, canceled in ~ntp() destructor
|
||||
// this task gets invoked every _retry_failed_request_interval_sec (currently 5 min), and if
|
||||
// _request_interval_sec (currently 1 hour) has passed since the last successful update,
|
||||
// it sends a new request
|
||||
void request_time_task()
|
||||
{
|
||||
assert(_ntp_thread.is_current());
|
||||
if (_last_valid_ntp_reply_received_time <= fc::time_point::now() - fc::seconds(_request_interval_sec - 5))
|
||||
request_now();
|
||||
if (!_request_time_task_done.valid() || !_request_time_task_done.canceled())
|
||||
_request_time_task_done = schedule( [=](){ request_time_task(); },
|
||||
fc::time_point::now() + fc::seconds(_retry_failed_request_interval_sec),
|
||||
"request_time_task" );
|
||||
} // request_loop
|
||||
|
||||
void start_read_loop()
|
||||
{
|
||||
_read_loop_done = _ntp_thread.async( [this](){ read_loop(); }, "ntp_read_loop" );
|
||||
}
|
||||
|
||||
void read_loop()
|
||||
{
|
||||
assert(_ntp_thread.is_current());
|
||||
|
||||
uint32_t receive_buffer_size = sizeof(uint64_t) * 1024;
|
||||
std::shared_ptr<char> receive_buffer(new char[receive_buffer_size], [](char* p){ delete[] p; });
|
||||
uint64_t* recv_buf = (uint64_t*)receive_buffer.get();
|
||||
|
||||
//outer while to restart read-loop if exception is thrown while waiting to receive on socket.
|
||||
while( !_read_loop_done.canceled() )
|
||||
{
|
||||
// if you start the read while loop here, the recieve_from call will throw "invalid argument" on win32,
|
||||
// so instead we start the loop after making our first request
|
||||
try
|
||||
{
|
||||
_sock.open();
|
||||
request_time_task(); //this will re-send a time request
|
||||
|
||||
while( !_read_loop_done.canceled() )
|
||||
{
|
||||
fc::ip::endpoint from;
|
||||
try
|
||||
{
|
||||
_sock.receive_from( receive_buffer, receive_buffer_size, from );
|
||||
// wlog("received ntp reply from ${from}",("from",from) );
|
||||
} FC_RETHROW_EXCEPTIONS(error, "Error reading from NTP socket");
|
||||
|
||||
fc::time_point receive_time = fc::time_point::now();
|
||||
fc::time_point origin_time = ntp_timestamp_to_fc_time_point(recv_buf[3]);
|
||||
fc::time_point server_receive_time = ntp_timestamp_to_fc_time_point(recv_buf[4]);
|
||||
fc::time_point server_transmit_time = ntp_timestamp_to_fc_time_point(recv_buf[5]);
|
||||
|
||||
fc::microseconds offset(((server_receive_time - origin_time) +
|
||||
(server_transmit_time - receive_time)).count() / 2);
|
||||
fc::microseconds round_trip_delay((receive_time - origin_time) -
|
||||
(server_transmit_time - server_receive_time));
|
||||
//wlog("origin_time = ${origin_time}, server_receive_time = ${server_receive_time}, server_transmit_time = ${server_transmit_time}, receive_time = ${receive_time}",
|
||||
// ("origin_time", origin_time)("server_receive_time", server_receive_time)("server_transmit_time", server_transmit_time)("receive_time", receive_time));
|
||||
// wlog("ntp offset: ${offset}, round_trip_delay ${delay}", ("offset", offset)("delay", round_trip_delay));
|
||||
|
||||
//if the reply we just received has occurred more than a second after our last time request (it was more than a second ago since our last request)
|
||||
if( round_trip_delay > fc::microseconds(300000) )
|
||||
{
|
||||
wlog("received stale ntp reply requested at ${request_time}, send a new time request", ("request_time", origin_time));
|
||||
request_now(); //request another reply and ignore this one
|
||||
}
|
||||
else //we think we have a timely reply, process it
|
||||
{
|
||||
if( offset < fc::seconds(60*60*24) && offset > fc::seconds(-60*60*24) )
|
||||
{
|
||||
_last_ntp_delta_microseconds = offset.count();
|
||||
_last_ntp_delta_initialized = true;
|
||||
fc::microseconds ntp_delta_time = fc::microseconds(_last_ntp_delta_microseconds);
|
||||
_last_valid_ntp_reply_received_time = receive_time;
|
||||
wlog("ntp_delta_time updated to ${delta_time} us", ("delta_time",ntp_delta_time) );
|
||||
}
|
||||
else
|
||||
elog( "NTP time and local time vary by more than a day! ntp:${ntp_time} local:${local}",
|
||||
("ntp_time", receive_time + offset)("local", fc::time_point::now()) );
|
||||
}
|
||||
}
|
||||
} // try
|
||||
catch (fc::canceled_exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (const fc::exception& e)
|
||||
{
|
||||
//swallow any other exception and restart loop
|
||||
elog("exception in read_loop, going to restart it. ${e}",("e",e));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
//swallow any other exception and restart loop
|
||||
elog("unknown exception in read_loop, going to restart it.");
|
||||
}
|
||||
_sock.close();
|
||||
fc::usleep(fc::seconds(_retry_failed_request_interval_sec));
|
||||
} //outer while loop
|
||||
wlog("exiting ntp read_loop");
|
||||
} //end read_loop()
|
||||
}; //ntp_impl
|
||||
|
||||
} // namespace detail
|
||||
|
||||
|
||||
|
||||
ntp::ntp()
|
||||
:my( new detail::ntp_impl() )
|
||||
{
|
||||
my->start_read_loop();
|
||||
}
|
||||
|
||||
ntp::~ntp()
|
||||
{
|
||||
my->_ntp_thread.async([=](){
|
||||
try
|
||||
{
|
||||
my->_request_time_task_done.cancel_and_wait("ntp object is destructing");
|
||||
}
|
||||
catch ( const fc::exception& e )
|
||||
{
|
||||
wlog( "Exception thrown while shutting down NTP's request_time_task, ignoring: ${e}", ("e",e) );
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
wlog( "Exception thrown while shutting down NTP's request_time_task, ignoring" );
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
my->_read_loop_done.cancel_and_wait("ntp object is destructing");
|
||||
}
|
||||
catch ( const fc::exception& e )
|
||||
{
|
||||
wlog( "Exception thrown while shutting down NTP's read_loop, ignoring: ${e}", ("e",e) );
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
wlog( "Exception thrown while shutting down NTP's read_loop, ignoring" );
|
||||
}
|
||||
|
||||
}, "ntp_shutdown_task").wait();
|
||||
}
|
||||
|
||||
|
||||
void ntp::add_server( const std::string& hostname, uint16_t port)
|
||||
{
|
||||
my->_ntp_thread.async( [=](){ my->_ntp_hosts.push_back( std::make_pair(hostname,port) ); }, "add_server" ).wait();
|
||||
}
|
||||
|
||||
void ntp::set_request_interval( uint32_t interval_sec )
|
||||
{
|
||||
my->_request_interval_sec = interval_sec;
|
||||
my->_retry_failed_request_interval_sec = std::min(my->_retry_failed_request_interval_sec, interval_sec);
|
||||
}
|
||||
|
||||
void ntp::request_now()
|
||||
{
|
||||
my->_ntp_thread.async( [=](){ my->request_now(); }, "request_now" ).wait();
|
||||
}
|
||||
|
||||
optional<time_point> ntp::get_time()const
|
||||
{
|
||||
if( my->_last_ntp_delta_initialized )
|
||||
return fc::time_point::now() + fc::microseconds(my->_last_ntp_delta_microseconds);
|
||||
return optional<time_point>();
|
||||
}
|
||||
|
||||
} //namespace fc
|
||||
|
|
@ -95,7 +95,21 @@ std::string websocket_api_connection::on_message(
|
|||
{
|
||||
try
|
||||
{
|
||||
#ifdef LOG_LONG_API
|
||||
auto start = time_point::now();
|
||||
#endif
|
||||
|
||||
auto result = _rpc_state.local_call( call.method, call.params );
|
||||
|
||||
#ifdef LOG_LONG_API
|
||||
auto end = time_point::now();
|
||||
|
||||
if( end - start > fc::milliseconds( LOG_LONG_API_MAX_MS ) )
|
||||
elog( "API call execution time limit exceeded. method: ${m} params: ${p} time: ${t}", ("m",call.method)("p",call.params)("t", end - start) );
|
||||
else if( end - start > fc::milliseconds( LOG_LONG_API_WARN_MS ) )
|
||||
wlog( "API call execution time nearing limit. method: ${m} params: ${p} time: ${t}", ("m",call.method)("p",call.params)("t", end - start) );
|
||||
#endif
|
||||
|
||||
if( call.id )
|
||||
{
|
||||
auto reply = fc::json::to_string( response( *call.id, result ) );
|
||||
|
|
|
|||
|
|
@ -10,9 +10,6 @@ if( ECC_IMPL STREQUAL secp256k1 )
|
|||
target_link_libraries( blind fc )
|
||||
endif()
|
||||
|
||||
add_executable( ntp_test all_tests.cpp network/ntp_test.cpp )
|
||||
target_link_libraries( ntp_test fc )
|
||||
|
||||
add_executable( task_cancel_test all_tests.cpp thread/task_cancel.cpp )
|
||||
target_link_libraries( task_cancel_test fc )
|
||||
|
||||
|
|
@ -52,7 +49,6 @@ add_executable( all_tests all_tests.cpp
|
|||
crypto/dh_test.cpp
|
||||
crypto/rand_test.cpp
|
||||
crypto/sha_tests.cpp
|
||||
network/ntp_test.cpp
|
||||
network/http/websocket_test.cpp
|
||||
thread/task_cancel.cpp
|
||||
bloom_test.cpp
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <fc/network/ntp.hpp>
|
||||
#include <fc/log/logger.hpp>
|
||||
#include <fc/thread/thread.hpp>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(fc_network)
|
||||
|
||||
BOOST_AUTO_TEST_CASE( ntp_test )
|
||||
{
|
||||
ilog("start ntp test");
|
||||
fc::usleep( fc::seconds(1) );
|
||||
ilog("done ntp test");
|
||||
/*
|
||||
fc::ntp ntp_service;
|
||||
ntp_service.set_request_interval(5);
|
||||
fc::usleep(fc::seconds(4) );
|
||||
auto time = ntp_service.get_time();
|
||||
BOOST_CHECK( time );
|
||||
auto ntp_time = *time;
|
||||
auto delta = ntp_time - fc::time_point::now();
|
||||
// auto minutes = delta.count() / 1000000 / 60;
|
||||
// auto hours = delta.count() / 1000000 / 60 / 60;
|
||||
// auto seconds = delta.count() / 1000000;
|
||||
auto msec= delta.count() / 1000;
|
||||
BOOST_CHECK( msec < 100 );
|
||||
*/
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
Loading…
Reference in a new issue