Merge remote-tracking branch 'remotes/bitshares/master'

This commit is contained in:
dnotestein 2015-04-08 09:33:47 -04:00
commit 4ddc8f053b
13 changed files with 493 additions and 160 deletions

View file

@ -61,6 +61,7 @@ namespace fc {
friend bool operator == ( const api& a, const api& b ) { return a._data == b._data && a._vtable == b._vtable; }
friend bool operator != ( const api& a, const api& b ) { return !(a._data == b._data && a._vtable == b._vtable); }
uint64_t get_handle()const { return uint64_t(_data.get()); }
vtable_type& operator*()const { FC_ASSERT( _vtable ); return *_vtable; }
vtable_type* operator->()const { FC_ASSERT( _vtable ); return _vtable.get(); }

View file

@ -31,6 +31,7 @@ namespace fc
static variant from_stream( buffered_istream& in, parse_type ptype = legacy_parser );
static variant from_string( const string& utf8_str, parse_type ptype = legacy_parser );
static variants variants_from_string( const string& utf8_str, parse_type ptype = legacy_parser );
static string to_string( const variant& v );
static string to_pretty_string( const variant& v );

View file

@ -3,6 +3,8 @@
#include <memory>
#include <string>
#include <fc/any.hpp>
#include <fc/network/ip.hpp>
#include <fc/signals.hpp>
namespace fc { namespace http {
namespace detail {
@ -22,6 +24,8 @@ namespace fc { namespace http {
void set_session_data( fc::any d ){ _session_data = std::move(d); }
fc::any& get_session_data() { return _session_data; }
fc::signal<void()> closed;
private:
fc::any _session_data;
std::function<void(const std::string&)> _on_message;
@ -38,6 +42,7 @@ namespace fc { namespace http {
void on_connection( const on_connection_handler& handler);
void listen( uint16_t port );
void listen( const fc::ip::endpoint& ep );
void start_accept();
private:

View file

@ -20,9 +20,20 @@ namespace fc
template<typename Member, class Class, Member (Class::*member)>
void operator()( const char* name )const
{
vo(name,(val.*member));
this->add(vo,name,(val.*member));
}
private:
template<typename M>
void add( mutable_variant_object& vo, const char* name, const optional<M>& v )const
{
if( v.valid() )
vo(name,*v);
}
template<typename M>
void add( mutable_variant_object& vo, const char* name, const M& v )const
{ vo(name,v); }
mutable_variant_object& vo;
const T& val;
};

View file

@ -7,6 +7,7 @@
#include <vector>
#include <functional>
#include <utility>
#include <fc/signals.hpp>
//#include <fc/rpc/json_connection.hpp>
namespace fc {
@ -15,93 +16,154 @@ namespace fc {
typedef uint32_t api_id_type;
namespace detail {
class generic_api
template<typename Signature>
class callback_functor
{
public:
template<typename Api>
generic_api( const Api& a, const std::shared_ptr<fc::api_connection>& c );
typedef typename std::function<Signature>::result_type result_type;
generic_api( const generic_api& cpy ) = delete;
callback_functor( fc::api_connection& con, uint64_t id )
:_callback_id(id),_api_connection(con){}
variant call( const string& name, const variants& args )
{
auto itr = _by_name.find(name);
FC_ASSERT( itr != _by_name.end() );
return call( itr->second, args );
}
variant call( uint32_t method_id, const variants& args )
{
FC_ASSERT( method_id < _methods.size() );
return _methods[method_id](args);
}
template<typename... Args>
result_type operator()( Args... args )const;
private:
friend struct api_visitor;
uint64_t _callback_id;
fc::api_connection& _api_connection;
};
struct api_visitor
{
api_visitor( generic_api& a, const std::shared_ptr<fc::api_connection>& s ):api(a),_api_con(s){ }
template<typename R, typename Arg0, typename ... Args>
std::function<R(Args...)> bind_first_arg( const std::function<R(Arg0,Args...)>& f, Arg0 a0 )
{
return [=]( Args... args ) { return f( a0, args... ); };
}
template<typename R>
R call_generic( const std::function<R()>& f, variants::const_iterator a0, variants::const_iterator e )
{
return f();
}
template<typename R, typename Arg0, typename ... Args>
std::function<R(Args...)> bind_first_arg( const std::function<R(Arg0,Args...)>& f, Arg0 a0 )const
{
return [=]( Args... args ) { return f( a0, args... ); };
}
template<typename R>
R call_generic( const std::function<R()>& f, variants::const_iterator a0, variants::const_iterator e )const
{
return f();
}
template<typename R, typename Arg0, typename ... Args>
R call_generic( const std::function<R(Arg0,Args...)>& f, variants::const_iterator a0, variants::const_iterator e )
{
FC_ASSERT( a0 != e );
return call_generic<R,Args...>( bind_first_arg<R,Arg0,Args...>( f, a0->as< typename std::decay<Arg0>::type >() ), a0+1, e );
}
template<typename R, typename Arg0, typename ... Args>
R call_generic( const std::function<R(Arg0,Args...)>& f, variants::const_iterator a0, variants::const_iterator e )const
{
FC_ASSERT( a0 != e );
return call_generic<R,Args...>( bind_first_arg<R,Arg0,Args...>( f, a0->as< typename std::decay<Arg0>::type >() ), a0+1, e );
}
template<typename R, typename ... Args>
std::function<variant(const fc::variants&)> to_generic( const std::function<R(Args...)>& f )
{
return [=]( const variants& args ) {
return variant( call_generic( f, args.begin(), args.end() ) );
};
}
template<typename Interface, typename Adaptor, typename ... Args>
std::function<variant(const fc::variants&)> to_generic( const std::function<api<Interface,Adaptor>(Args...)>& f )const;
template<typename ... Args>
std::function<variant(const fc::variants&)> to_generic( const std::function<void(Args...)>& f )
{
return [=]( const variants& args ) {
call_generic( f, args.begin(), args.end() );
return variant();
};
}
template<typename Interface, typename Adaptor, typename ... Args>
std::function<variant(const fc::variants&)> to_generic( const std::function<fc::optional<api<Interface,Adaptor>>(Args...)>& f )const;
template<typename R, typename ... Args>
std::function<variant(const fc::variants&)> to_generic( const std::function<R(Args...)>& f )const
{
return [=]( const variants& args ) {
return variant( call_generic( f, args.begin(), args.end() ) );
};
}
template<typename ... Args>
std::function<variant(const fc::variants&)> to_generic( const std::function<void(Args...)>& f )const
{
return [=]( const variants& args ) {
call_generic( f, args.begin(), args.end() );
return variant();
};
}
template<typename Result, typename... Args>
void operator()( const char* name, std::function<Result(Args...)>& memb )const {
api._methods.emplace_back( to_generic( memb ) );
api._by_name[name] = api._methods.size() - 1;
}
generic_api& api;
const std::shared_ptr<fc::api_connection>& _api_con;
};
std::shared_ptr<fc::api_connection> _api_connection;
fc::any _api;
std::map< std::string, uint32_t > _by_name;
std::vector< std::function<variant(const variants&)> > _methods;
}; // class generic_api
} // namespace detail
class generic_api
{
public:
template<typename Api>
generic_api( const Api& a, const std::shared_ptr<fc::api_connection>& c );
generic_api( const generic_api& cpy ) = delete;
variant call( const string& name, const variants& args )
{
auto itr = _by_name.find(name);
FC_ASSERT( itr != _by_name.end(), "no method with name '${name}'", ("name",name)("api",_by_name) );
return call( itr->second, args );
}
variant call( uint32_t method_id, const variants& args )
{
FC_ASSERT( method_id < _methods.size() );
return _methods[method_id](args);
}
fc::api_connection& get_connection(){ return *_api_connection; }
private:
friend struct api_visitor;
template<typename R, typename Arg0, typename ... Args>
std::function<R(Args...)> bind_first_arg( const std::function<R(Arg0,Args...)>& f, Arg0 a0 )const
{
return [=]( Args... args ) { return f( a0, args... ); };
}
template<typename R>
R call_generic( const std::function<R()>& f, variants::const_iterator a0, variants::const_iterator e )const
{
return f();
}
template<typename R, typename Signature, typename ... Args>
R call_generic( const std::function<R(std::function<Signature>,Args...)>& f, variants::const_iterator a0, variants::const_iterator e )
{
FC_ASSERT( a0 != e );
detail::callback_functor<Signature> arg0( *this, a0->as<uint64_t>() );
return call_generic<R,Args...>( this->bind_first_arg<R,std::function<Signature>,Args...>( f, std::function<Signature>(arg0) ), a0+1, e );
}
template<typename R, typename Signature, typename ... Args>
R call_generic( const std::function<R(const std::function<Signature>&,Args...)>& f, variants::const_iterator a0, variants::const_iterator e )
{
FC_ASSERT( a0 != e );
detail::callback_functor<Signature> arg0( get_connection(), a0->as<uint64_t>() );
return call_generic<R,Args...>( this->bind_first_arg<R,const std::function<Signature>&,Args...>( f, arg0 ), a0+1, e );
}
template<typename R, typename Arg0, typename ... Args>
R call_generic( const std::function<R(Arg0,Args...)>& f, variants::const_iterator a0, variants::const_iterator e )
{
FC_ASSERT( a0 != e );
return call_generic<R,Args...>( this->bind_first_arg<R,Arg0,Args...>( f, a0->as< typename std::decay<Arg0>::type >() ), a0+1, e );
}
struct api_visitor
{
api_visitor( generic_api& a, const std::shared_ptr<fc::api_connection>& s ):api(a),_api_con(s){ }
template<typename Interface, typename Adaptor, typename ... Args>
std::function<variant(const fc::variants&)> to_generic( const std::function<api<Interface,Adaptor>(Args...)>& f )const;
template<typename Interface, typename Adaptor, typename ... Args>
std::function<variant(const fc::variants&)> to_generic( const std::function<fc::optional<api<Interface,Adaptor>>(Args...)>& f )const;
template<typename R, typename ... Args>
std::function<variant(const fc::variants&)> to_generic( const std::function<R(Args...)>& f )const;
template<typename ... Args>
std::function<variant(const fc::variants&)> to_generic( const std::function<void(Args...)>& f )const;
template<typename Result, typename... Args>
void operator()( const char* name, std::function<Result(Args...)>& memb )const {
api._methods.emplace_back( to_generic( memb ) );
api._by_name[name] = api._methods.size() - 1;
}
generic_api& api;
const std::shared_ptr<fc::api_connection>& _api_con;
};
std::shared_ptr<fc::api_connection> _api_connection;
fc::any _api;
std::map< std::string, uint32_t > _by_name;
std::vector< std::function<variant(const variants&)> > _methods;
}; // class generic_api
class api_connection : public std::enable_shared_from_this<fc::api_connection>
@ -120,24 +182,50 @@ namespace fc {
}
/** makes calls to the remote server */
virtual variant send_call( api_id_type api_id, const string& method_name, const variants& args = variants() ) = 0;
virtual variant send_call( api_id_type api_id, string method_name, variants args = variants() ) = 0;
virtual variant send_callback( uint64_t callback_id, variants args = variants() ) = 0;
virtual void send_notice( uint64_t callback_id, variants args = variants() ) = 0;
variant receive_call( api_id_type api_id, const string& method_name, const variants& args = variants() )const
{
//wdump( (api_id)(method_name)(args) );
FC_ASSERT( _local_apis.size() > api_id );
return _local_apis[api_id]->call( method_name, args );
}
variant receive_callback( uint64_t callback_id, const variants& args = variants() )const
{
FC_ASSERT( _local_callbacks.size() > callback_id );
return _local_callbacks[callback_id]( args );
}
void receive_notice( uint64_t callback_id, const variants& args = variants() )const
{
FC_ASSERT( _local_callbacks.size() > callback_id );
_local_callbacks[callback_id]( args );
}
template<typename Interface>
api_id_type register_api( const Interface& a )
{
_local_apis.push_back( std::unique_ptr<detail::generic_api>( new detail::generic_api(a, shared_from_this() ) ) );
auto handle = a.get_handle();
auto itr = _handle_to_id.find(handle);
if( itr != _handle_to_id.end() ) return itr->second;
_local_apis.push_back( std::unique_ptr<generic_api>( new generic_api(a, shared_from_this() ) ) );
_handle_to_id[handle] = _local_apis.size() - 1;
return _local_apis.size() - 1;
}
template<typename Signature>
uint64_t register_callback( const std::function<Signature>& cb )
{
_local_callbacks.push_back( detail::to_generic( cb ) );
return _local_callbacks.size() - 1;
}
fc::signal<void()> closed;
private:
std::vector< std::unique_ptr<detail::generic_api> > _local_apis;
std::vector< std::unique_ptr<generic_api> > _local_apis;
std::map< uint64_t, api_id_type > _handle_to_id;
std::vector< std::function<variant(const variants&)> > _local_callbacks;
struct api_visitor
@ -160,13 +248,24 @@ namespace fc {
template<typename ResultInterface>
static fc::api<ResultInterface> from_variant( const variant& v,
fc::api<ResultInterface>* /*used for template deduction*/,
const std::shared_ptr<fc::api_connection>& con
)
fc::api<ResultInterface>* /*used for template deduction*/,
const std::shared_ptr<fc::api_connection>& con
)
{
return con->get_remote_api<ResultInterface>( v.as_uint64() );
}
template<typename T>
static fc::variant convert_callbacks( const std::shared_ptr<fc::api_connection>&, const T& v )
{
return fc::variant(v);
}
template<typename Signature>
static fc::variant convert_callbacks( const std::shared_ptr<fc::api_connection>& con, const std::function<Signature>& v )
{
return con->register_callback( v );
}
template<typename Result, typename... Args>
void operator()( const char* name, std::function<Result(Args...)>& memb )const
@ -174,10 +273,19 @@ namespace fc {
auto con = _connection;
auto api_id = _api_id;
memb = [con,api_id,name]( Args... args ) {
auto var_result = con->send_call( api_id, name, {args...} );
auto var_result = con->send_call( api_id, name, { convert_callbacks(con,args)...} );
return from_variant( var_result, (Result*)nullptr, con );
};
}
template<typename... Args>
void operator()( const char* name, std::function<void(Args...)>& memb )const
{
auto con = _connection;
auto api_id = _api_id;
memb = [con,api_id,name]( Args... args ) {
con->send_call( api_id, name, { convert_callbacks(con,args)...} );
};
}
};
};
@ -185,11 +293,22 @@ namespace fc {
{
public:
/** makes calls to the remote server */
virtual variant send_call( api_id_type api_id, const string& method_name, const variants& args = variants() ) override
virtual variant send_call( api_id_type api_id, string method_name, variants args = variants() ) override
{
FC_ASSERT( _remote_connection );
return _remote_connection->receive_call( api_id, method_name, args );
return _remote_connection->receive_call( api_id, method_name, std::move(args) );
}
virtual variant send_callback( uint64_t callback_id, variants args = variants() ) override
{
FC_ASSERT( _remote_connection );
return _remote_connection->receive_callback( callback_id, args );
}
virtual void send_notice( uint64_t callback_id, variants args = variants() ) override
{
FC_ASSERT( _remote_connection );
_remote_connection->receive_notice( callback_id, args );
}
void set_remote_connection( const std::shared_ptr<fc::api_connection>& rc )
{
@ -203,67 +322,82 @@ namespace fc {
};
template<typename Api>
detail::generic_api::generic_api( const Api& a, const std::shared_ptr<fc::api_connection>& c )
generic_api::generic_api( const Api& a, const std::shared_ptr<fc::api_connection>& c )
:_api_connection(c),_api(a)
{
boost::any_cast<const Api&>(a)->visit( api_visitor( *this, _api_connection ) );
}
template<typename Interface, typename Adaptor, typename ... Args>
std::function<variant(const fc::variants&)> detail::generic_api::api_visitor::to_generic(
std::function<variant(const fc::variants&)> generic_api::api_visitor::to_generic(
const std::function<fc::api<Interface,Adaptor>(Args...)>& f )const
{
auto api_con = _api_con;
auto gapi = &api;
return [=]( const variants& args ) {
auto api_result = call_generic( f, args.begin(), args.end() );
auto api_result = gapi->call_generic( f, args.begin(), args.end() );
return api_con->register_api( api_result );
};
}
template<typename Interface, typename Adaptor, typename ... Args>
std::function<variant(const fc::variants&)> detail::generic_api::api_visitor::to_generic(
std::function<variant(const fc::variants&)> generic_api::api_visitor::to_generic(
const std::function<fc::optional<fc::api<Interface,Adaptor>>(Args...)>& f )const
{
auto api_con = _api_con;
auto gapi = &api;
return [=]( const variants& args )-> fc::variant {
auto api_result = call_generic( f, args.begin(), args.end() );
auto api_result = gapi->call_generic( f, args.begin(), args.end() );
if( api_result )
return api_con->register_api( *api_result );
return variant();
};
}
/*
class json_api_connection : public api_connection
template<typename R, typename ... Args>
std::function<variant(const fc::variants&)> generic_api::api_visitor::to_generic( const std::function<R(Args...)>& f )const
{
public:
json_api_connection(){};
void set_json_connection( const std::shared_ptr<rpc::json_connection>& json_con )
{
json_con->add_method( "call", [this]( const variants& args ) -> variant {
FC_ASSERT( args.size() == 3 && args[2].is_array() );
return this->receive_call( args[0].as_uint64(),
args[1].as_string(),
args[2].get_array() );
generic_api* gapi = &api;
return [f,gapi]( const variants& args ) {
return variant( gapi->call_generic( f, args.begin(), args.end() ) );
};
}
});
}
template<typename ... Args>
std::function<variant(const fc::variants&)> generic_api::api_visitor::to_generic( const std::function<void(Args...)>& f )const
{
generic_api* gapi = &api;
return [f,gapi]( const variants& args ) {
gapi->call_generic( f, args.begin(), args.end() );
return variant();
};
}
json_api_connection( const std::shared_ptr<rpc::json_connection>& json_con )
{
set_json_connection( json_con );
}
namespace detail {
template<typename Signature>
template<typename... Args>
typename callback_functor<Signature>::result_type callback_functor<Signature>::operator()( Args... args )const
{
_api_connection.send_callback( _callback_id, fc::variants{ args... } ).template as< result_type >();
}
virtual variant send_call( api_id_type api_id,
const string& method_name,
const variants& args = variants() )const override
{
return _json_con->async_call( "call", {api_id, method_name, args} ).wait();
}
private:
std::shared_ptr<rpc::json_connection> _json_con;
template<typename... Args>
class callback_functor<void(Args...)>
{
public:
typedef void result_type;
};
*/
callback_functor( fc::api_connection& con, uint64_t id )
:_callback_id(id),_api_connection(con){}
void operator()( Args... args )const
{
_api_connection.send_notice( _callback_id, fc::variants{ args... } );
}
private:
uint64_t _callback_id;
fc::api_connection& _api_connection;
};
} // namespace detail
} // fc

85
include/fc/rpc/cli.hpp Normal file
View file

@ -0,0 +1,85 @@
#pragma once
#include <fc/io/stdio.hpp>
#include <fc/io/json.hpp>
#include <fc/io/buffered_iostream.hpp>
#include <fc/io/sstream.hpp>
#include <fc/thread/thread.hpp>
namespace fc { namespace rpc {
/**
* Provides a simple wrapper for RPC calls to a given interface.
*/
class cli : public api_connection
{
public:
~cli()
{
if( _run_complete.valid() )
{
stop();
}
}
virtual variant send_call( api_id_type api_id, string method_name, variants args = variants() )
{
FC_ASSERT(false);
}
virtual variant send_callback( uint64_t callback_id, variants args = variants() )
{
FC_ASSERT(false);
}
virtual void send_notice( uint64_t callback_id, variants args = variants() )
{
FC_ASSERT(false);
}
void start()
{
_run_complete = fc::async( [&](){ run(); } );
}
void stop()
{
_run_complete.cancel();
_run_complete.wait();
}
void wait(){ _run_complete.wait(); }
void format_result( const string& method, std::function<string(variant,const variants&)> formatter)
{
_result_formatters[method] = formatter;
}
private:
void run()
{
while( !fc::cin.eof() && !_run_complete.canceled() )
{
try {
std::cout << ">>> ";
std::cout.flush();
std::string line;
fc::getline( fc::cin, line );
std::cout << line << "\n";
line += char(EOF);
fc::variants args = fc::json::variants_from_string(line);;
if( args.size() == 0 ) continue;
const string& method = args[0].get_string();
auto result = receive_call( 0, method, variants( args.begin()+1,args.end() ) );
auto itr = _result_formatters.find( method );
if( itr == _result_formatters.end() )
{
std::cout << fc::json::to_pretty_string( result ) << "\n";
}
else
std::cout << itr->second( result, args ) << "\n";
}
catch ( const fc::exception& e )
{
std::cout << e.to_detail_string() << "\n";
}
}
}
std::map<string,std::function<string(variant,const variants&)> > _result_formatters;
fc::future<void> _run_complete;
};
} }

View file

@ -45,10 +45,13 @@ namespace fc { namespace rpc {
void close();
void on_unhandled( const std::function<variant(const string&,const variants&)>& unhandled );
private:
uint64_t _next_id = 1;
std::unordered_map<uint64_t, fc::promise<variant>::ptr> _awaiting;
std::unordered_map<std::string, method> _methods;
std::function<variant(const string&,const variants&)> _unhandled;
};
} } // namespace fc::rpc

View file

@ -10,7 +10,7 @@ namespace fc { namespace rpc {
class websocket_api_connection : public api_connection
{
public:
websocket_api_connection( fc::http::websocket_connection_ptr c )
websocket_api_connection( fc::http::websocket_connection& c )
:_connection(c)
{
_rpc_state.add_method( "call", [this]( const variants& args ) -> variant {
@ -19,49 +19,83 @@ namespace fc { namespace rpc {
args[1].as_string(),
args[2].get_array() );
});
_connection->on_message_handler( [&]( const std::string& msg ){ on_message(msg); } );
_rpc_state.add_method( "notice", [this]( const variants& args ) -> variant {
FC_ASSERT( args.size() == 2 && args[1].is_array() );
this->receive_notice( args[0].as_uint64(), args[1].get_array() );
return variant();
});
_rpc_state.add_method( "callback", [this]( const variants& args ) -> variant {
FC_ASSERT( args.size() == 2 && args[1].is_array() );
this->receive_callback( args[0].as_uint64(), args[1].get_array() );
return variant();
});
_rpc_state.on_unhandled( [&]( const std::string& method_name, const variants& args ){
return this->receive_call( 0, method_name, args );
});
_connection.on_message_handler( [&]( const std::string& msg ){ on_message(msg); } );
_connection.closed.connect( [this](){ closed(); } );
}
virtual variant send_call( api_id_type api_id,
const string& method_name,
const variants& args = variants() ) override
string method_name,
variants args = variants() ) override
{
auto request = _rpc_state.start_remote_call( "call", {api_id, method_name, args} );
_connection->send_message( fc::json::to_string(request) );
auto request = _rpc_state.start_remote_call( "call", {api_id, std::move(method_name), std::move(args) } );
_connection.send_message( fc::json::to_string(request) );
return _rpc_state.wait_for_response( *request.id );
}
virtual variant send_callback( uint64_t callback_id, variants args = variants() ) override
{
auto request = _rpc_state.start_remote_call( "callback", {callback_id, std::move(args) } );
_connection.send_message( fc::json::to_string(request) );
return _rpc_state.wait_for_response( *request.id );
}
virtual void send_notice( uint64_t callback_id, variants args = variants() ) override
{
fc::rpc::request req{ optional<uint64_t>(), "notice", {callback_id, std::move(args)}};
_connection.send_message( fc::json::to_string(req) );
}
protected:
void on_message( const std::string& message )
{
auto var = fc::json::from_string(message);
const auto& var_obj = var.get_object();
if( var_obj.contains( "method" ) )
{
auto call = var.as<fc::rpc::request>();
try {
auto result = _rpc_state.local_call( call.method, call.params );
if( call.id )
{
_connection->send_message( fc::json::to_string( response( *call.id, result ) ) );
}
}
catch ( const fc::exception& e )
{
try {
auto var = fc::json::from_string(message);
const auto& var_obj = var.get_object();
if( var_obj.contains( "method" ) )
{
if( call.id )
auto call = var.as<fc::rpc::request>();
try {
auto result = _rpc_state.local_call( call.method, call.params );
if( call.id )
{
_connection.send_message( fc::json::to_string( response( *call.id, result ) ) );
}
}
catch ( const fc::exception& e )
{
_connection->send_message( fc::json::to_string( response( *call.id, error_object{ 1, e.to_detail_string(), fc::variant(e)} ) ) );
if( call.id )
{
_connection.send_message( fc::json::to_string( response( *call.id, error_object{ 1, e.to_detail_string(), fc::variant(e)} ) ) );
}
}
}
}
else
{
auto reply = var.as<fc::rpc::response>();
_rpc_state.handle_reply( reply );
else
{
auto reply = var.as<fc::rpc::response>();
_rpc_state.handle_reply( reply );
}
} catch ( const fc::exception& e ) {
wdump((e.to_detail_string()));
}
}
fc::http::websocket_connection_ptr _connection;
fc::rpc::state _rpc_state;
fc::http::websocket_connection& _connection;
fc::rpc::state _rpc_state;
};
} } // namespace fc::rpc

View file

@ -41,6 +41,8 @@ namespace fc
void from_variant( const variant& var, blob& vo );
template<typename T> void to_variant( const safe<T>& s, variant& v );
template<typename T> void from_variant( const variant& v, safe<T>& s );
template<typename T> void to_variant( const std::unique_ptr<T>& s, variant& v );
template<typename T> void from_variant( const variant& v, std::unique_ptr<T>& s );
template<typename... T> void to_variant( const static_variant<T...>& s, variant& v );
template<typename... T> void from_variant( const variant& v, static_variant<T...>& s );
@ -502,6 +504,23 @@ namespace fc
from_variant( var, *vo );
}
}
template<typename T>
void to_variant( const std::unique_ptr<T>& var, variant& vo )
{
if( var ) to_variant( *var, vo );
else vo = nullptr;
}
template<typename T>
void from_variant( const variant& var, std::unique_ptr<T>& vo )
{
if( var.is_null() ) vo.reset();
else if( vo ) from_variant( var, *vo );
else {
vo.reset( new T() );
from_variant( var, *vo );
}
}
template<typename T>
@ -510,6 +529,7 @@ namespace fc
template<typename T>
void from_variant( const variant& v, safe<T>& s ) { s.value = v.as_uint64(); }
variant operator + ( const variant& a, const variant& b );
variant operator - ( const variant& a, const variant& b );
variant operator * ( const variant& a, const variant& b );

View file

@ -426,6 +426,7 @@ namespace fc
return token_from_stream( in );
case 0x04: // ^D end of transmission
case EOF:
case 0:
FC_THROW_EXCEPTION( eof_exception, "unexpected end of file" );
default:
FC_THROW_EXCEPTION( parse_error_exception, "Unexpected char '${c}' in \"${s}\"",
@ -454,6 +455,20 @@ namespace fc
}
} FC_RETHROW_EXCEPTIONS( warn, "", ("str",utf8_str) ) }
variants json::variants_from_string( const std::string& utf8_str, parse_type ptype )
{ try {
variants result;
fc::stringstream in( utf8_str );
//in.exceptions( std::ifstream::eofbit );
try {
while( true )
{
// result.push_back( variant_from_stream( in ));
result.push_back(json_relaxed::variant_from_stream<fc::stringstream, false>( in ));
}
} catch ( const fc::eof_exception& ){}
return result;
} FC_RETHROW_EXCEPTIONS( warn, "", ("str",utf8_str) ) }
/*
void toUTF8( const char str, ostream& os )
{

View file

@ -73,7 +73,8 @@ namespace fc { namespace http {
virtual void send_message( const std::string& message )override
{
_ws_connection->send( message );
auto ec = _ws_connection->send( message );
FC_ASSERT( !ec, "websocket send failed: ${msg}", ("msg",ec.message() ) );
}
virtual void close( int64_t code, const std::string& reason )override
{
@ -158,13 +159,15 @@ namespace fc { namespace http {
}).wait();
});
_client.set_close_handler( [=]( connection_hdl hdl ){
_client_thread.async( [&](){ _connection.reset(); } ).wait();
if( _connection )
_client_thread.async( [&](){ if( _connection ) _connection->closed(); _connection.reset(); } ).wait();
if( _closed ) _closed->set_value();
});
_client.set_fail_handler( [=]( connection_hdl hdl ){
auto con = _client.get_con_from_hdl(hdl);
auto message = con->get_ec().message();
_client_thread.async( [&](){ _connection.reset(); } ).wait();
if( _connection )
_client_thread.async( [&](){ if( _connection ) _connection->closed(); _connection.reset(); } ).wait();
if( _connected && !_connected->ready() )
_connected->set_exception( exception_ptr( new FC_EXCEPTION( exception, "${message}", ("message",message)) ) );
if( _closed )
@ -200,6 +203,10 @@ namespace fc { namespace http {
{
my->_server.listen(port);
}
void websocket_server::listen( const fc::ip::endpoint& ep )
{
my->_server.listen( boost::asio::ip::tcp::endpoint( boost::asio::ip::address_v4(uint32_t(ep.get_address())),ep.port()) );
}
void websocket_server::start_accept() {
my->_server.start_accept();

View file

@ -21,6 +21,8 @@ void state::remove_method( const fc::string& name )
variant state::local_call( const string& method_name, const variants& args )
{
auto method_itr = _methods.find(method_name);
if( method_itr == _methods.end() && _unhandled )
return _unhandled( method_name, args );
FC_ASSERT( method_itr != _methods.end(), "Unknown Method: ${name}", ("name",method_name) );
return method_itr->second(args);
}
@ -31,11 +33,12 @@ void state::handle_reply( const response& response )
FC_ASSERT( await != _awaiting.end(), "Unknown Response ID: ${id}", ("id",response.id)("response",response) );
if( response.result )
await->second->set_value( *response.result );
else
else if( response.error )
{
FC_ASSERT( response.error );
await->second->set_exception( exception_ptr(new FC_EXCEPTION( exception, "${error}", ("error",*response.error) ) ) );
await->second->set_exception( exception_ptr(new FC_EXCEPTION( exception, "${error}", ("error",response.error->message)("data",response) ) ) );
}
else
await->second->set_value( fc::variant() );
_awaiting.erase(await);
}
@ -57,5 +60,9 @@ void state::close()
item.second->set_exception( fc::exception_ptr(new FC_EXCEPTION( eof_exception, "connection closed" )) );
_awaiting.clear();
}
void state::on_unhandled( const std::function<variant(const string&, const variants&)>& unhandled )
{
_unhandled = unhandled;
}
} } // namespace fc::rpc

View file

@ -8,9 +8,10 @@ class calculator
public:
int32_t add( int32_t a, int32_t b ); // not implemented
int32_t sub( int32_t a, int32_t b ); // not implemented
void on_result( const std::function<void(int32_t)>& cb );
};
FC_API( calculator, (add)(sub) )
FC_API( calculator, (add)(sub)(on_result) )
class login_api
@ -22,7 +23,7 @@ class login_api
return *calc;
}
fc::optional<fc::api<calculator>> calc;
std::set<std::string> test( const std::string&, const std::string& ){};
std::set<std::string> test( const std::string&, const std::string& ) { return std::set<std::string>(); }
};
FC_API( login_api, (get_calc)(test) );
@ -31,14 +32,18 @@ using namespace fc;
class some_calculator
{
public:
int32_t add( int32_t a, int32_t b ) { return a+b; }
int32_t sub( int32_t a, int32_t b ) { return a-b; }
int32_t add( int32_t a, int32_t b ) { wlog("."); if( _cb ) _cb(a+b); return a+b; }
int32_t sub( int32_t a, int32_t b ) { wlog(".");if( _cb ) _cb(a-b); return a-b; }
void on_result( const std::function<void(int32_t)>& cb ) { wlog( "set callback" ); _cb = cb; return ; }
std::function<void(int32_t)> _cb;
};
class variant_calculator
{
public:
double add( fc::variant a, fc::variant b ) { return a.as_double()+b.as_double(); }
double sub( fc::variant a, fc::variant b ) { return a.as_double()-b.as_double(); }
void on_result( const std::function<void(int32_t)>& cb ) { wlog("set callback"); _cb = cb; return ; }
std::function<void(int32_t)> _cb;
};
using namespace fc::http;
@ -46,12 +51,12 @@ using namespace fc::rpc;
int main( int argc, char** argv )
{
{
try {
fc::api<calculator> calc_api( std::make_shared<some_calculator>() );
fc::http::websocket_server server;
server.on_connection([&]( const websocket_connection_ptr& c ){
auto wsc = std::make_shared<websocket_api_connection>(c);
auto wsc = std::make_shared<websocket_api_connection>(*c);
auto login = std::make_shared<login_api>();
login->calc = calc_api;
wsc->register_api(fc::api<login_api>(login));
@ -66,9 +71,10 @@ int main( int argc, char** argv )
try {
fc::http::websocket_client client;
auto con = client.connect( "ws://localhost:8090" );
auto apic = std::make_shared<websocket_api_connection>(con);
auto apic = std::make_shared<websocket_api_connection>(*con);
auto remote_login_api = apic->get_remote_api<login_api>();
auto remote_calc = remote_login_api->get_calc();
remote_calc->on_result( []( uint32_t r ) { elog( "callback result ${r}", ("r",r) ); } );
wdump((remote_calc->add( 4, 5 )));
} catch ( const fc::exception& e )
{
@ -76,6 +82,10 @@ int main( int argc, char** argv )
}
}
wlog( "exit scope" );
}
catch( const fc::exception& e )
{
edump((e.to_detail_string()));
}
wlog( "returning now..." );