Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
sysio::http_plugin Class Reference

#include <http_plugin.hpp>

Inheritance diagram for sysio::http_plugin:
Collaboration diagram for sysio::http_plugin:

Classes

struct  get_supported_apis_result
 

Public Member Functions

 http_plugin ()
 
virtual ~http_plugin ()
 
virtual void set_program_options (options_description &, options_description &cfg) override
 
void plugin_initialize (const variables_map &options)
 
void plugin_startup ()
 
void plugin_shutdown ()
 
void handle_sighup () override
 
void add_handler (const string &url, const url_handler &, int priority=appbase::priority::medium_low)
 
void add_api (const api_description &api, int priority=appbase::priority::medium_low)
 
void add_async_handler (const string &url, const url_handler &handler)
 
void add_async_api (const api_description &api)
 
bool is_on_loopback () const
 
bool is_secure () const
 
bool verbose_errors () const
 
get_supported_apis_result get_supported_apis () const
 
fc::microseconds get_max_response_time () const
 
- Public Member Functions inherited from appbase::plugin< http_plugin >
 plugin ()
 
virtual ~plugin ()
 
virtual state get_state () const override
 
virtual const std::string & name () const override
 
virtual void register_dependencies ()
 
virtual void initialize (const variables_map &options) override
 
virtual void startup () override
 
virtual void shutdown () override
 
- Public Member Functions inherited from appbase::abstract_plugin
virtual ~abstract_plugin ()
 

Static Public Member Functions

static void set_defaults (const http_plugin_defaults config)
 
static void handle_exception (const char *api_name, const char *call_name, const string &body, url_response_callback cb)
 

Additional Inherited Members

- Public Types inherited from appbase::abstract_plugin
enum  state { registered , initialized , started , stopped }
 
- Protected Member Functions inherited from appbase::plugin< http_plugin >
 plugin (const string &name)
 

Detailed Description

This plugin starts an HTTP server and dispatches queries to registered handles based upon URL. The handler is passed the URL that was requested and a callback method that should be called with the response code and body.

The handler will be called from the appbase application io_service thread. The callback can be called from any thread and will automatically propagate the call to the http thread.

The HTTP service will run in its own thread with its own io_service to make sure that HTTP request processing does not interfer with other plugins.

Definition at line 64 of file http_plugin.hpp.

Constructor & Destructor Documentation

◆ http_plugin()

sysio::http_plugin::http_plugin ( )

Definition at line 632 of file http_plugin.cpp.

632 :my(new http_plugin_impl()){
634 }
application & app()
https_ecdh_curve_t
Here is the call graph for this function:

◆ ~http_plugin()

sysio::http_plugin::~http_plugin ( )
virtual

Definition at line 635 of file http_plugin.cpp.

635{}

Member Function Documentation

◆ add_api()

void sysio::http_plugin::add_api ( const api_description & api,
int priority = appbase::priority::medium_low )
inline

Definition at line 82 of file http_plugin.hpp.

82 {
83 for (const auto& call : api)
84 add_handler(call.first, call.second, priority);
85 }
void add_handler(const string &url, const url_handler &, int priority=appbase::priority::medium_low)
fc::variant call(const std::string &url, const std::string &path, const T &v)
Definition main.cpp:258
Here is the call graph for this function:
Here is the caller graph for this function:

◆ add_async_api()

void sysio::http_plugin::add_async_api ( const api_description & api)
inline

Definition at line 88 of file http_plugin.hpp.

88 {
89 for (const auto& call : api)
90 add_handler(call.first, call.second);
91 }
Here is the call graph for this function:

◆ add_async_handler()

void sysio::http_plugin::add_async_handler ( const string & url,
const url_handler & handler )

Definition at line 923 of file http_plugin.cpp.

923 {
924 fc_ilog( logger, "add api url: ${c}", ("c", url) );
925 my->url_handlers[url] = my->make_http_thread_url_handler(handler);
926 }
#define fc_ilog(LOGGER, FORMAT,...)
Definition logger.hpp:83
fc::logger logger
string url
Definition main.cpp:166

◆ add_handler()

void sysio::http_plugin::add_handler ( const string & url,
const url_handler & handler,
int priority = appbase::priority::medium_low )

Definition at line 918 of file http_plugin.cpp.

918 {
919 fc_ilog( logger, "add api url: ${c}", ("c", url) );
920 my->url_handlers[url] = my->make_app_thread_url_handler(priority, handler, my);
921 }
Here is the caller graph for this function:

◆ get_max_response_time()

fc::microseconds sysio::http_plugin::get_max_response_time ( ) const
Returns
the configured http-max-response-time-ms

Definition at line 995 of file http_plugin.cpp.

995 {
996 return my->max_response_time;
997 }

◆ get_supported_apis()

http_plugin::get_supported_apis_result sysio::http_plugin::get_supported_apis ( ) const

Definition at line 984 of file http_plugin.cpp.

984 {
985 get_supported_apis_result result;
986
987 for (const auto& handler : my->url_handlers) {
988 if (handler.first != "/v1/node/get_supported_apis")
989 result.apis.emplace_back(handler.first);
990 }
991
992 return result;
993 }

◆ handle_exception()

void sysio::http_plugin::handle_exception ( const char * api_name,
const char * call_name,
const string & body,
url_response_callback cb )
static

Definition at line 928 of file http_plugin.cpp.

928 {
929 try {
930 try {
931 throw;
932 } catch (chain::unknown_block_exception& e) {
933 error_results results{400, "Unknown Block", error_results::error_info(e, verbose_http_errors)};
934 cb( 400, fc::variant( results ));
935 } catch (chain::invalid_http_request& e) {
936 error_results results{400, "Invalid Request", error_results::error_info(e, verbose_http_errors)};
937 cb( 400, fc::variant( results ));
938 } catch (chain::unsatisfied_authorization& e) {
939 error_results results{401, "UnAuthorized", error_results::error_info(e, verbose_http_errors)};
940 cb( 401, fc::variant( results ));
941 } catch (chain::tx_duplicate& e) {
942 error_results results{409, "Conflict", error_results::error_info(e, verbose_http_errors)};
943 cb( 409, fc::variant( results ));
944 } catch (fc::eof_exception& e) {
945 error_results results{422, "Unprocessable Entity", error_results::error_info(e, verbose_http_errors)};
946 cb( 422, fc::variant( results ));
947 fc_elog( logger, "Unable to parse arguments to ${api}.${call}", ("api", api_name)( "call", call_name ) );
948 fc_dlog( logger, "Bad arguments: ${args}", ("args", body) );
949 } catch (fc::exception& e) {
950 error_results results{500, "Internal Service Error", error_results::error_info(e, verbose_http_errors)};
951 cb( 500, fc::variant( results ));
952 fc_dlog( logger, "Exception while processing ${api}.${call}: ${e}",
953 ("api", api_name)( "call", call_name )("e", e.to_detail_string()) );
954 } catch (std::exception& e) {
955 error_results results{500, "Internal Service Error", error_results::error_info(fc::exception( FC_LOG_MESSAGE( error, e.what())), verbose_http_errors)};
956 cb( 500, fc::variant( results ));
957 fc_elog( logger, "STD Exception encountered while processing ${api}.${call}",
958 ("api", api_name)( "call", call_name ) );
959 fc_dlog( logger, "Exception Details: ${e}", ("e", e.what()) );
960 } catch (...) {
961 error_results results{500, "Internal Service Error",
962 error_results::error_info(fc::exception( FC_LOG_MESSAGE( error, "Unknown Exception" )), verbose_http_errors)};
963 cb( 500, fc::variant( results ));
964 fc_elog( logger, "Unknown Exception encountered while processing ${api}.${call}",
965 ("api", api_name)( "call", call_name ) );
966 }
967 } catch (...) {
968 std::cerr << "Exception attempting to handle exception for " << api_name << "." << call_name << std::endl;
969 }
970 }
Used to generate a useful error report when an exception is thrown.
Definition exception.hpp:58
std::string to_detail_string(log_level ll=log_level::all) const
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.
#define fc_elog(LOGGER, FORMAT,...)
Definition logger.hpp:95
#define fc_dlog(LOGGER, FORMAT,...)
Definition logger.hpp:77
Here is the caller graph for this function:

◆ handle_sighup()

void sysio::http_plugin::handle_sighup ( )
overridevirtual

Reimplemented from appbase::plugin< http_plugin >.

Definition at line 894 of file http_plugin.cpp.

894 {
896 }
static void update(const fc::string &name, logger &log)
Definition logger.cpp:92
const fc::string logger_name("net_plugin_impl")
Here is the call graph for this function:
Here is the caller graph for this function:

◆ is_on_loopback()

bool sysio::http_plugin::is_on_loopback ( ) const

Definition at line 972 of file http_plugin.cpp.

972 {
973 return (!my->listen_endpoint || my->listen_endpoint->address().is_loopback()) && (!my->https_listen_endpoint || my->https_listen_endpoint->address().is_loopback());
974 }

◆ is_secure()

bool sysio::http_plugin::is_secure ( ) const

Definition at line 976 of file http_plugin.cpp.

976 {
977 return (!my->listen_endpoint || my->listen_endpoint->address().is_loopback());
978 }

◆ plugin_initialize()

void sysio::http_plugin::plugin_initialize ( const variables_map & options)

Definition at line 715 of file http_plugin.cpp.

715 {
716 try {
717 my->max_body_size = options.at( "max-body-size" ).as<uint32_t>();
718 verbose_http_errors = options.at( "verbose-http-errors" ).as<bool>();
719
720 my->thread_pool_size = options.at( "http-threads" ).as<uint16_t>();
721 SYS_ASSERT( my->thread_pool_size > 0, chain::plugin_config_exception,
722 "http-threads ${num} must be greater than 0", ("num", my->thread_pool_size));
723
724 auto max_bytes_mb = options.at( "http-max-bytes-in-flight-mb" ).as<int64_t>();
725 SYS_ASSERT( (max_bytes_mb >= -1 && max_bytes_mb < std::numeric_limits<int64_t>::max() / (1024 * 1024)), chain::plugin_config_exception,
726 "http-max-bytes-in-flight-mb (${max_bytes_mb}) must be equal to or greater than -1 and less than ${max}", ("max_bytes_mb", max_bytes_mb) ("max", std::numeric_limits<int64_t>::max() / (1024 * 1024)) );
727 if ( max_bytes_mb == -1 ) {
728 my->max_bytes_in_flight = std::numeric_limits<size_t>::max();
729 } else {
730 my->max_bytes_in_flight = max_bytes_mb * 1024 * 1024;
731 }
732 my->max_response_time = fc::microseconds( options.at("http-max-response-time-ms").as<uint32_t>() * 1000 );
733
734 my->validate_host = options.at("http-validate-host").as<bool>();
735 if( options.count( "http-alias" )) {
736 const auto& aliases = options["http-alias"].as<vector<string>>();
737 my->valid_hosts.insert(aliases.begin(), aliases.end());
738 }
739
740 tcp::resolver resolver( app().get_io_service());
741 if( options.count( "http-server-address" ) && options.at( "http-server-address" ).as<string>().length()) {
742 string lipstr = options.at( "http-server-address" ).as<string>();
743 string host = lipstr.substr( 0, lipstr.find( ':' ));
744 string port = lipstr.substr( host.size() + 1, lipstr.size());
745 tcp::resolver::query query( tcp::v4(), host.c_str(), port.c_str());
746 try {
747 my->listen_endpoint = *resolver.resolve( query );
748 ilog( "configured http to listen on ${h}:${p}", ("h", host)( "p", port ));
749 } catch ( const boost::system::system_error& ec ) {
750 elog( "failed to configure http to listen on ${h}:${p} (${m})",
751 ("h", host)( "p", port )( "m", ec.what()));
752 }
753
754 // add in resolved hosts and ports as well
755 if (my->listen_endpoint) {
756 my->add_aliases_for_endpoint(*my->listen_endpoint, host, port);
757 }
758 }
759
760 if( options.count( "unix-socket-path" ) && !options.at( "unix-socket-path" ).as<string>().empty()) {
761 boost::filesystem::path sock_path = options.at("unix-socket-path").as<string>();
762 if (sock_path.is_relative())
763 sock_path = app().data_dir() / sock_path;
764 my->unix_endpoint = asio::local::stream_protocol::endpoint(sock_path.string());
765 }
766
767 if( options.count( "https-server-address" ) && options.at( "https-server-address" ).as<string>().length()) {
768 if( !options.count( "https-certificate-chain-file" ) ||
769 options.at( "https-certificate-chain-file" ).as<string>().empty()) {
770 elog( "https-certificate-chain-file is required for HTTPS" );
771 return;
772 }
773 if( !options.count( "https-private-key-file" ) ||
774 options.at( "https-private-key-file" ).as<string>().empty()) {
775 elog( "https-private-key-file is required for HTTPS" );
776 return;
777 }
778
779 string lipstr = options.at( "https-server-address" ).as<string>();
780 string host = lipstr.substr( 0, lipstr.find( ':' ));
781 string port = lipstr.substr( host.size() + 1, lipstr.size());
782 tcp::resolver::query query( tcp::v4(), host.c_str(), port.c_str());
783 try {
784 my->https_listen_endpoint = *resolver.resolve( query );
785 ilog( "configured https to listen on ${h}:${p} (TLS configuration will be validated momentarily)",
786 ("h", host)( "p", port ));
787 my->https_cert_chain = options.at( "https-certificate-chain-file" ).as<string>();
788 my->https_key = options.at( "https-private-key-file" ).as<string>();
789 } catch ( const boost::system::system_error& ec ) {
790 elog( "failed to configure https to listen on ${h}:${p} (${m})",
791 ("h", host)( "p", port )( "m", ec.what()));
792 }
793
794 // add in resolved hosts and ports as well
795 if (my->https_listen_endpoint) {
796 my->add_aliases_for_endpoint(*my->https_listen_endpoint, host, port);
797 }
798 }
799
800 //watch out for the returns above when adding new code here
802 }
#define SYS_ASSERT(expr, exc_type, FORMAT,...)
Definition exceptions.hpp:7
bfs::path data_dir() const
Get data directory.
#define FC_LOG_AND_RETHROW()
#define ilog(FORMAT,...)
Definition logger.hpp:118
#define elog(FORMAT,...)
Definition logger.hpp:130
unsigned short uint16_t
Definition stdint.h:125
signed __int64 int64_t
Definition stdint.h:135
unsigned int uint32_t
Definition stdint.h:126
Here is the call graph for this function:

◆ plugin_shutdown()

void sysio::http_plugin::plugin_shutdown ( )

Definition at line 898 of file http_plugin.cpp.

898 {
899 if(my->server.is_listening())
900 my->server.stop_listening();
901 if(my->https_server.is_listening())
902 my->https_server.stop_listening();
903 if(my->unix_server.is_listening())
904 my->unix_server.stop_listening();
905
906 if( my->thread_pool ) {
907 my->thread_pool->stop();
908 my->thread_pool.reset();
909 }
910
911 // release http_plugin_impl_ptr shared_ptrs captured in url handlers
912 my->url_handlers.clear();
913
914 app().post( 0, [me = my](){} ); // keep my pointer alive until queue is drained
915 fc_ilog( logger, "exit shutdown");
916 }
auto post(int priority, Func &&func)
Here is the call graph for this function:

◆ plugin_startup()

void sysio::http_plugin::plugin_startup ( )

Definition at line 804 of file http_plugin.cpp.

804 {
805
806 handle_sighup(); // setup logging
807 app().post(appbase::priority::high, [this] ()
808 {
809 try {
810 my->thread_pool.emplace( "http", my->thread_pool_size );
811 if(my->listen_endpoint) {
812 try {
813 my->create_server_for_endpoint(*my->listen_endpoint, my->server);
814
815 fc_ilog( logger, "start listening for http requests" );
816 my->server.listen(*my->listen_endpoint);
817 my->server.start_accept();
818 } catch ( const fc::exception& e ){
819 fc_elog( logger, "http service failed to start: ${e}", ("e", e.to_detail_string()) );
820 throw;
821 } catch ( const std::exception& e ){
822 fc_elog( logger, "http service failed to start: ${e}", ("e", e.what()) );
823 throw;
824 } catch (...) {
825 fc_elog( logger, "error thrown from http io service" );
826 throw;
827 }
828 }
829
830 if(my->unix_endpoint) {
831 try {
832 my->unix_server.clear_access_channels(websocketpp::log::alevel::all);
833 my->unix_server.init_asio( &my->thread_pool->get_executor() );
834 my->unix_server.set_max_http_body_size(my->max_body_size);
835 my->unix_server.listen(*my->unix_endpoint);
836 // captures `this`, my needs to live as long as unix_server is handling requests
837 my->unix_server.set_http_handler([this](connection_hdl hdl) {
838 my->handle_http_request<detail::asio_local_with_stub_log>( my->unix_server.get_con_from_hdl(hdl));
839 });
840 my->unix_server.start_accept();
841 } catch ( const fc::exception& e ){
842 fc_elog( logger, "unix socket service (${path}) failed to start: ${e}", ("e", e.to_detail_string())("path",my->unix_endpoint->path()) );
843 throw;
844 } catch ( const std::exception& e ){
845 fc_elog( logger, "unix socket service (${path}) failed to start: ${e}", ("e", e.what())("path",my->unix_endpoint->path()) );
846 throw;
847 } catch (...) {
848 fc_elog( logger, "error thrown from unix socket (${path}) io service", ("path",my->unix_endpoint->path()) );
849 throw;
850 }
851 }
852
853 if(my->https_listen_endpoint) {
854 try {
855 my->create_server_for_endpoint(*my->https_listen_endpoint, my->https_server);
856 my->https_server.set_tls_init_handler([this](websocketpp::connection_hdl hdl) -> ssl_context_ptr{
857 return my->on_tls_init(hdl);
858 });
859
860 fc_ilog( logger, "start listening for https requests" );
861 my->https_server.listen(*my->https_listen_endpoint);
862 my->https_server.start_accept();
863 } catch ( const fc::exception& e ){
864 fc_elog( logger, "https service failed to start: ${e}", ("e", e.to_detail_string()) );
865 throw;
866 } catch ( const std::exception& e ){
867 fc_elog( logger, "https service failed to start: ${e}", ("e", e.what()) );
868 throw;
869 } catch (...) {
870 fc_elog( logger, "error thrown from https io service" );
871 throw;
872 }
873 }
874
875 add_api({{
876 std::string("/v1/node/get_supported_apis"),
877 [&](string, string body, url_response_callback cb) mutable {
878 try {
879 if (body.empty()) body = "{}";
880 auto result = (*this).get_supported_apis();
881 cb(200, fc::variant(result));
882 } catch (...) {
883 handle_exception("node", "get_supported_apis", body, cb);
884 }
885 }
886 }});
887 } catch (...) {
888 fc_elog(logger, "http_plugin startup fails, shutting down");
889 app().shutdown();
890 }
891 });
892 }
void add_api(const api_description &api, int priority=appbase::priority::medium_low)
static void handle_exception(const char *api_name, const char *call_name, const string &body, url_response_callback cb)
void handle_sighup() override
std::string string
Definition string.hpp:10
websocketpp::lib::shared_ptr< websocketpp::lib::asio::ssl::context > ssl_context_ptr
std::function< void(int, fc::variant)> url_response_callback
A callback function provided to a URL handler to allow it to specify the HTTP response code and body.
lib::weak_ptr< void > connection_hdl
A handle to uniquely identify a connection.
static constexpr int high
static level const all
Special aggregate value representing "all levels".
Definition levels.hpp:152
Here is the call graph for this function:

◆ set_defaults()

void sysio::http_plugin::set_defaults ( const http_plugin_defaults config)
static

Definition at line 55 of file http_plugin.cpp.

55 {
56 current_http_plugin_defaults = config;
57 }
Here is the caller graph for this function:

◆ set_program_options()

void sysio::http_plugin::set_program_options ( options_description & ,
options_description & cfg )
overridevirtual

Implements appbase::abstract_plugin.

Definition at line 637 of file http_plugin.cpp.

637 {
638 if(current_http_plugin_defaults.default_unix_socket_path.length())
639 cfg.add_options()
640 ("unix-socket-path", bpo::value<string>()->default_value(current_http_plugin_defaults.default_unix_socket_path),
641 "The filename (relative to data-dir) to create a unix socket for HTTP RPC; set blank to disable.");
642 else
643 cfg.add_options()
644 ("unix-socket-path", bpo::value<string>(),
645 "The filename (relative to data-dir) to create a unix socket for HTTP RPC; set blank to disable.");
646
647 if(current_http_plugin_defaults.default_http_port)
648 cfg.add_options()
649 ("http-server-address", bpo::value<string>()->default_value("127.0.0.1:" + std::to_string(current_http_plugin_defaults.default_http_port)),
650 "The local IP and port to listen for incoming http connections; set blank to disable.");
651 else
652 cfg.add_options()
653 ("http-server-address", bpo::value<string>(),
654 "The local IP and port to listen for incoming http connections; leave blank to disable.");
655
656 cfg.add_options()
657 ("https-server-address", bpo::value<string>(),
658 "The local IP and port to listen for incoming https connections; leave blank to disable.")
659
660 ("https-certificate-chain-file", bpo::value<string>(),
661 "Filename with the certificate chain to present on https connections. PEM format. Required for https.")
662
663 ("https-private-key-file", bpo::value<string>(),
664 "Filename with https private key in PEM format. Required for https")
665
666 ("https-ecdh-curve", bpo::value<https_ecdh_curve_t>()->notifier([this](https_ecdh_curve_t c) {
667 my->https_ecdh_curve = c;
668 })->default_value(SECP384R1),
669 "Configure https ECDH curve to use: secp384r1 or prime256v1")
670
671 ("access-control-allow-origin", bpo::value<string>()->notifier([this](const string& v) {
672 my->access_control_allow_origin = v;
673 fc_ilog( logger, "configured http with Access-Control-Allow-Origin: ${o}",
674 ("o", my->access_control_allow_origin) );
675 }),
676 "Specify the Access-Control-Allow-Origin to be returned on each request.")
677
678 ("access-control-allow-headers", bpo::value<string>()->notifier([this](const string& v) {
679 my->access_control_allow_headers = v;
680 fc_ilog( logger, "configured http with Access-Control-Allow-Headers : ${o}",
681 ("o", my->access_control_allow_headers) );
682 }),
683 "Specify the Access-Control-Allow-Headers to be returned on each request.")
684
685 ("access-control-max-age", bpo::value<string>()->notifier([this](const string& v) {
686 my->access_control_max_age = v;
687 fc_ilog( logger, "configured http with Access-Control-Max-Age : ${o}",
688 ("o", my->access_control_max_age) );
689 }),
690 "Specify the Access-Control-Max-Age to be returned on each request.")
691
692 ("access-control-allow-credentials",
693 bpo::bool_switch()->notifier([this](bool v) {
694 my->access_control_allow_credentials = v;
695 if( v ) fc_ilog( logger, "configured http with Access-Control-Allow-Credentials: true" );
696 })->default_value(false),
697 "Specify if Access-Control-Allow-Credentials: true should be returned on each request.")
698 ("max-body-size", bpo::value<uint32_t>()->default_value(my->max_body_size),
699 "The maximum body size in bytes allowed for incoming RPC requests")
700 ("http-max-bytes-in-flight-mb", bpo::value<int64_t>()->default_value(500),
701 "Maximum size in megabytes http_plugin should use for processing http requests. -1 for unlimited. 429 error response when exceeded." )
702 ("http-max-response-time-ms", bpo::value<uint32_t>()->default_value(30),
703 "Maximum time for processing a request.")
704 ("verbose-http-errors", bpo::bool_switch()->default_value(false),
705 "Append the error log to HTTP responses")
706 ("http-validate-host", boost::program_options::value<bool>()->default_value(true),
707 "If set to false, then any incoming \"Host\" header is considered valid")
708 ("http-alias", bpo::value<std::vector<string>>()->composing(),
709 "Additionaly acceptable values for the \"Host\" header of incoming HTTP requests, can be specified multiple times. Includes http/s_server_address by default.")
710 ("http-threads", bpo::value<uint16_t>()->default_value( my->thread_pool_size ),
711 "Number of worker threads in http thread pool")
712 ;
713 }

◆ verbose_errors()

bool sysio::http_plugin::verbose_errors ( ) const

Definition at line 980 of file http_plugin.cpp.

980 {
981 return verbose_http_errors;
982 }

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