RPC Add cross origin resource sharing support
This commit is contained in:
parent
8d511f3c24
commit
69c37200aa
|
@ -46,6 +46,7 @@ namespace net_utils
|
|||
{
|
||||
|
||||
enum http_method{
|
||||
http_method_options,
|
||||
http_method_get,
|
||||
http_method_post,
|
||||
http_method_put,
|
||||
|
@ -115,6 +116,7 @@ namespace net_utils
|
|||
std::string m_host; //"Host:"
|
||||
std::string m_cookie; //"Cookie:"
|
||||
std::string m_user_agent; //"User-Agent:"
|
||||
std::string m_origin; //"Origin:"
|
||||
fields_list m_etc_fields;
|
||||
|
||||
void clear()
|
||||
|
@ -128,6 +130,7 @@ namespace net_utils
|
|||
m_host.clear();
|
||||
m_cookie.clear();
|
||||
m_user_agent.clear();
|
||||
m_origin.clear();
|
||||
m_etc_fields.clear();
|
||||
}
|
||||
};
|
||||
|
|
|
@ -749,10 +749,10 @@ using namespace std;
|
|||
MTRACE("http_stream_filter::parse_cached_header(*)");
|
||||
|
||||
STATIC_REGEXP_EXPR_1(rexp_mach_field,
|
||||
"\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)|(User-Agent)"
|
||||
// 12 3 4 5 6 7 8 9 10
|
||||
"\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)|(User-Agent)|(Origin)"
|
||||
// 12 3 4 5 6 7 8 9 10 11
|
||||
"|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]",
|
||||
//11 1213 14
|
||||
//12 13 14 15
|
||||
boost::regex::icase | boost::regex::normal);
|
||||
|
||||
boost::smatch result;
|
||||
|
@ -764,7 +764,7 @@ using namespace std;
|
|||
//lookup all fields and fill well-known fields
|
||||
while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched)
|
||||
{
|
||||
const size_t field_val = 13;
|
||||
const size_t field_val = 14;
|
||||
//const size_t field_etc_name = 11;
|
||||
|
||||
int i = 2; //start position = 2
|
||||
|
@ -788,8 +788,10 @@ using namespace std;
|
|||
body_info.m_cookie = result[field_val];
|
||||
else if(result[i++].matched)//"User-Agent"
|
||||
body_info.m_user_agent = result[field_val];
|
||||
else if(result[i++].matched)//"Origin"
|
||||
body_info.m_origin = result[field_val];
|
||||
else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!)
|
||||
body_info.m_etc_fields.emplace_back(result[11], result[field_val]);
|
||||
body_info.m_etc_fields.emplace_back(result[12], result[field_val]);
|
||||
else
|
||||
{CHECK_AND_ASSERT_MES(false, false, "http_stream_filter::parse_cached_header() not matched last entry in:"<<m_cache_to_process);}
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@ namespace net_utils
|
|||
struct http_server_config
|
||||
{
|
||||
std::string m_folder;
|
||||
std::vector<std::string> m_access_control_origins;
|
||||
boost::optional<login> m_user;
|
||||
critical_section m_lock;
|
||||
};
|
||||
|
@ -193,6 +194,7 @@ namespace net_utils
|
|||
response.m_response_code = 200;
|
||||
response.m_response_comment = "OK";
|
||||
response.m_body.clear();
|
||||
|
||||
return m_config.m_phandler->handle_http_request(query_info, response, m_conn_context);
|
||||
}
|
||||
|
||||
|
|
|
@ -316,7 +316,10 @@ namespace net_utils
|
|||
CHECK_AND_ASSERT_MES(result[0].matched, false, "simple_http_connection_handler::analize_http_method() assert failed...");
|
||||
http_ver_major = boost::lexical_cast<int>(result[11]);
|
||||
http_ver_minor = boost::lexical_cast<int>(result[12]);
|
||||
if(result[4].matched)
|
||||
|
||||
if(result[3].matched)
|
||||
method = http::http_method_options;
|
||||
else if(result[4].matched)
|
||||
method = http::http_method_get;
|
||||
else if(result[5].matched)
|
||||
method = http::http_method_head;
|
||||
|
@ -472,8 +475,8 @@ namespace net_utils
|
|||
bool simple_http_connection_handler<t_connection_context>::parse_cached_header(http_header_info& body_info, const std::string& m_cache_to_process, size_t pos)
|
||||
{
|
||||
STATIC_REGEXP_EXPR_1(rexp_mach_field,
|
||||
"\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)|(User-Agent)"
|
||||
// 12 3 4 5 6 7 8 9 10
|
||||
"\n?((Connection)|(Referer)|(Content-Length)|(Content-Type)|(Transfer-Encoding)|(Content-Encoding)|(Host)|(Cookie)|(User-Agent)|(Origin)"
|
||||
// 12 3 4 5 6 7 8 9 10 11
|
||||
"|([\\w-]+?)) ?: ?((.*?)(\r?\n))[^\t ]",
|
||||
//11 1213 14
|
||||
boost::regex::icase | boost::regex::normal);
|
||||
|
@ -487,8 +490,8 @@ namespace net_utils
|
|||
//lookup all fields and fill well-known fields
|
||||
while( boost::regex_search( it_current_bound, it_end_bound, result, rexp_mach_field, boost::match_default) && result[0].matched)
|
||||
{
|
||||
const size_t field_val = 13;
|
||||
const size_t field_etc_name = 11;
|
||||
const size_t field_val = 14;
|
||||
const size_t field_etc_name = 12;
|
||||
|
||||
int i = 2; //start position = 2
|
||||
if(result[i++].matched)//"Connection"
|
||||
|
@ -509,6 +512,8 @@ namespace net_utils
|
|||
body_info.m_cookie = result[field_val];
|
||||
else if(result[i++].matched)//"User-Agent"
|
||||
body_info.m_user_agent = result[field_val];
|
||||
else if(result[i++].matched)//"Origin"
|
||||
body_info.m_origin = result[field_val];
|
||||
else if(result[i++].matched)//e.t.c (HAVE TO BE MATCHED!)
|
||||
body_info.m_etc_fields.push_back(std::pair<std::string, std::string>(result[field_etc_name], result[field_val]));
|
||||
else
|
||||
|
@ -537,17 +542,27 @@ namespace net_utils
|
|||
template<class t_connection_context>
|
||||
bool simple_http_connection_handler<t_connection_context>::handle_request_and_send_response(const http::http_request_info& query_info)
|
||||
{
|
||||
http_response_info response;
|
||||
bool res = handle_request(query_info, response);
|
||||
http_response_info response{};
|
||||
//CHECK_AND_ASSERT_MES(res, res, "handle_request(query_info, response) returned false" );
|
||||
bool res = true;
|
||||
|
||||
if (query_info.m_http_method != http::http_method_options)
|
||||
{
|
||||
res = handle_request(query_info, response);
|
||||
}
|
||||
else
|
||||
{
|
||||
response.m_response_code = 200;
|
||||
response.m_response_comment = "OK";
|
||||
}
|
||||
|
||||
std::string response_data = get_response_header(response);
|
||||
|
||||
//LOG_PRINT_L0("HTTP_SEND: << \r\n" << response_data + response.m_body);
|
||||
|
||||
LOG_PRINT_L3("HTTP_RESPONSE_HEAD: << \r\n" << response_data);
|
||||
|
||||
m_psnd_hndlr->do_send((void*)response_data.data(), response_data.size());
|
||||
if(response.m_body.size() && (query_info.m_http_method != http::http_method_head))
|
||||
if ((response.m_body.size() && (query_info.m_http_method != http::http_method_head)) || (query_info.m_http_method == http::http_method_options))
|
||||
m_psnd_hndlr->do_send((void*)response.m_body.data(), response.m_body.size());
|
||||
return res;
|
||||
}
|
||||
|
@ -579,7 +594,6 @@ namespace net_utils
|
|||
response.m_response_comment = "OK";
|
||||
response.m_mime_tipe = get_file_mime_tipe(uri_to_path);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
//-----------------------------------------------------------------------------------
|
||||
|
@ -591,8 +605,12 @@ namespace net_utils
|
|||
"Server: Epee-based\r\n"
|
||||
"Content-Length: ";
|
||||
buf += boost::lexical_cast<std::string>(response.m_body.size()) + "\r\n";
|
||||
buf += "Content-Type: ";
|
||||
buf += response.m_mime_tipe + "\r\n";
|
||||
|
||||
if(!response.m_mime_tipe.empty())
|
||||
{
|
||||
buf += "Content-Type: ";
|
||||
buf += response.m_mime_tipe + "\r\n";
|
||||
}
|
||||
|
||||
buf += "Last-Modified: ";
|
||||
time_t tm;
|
||||
|
@ -612,6 +630,19 @@ namespace net_utils
|
|||
m_want_close = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Cross-origin resource sharing
|
||||
if(m_query_info.m_header_info.m_origin.size())
|
||||
{
|
||||
if (std::binary_search(m_config.m_access_control_origins.begin(), m_config.m_access_control_origins.end(), m_query_info.m_header_info.m_origin))
|
||||
{
|
||||
buf += "Access-Control-Allow-Origin: ";
|
||||
buf += m_query_info.m_header_info.m_origin;
|
||||
buf += "\r\n";
|
||||
buf += "Access-Control-Allow-Methods: POST, PUT, GET, OPTIONS\r\n";
|
||||
}
|
||||
}
|
||||
|
||||
//add additional fields, if it is
|
||||
for(fields_list::const_iterator it = response.m_additional_fields.begin(); it!=response.m_additional_fields.end(); it++)
|
||||
buf += it->first + ":" + it->second + "\r\n";
|
||||
|
|
|
@ -56,6 +56,7 @@ namespace epee
|
|||
{}
|
||||
|
||||
bool init(const std::string& bind_port = "0", const std::string& bind_ip = "0.0.0.0",
|
||||
std::vector<std::string> access_control_origins = std::vector<std::string>(),
|
||||
boost::optional<net_utils::http::login> user = boost::none)
|
||||
{
|
||||
|
||||
|
@ -65,6 +66,10 @@ namespace epee
|
|||
//here set folder for hosting reqests
|
||||
m_net_server.get_config_object().m_folder = "";
|
||||
|
||||
//set access control allow origins if configured
|
||||
std::sort(access_control_origins.begin(), access_control_origins.end());
|
||||
m_net_server.get_config_object().m_access_control_origins = std::move(access_control_origins);
|
||||
|
||||
m_net_server.get_config_object().m_user = std::move(user);
|
||||
|
||||
MGINFO("Binding on " << bind_ip << ":" << bind_port);
|
||||
|
|
|
@ -101,7 +101,7 @@ namespace cryptonote
|
|||
http_login.emplace(std::move(rpc_config->login->username), std::move(rpc_config->login->password).password());
|
||||
|
||||
return epee::http_server_impl_base<core_rpc_server, connection_context>::init(
|
||||
std::move(port), std::move(rpc_config->bind_ip), std::move(http_login)
|
||||
std::move(port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login)
|
||||
);
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
//
|
||||
#include "rpc_args.h"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
#include "common/command_line.h"
|
||||
#include "common/i18n.h"
|
||||
|
@ -38,6 +39,7 @@ namespace cryptonote
|
|||
: rpc_bind_ip({"rpc-bind-ip", rpc_args::tr("Specify ip to bind rpc server"), "127.0.0.1"})
|
||||
, rpc_login({"rpc-login", rpc_args::tr("Specify username[:password] required for RPC server"), "", true})
|
||||
, confirm_external_bind({"confirm-external-bind", rpc_args::tr("Confirm rpc-bind-ip value is NOT a loopback (local) IP")})
|
||||
, rpc_access_control_origins({"rpc-access-control-origins", rpc_args::tr("Specify a comma separated list of origins to allow cross origin resource sharing"), ""})
|
||||
{}
|
||||
|
||||
const char* rpc_args::tr(const char* str) { return i18n_translate(str, "cryptonote::rpc_args"); }
|
||||
|
@ -48,6 +50,7 @@ namespace cryptonote
|
|||
command_line::add_arg(desc, arg.rpc_bind_ip);
|
||||
command_line::add_arg(desc, arg.rpc_login);
|
||||
command_line::add_arg(desc, arg.confirm_external_bind);
|
||||
command_line::add_arg(desc, arg.rpc_access_control_origins);
|
||||
}
|
||||
|
||||
boost::optional<rpc_args> rpc_args::process(const boost::program_options::variables_map& vm)
|
||||
|
@ -91,6 +94,21 @@ namespace cryptonote
|
|||
}
|
||||
}
|
||||
|
||||
auto access_control_origins_input = command_line::get_arg(vm, arg.rpc_access_control_origins);
|
||||
if (!access_control_origins_input.empty())
|
||||
{
|
||||
if (!config.login)
|
||||
{
|
||||
LOG_ERROR(arg.rpc_access_control_origins.name << tr(" requires RFC server password --") << arg.rpc_login.name << tr(" cannot be empty"));
|
||||
return boost::none;
|
||||
}
|
||||
|
||||
std::vector<std::string> access_control_origins;
|
||||
boost::split(access_control_origins, access_control_origins_input, boost::is_any_of(","));
|
||||
std::for_each(access_control_origins.begin(), access_control_origins.end(), boost::bind(&boost::trim<std::string>, _1, std::locale::classic()));
|
||||
config.access_control_origins = std::move(access_control_origins);
|
||||
}
|
||||
|
||||
return {std::move(config)};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,7 @@ namespace cryptonote
|
|||
const command_line::arg_descriptor<std::string> rpc_bind_ip;
|
||||
const command_line::arg_descriptor<std::string> rpc_login;
|
||||
const command_line::arg_descriptor<bool> confirm_external_bind;
|
||||
const command_line::arg_descriptor<std::string> rpc_access_control_origins;
|
||||
};
|
||||
|
||||
static const char* tr(const char* str);
|
||||
|
@ -62,6 +63,7 @@ namespace cryptonote
|
|||
static boost::optional<rpc_args> process(const boost::program_options::variables_map& vm);
|
||||
|
||||
std::string bind_ip;
|
||||
std::vector<std::string> access_control_origins;
|
||||
boost::optional<tools::login> login; // currently `boost::none` if unspecified by user
|
||||
};
|
||||
}
|
||||
|
|
|
@ -216,7 +216,7 @@ namespace tools
|
|||
|
||||
m_net_server.set_threads_prefix("RPC");
|
||||
return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init(
|
||||
std::move(bind_port), std::move(rpc_config->bind_ip), std::move(http_login)
|
||||
std::move(bind_port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login)
|
||||
);
|
||||
}
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
|
|
Loading…
Reference in New Issue