Skip to content
This repository has been archived by the owner on Aug 2, 2022. It is now read-only.

Commit

Permalink
Merge pull request #5425 from EOSIO/unix_socket_wallet
Browse files Browse the repository at this point in the history
Add UNIX socket HTTP server to keosd
  • Loading branch information
spoonincode authored Sep 1, 2018
2 parents 84e7c61 + 77d4ea1 commit ad33a01
Show file tree
Hide file tree
Showing 11 changed files with 1,001 additions and 23 deletions.
145 changes: 126 additions & 19 deletions plugins/http_plugin/http_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* @copyright defined in eos/LICENSE.txt
*/
#include <eosio/http_plugin/http_plugin.hpp>
#include <eosio/http_plugin/local_endpoint.hpp>
#include <eosio/chain/exceptions.hpp>

#include <fc/network/ip.hpp>
Expand Down Expand Up @@ -43,6 +44,11 @@ namespace eosio {
using std::shared_ptr;
using websocketpp::connection_hdl;

static http_plugin_defaults current_http_plugin_defaults;

void http_plugin::set_defaults(const http_plugin_defaults config) {
current_http_plugin_defaults = config;
}

namespace detail {

Expand Down Expand Up @@ -79,9 +85,42 @@ namespace eosio {

static const long timeout_open_handshake = 0;
};

struct asio_local_with_stub_log : public websocketpp::config::asio {
typedef asio_local_with_stub_log type;
typedef asio base;

typedef base::concurrency_type concurrency_type;

typedef base::request_type request_type;
typedef base::response_type response_type;

typedef base::message_type message_type;
typedef base::con_msg_manager_type con_msg_manager_type;
typedef base::endpoint_msg_manager_type endpoint_msg_manager_type;

typedef websocketpp::log::stub elog_type;
typedef websocketpp::log::stub alog_type;

typedef base::rng_type rng_type;

struct transport_config : public base::transport_config {
typedef type::concurrency_type concurrency_type;
typedef type::alog_type alog_type;
typedef type::elog_type elog_type;
typedef type::request_type request_type;
typedef type::response_type response_type;
typedef websocketpp::transport::asio::basic_socket::local_endpoint socket_type;
};

typedef websocketpp::transport::asio::local_endpoint<transport_config> transport_type;

static const long timeout_open_handshake = 0;
};
}

using websocket_server_type = websocketpp::server<detail::asio_with_stub_log<websocketpp::transport::asio::basic_socket::endpoint>>;
using websocket_local_server_type = websocketpp::server<detail::asio_local_with_stub_log>;
using websocket_server_tls_type = websocketpp::server<detail::asio_with_stub_log<websocketpp::transport::asio::tls_socket::endpoint>>;
using ssl_context_ptr = websocketpp::lib::shared_ptr<websocketpp::lib::asio::ssl::context>;

Expand All @@ -105,9 +144,16 @@ namespace eosio {

websocket_server_tls_type https_server;

optional<asio::local::stream_protocol::endpoint> unix_endpoint;
websocket_local_server_type unix_server;

bool validate_host;
set<string> valid_hosts;

string unix_socket_path_option_name = "unix-socket-path";
string http_server_address_option_name = "http-server-address";
string https_server_address_option_name = "https-server-address";

bool host_port_is_valid( const std::string& header_host_port, const string& endpoint_local_host_port ) {
return !validate_host || header_host_port == endpoint_local_host_port || valid_hosts.find(header_host_port) != valid_hosts.end();
}
Expand Down Expand Up @@ -163,7 +209,7 @@ namespace eosio {
}

template<class T>
static void handle_exception(typename websocketpp::server<detail::asio_with_stub_log<T>>::connection_ptr con) {
static void handle_exception(typename websocketpp::server<T>::connection_ptr con) {
string err = "Internal Service error, http: ";
try {
con->set_status( websocketpp::http::status_code::internal_server_error );
Expand Down Expand Up @@ -195,18 +241,26 @@ namespace eosio {
}

template<class T>
void handle_http_request(typename websocketpp::server<detail::asio_with_stub_log<T>>::connection_ptr con) {
try {
bool is_secure = con->get_uri()->get_secure();
const auto& local_endpoint = con->get_socket().lowest_layer().local_endpoint();
auto local_socket_host_port = local_endpoint.address().to_string() + ":" + std::to_string(local_endpoint.port());
bool allow_host(const typename T::request_type& req, typename websocketpp::server<T>::connection_ptr con) {
bool is_secure = con->get_uri()->get_secure();
const auto& local_endpoint = con->get_socket().lowest_layer().local_endpoint();
auto local_socket_host_port = local_endpoint.address().to_string() + ":" + std::to_string(local_endpoint.port());

const auto& host_str = req.get_header("Host");
if (host_str.empty() || !host_is_valid(host_str, local_socket_host_port, is_secure)) {
con->set_status(websocketpp::http::status_code::bad_request);
return false;
}
return true;
}

template<class T>
void handle_http_request(typename websocketpp::server<T>::connection_ptr con) {
try {
auto& req = con->get_request();
const auto& host_str = req.get_header("Host");
if (host_str.empty() || !host_is_valid(host_str, local_socket_host_port, is_secure)) {
con->set_status(websocketpp::http::status_code::bad_request);

if(!allow_host<T>(req, con))
return;
}

if( !access_control_allow_origin.empty()) {
con->append_header( "Access-Control-Allow-Origin", access_control_allow_origin );
Expand Down Expand Up @@ -258,7 +312,7 @@ namespace eosio {
ws.set_reuse_addr(true);
ws.set_max_http_body_size(max_body_size);
ws.set_http_handler([&](connection_hdl hdl) {
handle_http_request<T>(ws.get_con_from_hdl(hdl));
handle_http_request<detail::asio_with_stub_log<T>>(ws.get_con_from_hdl(hdl));
});
} catch ( const fc::exception& e ){
elog( "http: ${e}", ("e",e.to_detail_string()));
Expand All @@ -275,17 +329,41 @@ namespace eosio {
valid_hosts.emplace(host + ":" + resolved_port_str);
}

void mangle_option_names() {
if(current_http_plugin_defaults.address_config_prefix.empty())
return;
unix_socket_path_option_name.insert(0, current_http_plugin_defaults.address_config_prefix+"-");
http_server_address_option_name.insert(0, current_http_plugin_defaults.address_config_prefix+"-");
https_server_address_option_name.insert(0, current_http_plugin_defaults.address_config_prefix+"-");
}
};

template<>
bool http_plugin_impl::allow_host<detail::asio_local_with_stub_log>(const detail::asio_local_with_stub_log::request_type& req, websocketpp::server<detail::asio_local_with_stub_log>::connection_ptr con) {
return true;
}

http_plugin::http_plugin():my(new http_plugin_impl()){}
http_plugin::~http_plugin(){}

void http_plugin::set_program_options(options_description&, options_description& cfg) {
cfg.add_options()
("http-server-address", bpo::value<string>()->default_value("127.0.0.1:8888"),
"The local IP and port to listen for incoming http connections; set blank to disable.")
my->mangle_option_names();
if(current_http_plugin_defaults.default_unix_socket_path.length())
cfg.add_options()
(my->unix_socket_path_option_name.c_str(), bpo::value<string>()->default_value(current_http_plugin_defaults.default_unix_socket_path),
"The filename (relative to data-dir) to create a unix socket for HTTP RPC; set blank to disable.");

if(current_http_plugin_defaults.default_http_port)
cfg.add_options()
(my->http_server_address_option_name.c_str(), bpo::value<string>()->default_value("127.0.0.1:" + std::to_string(current_http_plugin_defaults.default_http_port)),
"The local IP and port to listen for incoming http connections; set blank to disable.");
else
cfg.add_options()
(my->http_server_address_option_name.c_str(), bpo::value<string>(),
"The local IP and port to listen for incoming http connections; leave blank to disable.");

("https-server-address", bpo::value<string>(),
cfg.add_options()
(my->https_server_address_option_name.c_str(), bpo::value<string>(),
"The local IP and port to listen for incoming https connections; leave blank to disable.")

("https-certificate-chain-file", bpo::value<string>(),
Expand Down Expand Up @@ -334,8 +412,8 @@ namespace eosio {
}

tcp::resolver resolver( app().get_io_service());
if( options.count( "http-server-address" ) && options.at( "http-server-address" ).as<string>().length()) {
string lipstr = options.at( "http-server-address" ).as<string>();
if( options.count( my->http_server_address_option_name ) && options.at( my->http_server_address_option_name ).as<string>().length()) {
string lipstr = options.at( my->http_server_address_option_name ).as<string>();
string host = lipstr.substr( 0, lipstr.find( ':' ));
string port = lipstr.substr( host.size() + 1, lipstr.size());
tcp::resolver::query query( tcp::v4(), host.c_str(), port.c_str());
Expand All @@ -353,7 +431,14 @@ namespace eosio {
}
}

if( options.count( "https-server-address" ) && options.at( "https-server-address" ).as<string>().length()) {
if( options.count( my->unix_socket_path_option_name ) && !options.at( my->unix_socket_path_option_name ).as<string>().empty()) {
boost::filesystem::path sock_path = options.at(my->unix_socket_path_option_name).as<string>();
if (sock_path.is_relative())
sock_path = app().data_dir() / sock_path;
my->unix_endpoint = asio::local::stream_protocol::endpoint(sock_path.string());
}

if( options.count( my->https_server_address_option_name ) && options.at( my->https_server_address_option_name ).as<string>().length()) {
if( !options.count( "https-certificate-chain-file" ) ||
options.at( "https-certificate-chain-file" ).as<string>().empty()) {
elog( "https-certificate-chain-file is required for HTTPS" );
Expand All @@ -365,7 +450,7 @@ namespace eosio {
return;
}

string lipstr = options.at( "https-server-address" ).as<string>();
string lipstr = options.at( my->https_server_address_option_name ).as<string>();
string host = lipstr.substr( 0, lipstr.find( ':' ));
string port = lipstr.substr( host.size() + 1, lipstr.size());
tcp::resolver::query query( tcp::v4(), host.c_str(), port.c_str());
Expand Down Expand Up @@ -413,6 +498,28 @@ namespace eosio {
}
}

if(my->unix_endpoint) {
try {
my->unix_server.clear_access_channels(websocketpp::log::alevel::all);
my->unix_server.init_asio(&app().get_io_service());
my->unix_server.set_max_http_body_size(my->max_body_size);
my->unix_server.listen(*my->unix_endpoint);
my->unix_server.set_http_handler([&](connection_hdl hdl) {
my->handle_http_request<detail::asio_local_with_stub_log>( my->unix_server.get_con_from_hdl(hdl));
});
my->unix_server.start_accept();
} catch ( const fc::exception& e ){
elog( "unix socket service failed to start: ${e}", ("e",e.to_detail_string()));
throw;
} catch ( const std::exception& e ){
elog( "unix socket service failed to start: ${e}", ("e",e.what()));
throw;
} catch (...) {
elog("error thrown from unix socket io service");
throw;
}
}

if(my->https_listen_endpoint) {
try {
my->create_server_for_endpoint(*my->https_listen_endpoint, my->https_server);
Expand Down
16 changes: 16 additions & 0 deletions plugins/http_plugin/include/eosio/http_plugin/http_plugin.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,19 @@ namespace eosio {
*/
using api_description = std::map<string, url_handler>;

struct http_plugin_defaults {
//If not empty, this string is prepended on to the various configuration
// items for setting listen addresses
string address_config_prefix;
//If empty, unix socket support will be completely disabled. If not empty,
// unix socket support is enabled with the given default path (treated relative
// to the datadir)
string default_unix_socket_path;
//If non 0, HTTP will be enabled by default on the given port number. If
// 0, HTTP will not be enabled by default
uint16_t default_http_port{0};
};

/**
* This plugin starts an HTTP server and dispatches queries to
* registered handles based upon URL. The handler is passed the
Expand All @@ -60,6 +73,9 @@ namespace eosio {
http_plugin();
virtual ~http_plugin();

//must be called before initialize
static void set_defaults(const http_plugin_defaults config);

APPBASE_PLUGIN_REQUIRES()
virtual void set_program_options(options_description&, options_description& cfg) override;

Expand Down
Loading

0 comments on commit ad33a01

Please sign in to comment.