<commit_before>//
// sync_client.cpp
// ~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2012 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#include <iostream>
#include <istream>
#include <ostream>
#include <string>
#include <regex>
#include <boost/algorithm/string.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <fc/variant.hpp>
#include <fc/io/json.hpp>
#include <fc/network/platform_root_ca.hpp>
#include <eosio/chain/exceptions.hpp>
#include <eosio/http_plugin/http_plugin.hpp>
#include <eosio/chain_plugin/chain_plugin.hpp>
#include <boost/asio/ssl/rfc2818_verification.hpp>
#include "httpc.hpp"

using boost::asio::ip::tcp;
using namespace eosio::chain;
namespace eosio { namespace client { namespace http {

   namespace detail {
      class http_context_impl {
         public:
            boost::asio::io_service ios;
      };

      void http_context_deleter::operator()(http_context_impl* p) const {
         delete p;
      }
   }

   http_context create_http_context() {
      return http_context(new detail::http_context_impl, detail::http_context_deleter());
   }

   void do_connect(tcp::socket& sock, const resolved_url& url) {
      // Get a list of endpoints corresponding to the server name.
      vector<tcp::endpoint> endpoints;
      endpoints.reserve(url.resolved_addresses.size());
      for (const auto& addr: url.resolved_addresses) {
         endpoints.emplace_back(boost::asio::ip::make_address(addr), url.resolved_port);
      }
      boost::asio::connect(sock, endpoints);
   }

   template<class T>
   std::string do_txrx(T& socket, boost::asio::streambuf& request_buff, unsigned int& status_code) {
      // Send the request.
      boost::asio::write(socket, request_buff);

      // Read the response status line. The response streambuf will automatically
      // grow to accommodate the entire line. The growth may be limited by passing
      // a maximum size to the streambuf constructor.
      boost::asio::streambuf response;
      boost::asio::read_until(socket, response, "\r\n");

      // Check that response is OK.
      std::istream response_stream(&response);
      std::string http_version;
      response_stream >> http_version;
      response_stream >> status_code;

      EOS_ASSERT( status_code != 400, invalid_http_request, "The server has rejected the request as invalid!");

      std::string status_message;
      std::getline(response_stream, status_message);
      EOS_ASSERT( !(!response_stream || http_version.substr(0, 5) != "HTTP/"), invalid_http_response, "Invalid Response" );

      // Read the response headers, which are terminated by a blank line.
      boost::asio::read_until(socket, response, "\r\n\r\n");

      // Process the response headers.
      std::string header;
      int response_content_length = -1;
      std::regex clregex(R"xx(^content-length:\s+(\d+))xx", std::regex_constants::icase);
      while (std::getline(response_stream, header) && header != "\r") {
         std::smatch match;
         if(std::regex_search(header, match, clregex))
            response_content_length = std::stoi(match[1]);
      }
      EOS_ASSERT(response_content_length >= 0, invalid_http_response, "Invalid content-length response");

      std::stringstream re;
      // Write whatever content we already have to output.
      response_content_length -= response.size();
      if (response.size() > 0)
         re << &response;

      boost::asio::read(socket, response, boost::asio::transfer_exactly(response_content_length));
      re << &response;

      return re.str();
   }

   parsed_url parse_url( const string& server_url ) {
      parsed_url res;

      //unix socket doesn't quite follow classical "URL" rules so deal with it manually
      if(boost::algorithm::starts_with(server_url, "unix://")) {
         res.scheme = "unix";
         res.server = server_url.substr(strlen("unix://"));
         return res;
      }

      //via rfc3986 and modified a bit to suck out the port number
      //Sadly this doesn't work for ipv6 addresses
      std::regex rgx(R"xx(^(([^:/?#]+):)?(//([^:/?#]*)(:(\d+))?)?([^?#]*)(\?([^#]*))?(#(.*))?)xx");
      std::smatch match;
      if(std::regex_search(server_url.begin(), server_url.end(), match, rgx)) {
         res.scheme = match[2];
         res.server = match[4];
         res.port = match[6];
         res.path = match[7];
      }
      if(res.scheme != "http" && res.scheme != "https")
         EOS_THROW(fail_to_resolve_host, "Unrecognized URL scheme (${s}) in URL \"${u}\"", ("s", res.scheme)("u", server_url));
      if(res.server.empty())
         EOS_THROW(fail_to_resolve_host, "No server parsed from URL \"${u}\"", ("u", server_url));
      if(res.port.empty())
         res.port = res.scheme == "http" ? "80" : "443";
      boost::trim_right_if(res.path, boost::is_any_of("/"));
      return res;
   }

   resolved_url resolve_url( const http_context& context, const parsed_url& url ) {
      if(url.scheme == "unix")
         return resolved_url(url);

      tcp::resolver resolver(context->ios);
      boost::system::error_code ec;
      auto result = resolver.resolve(tcp::v4(), url.server, url.port, ec);
      if (ec) {
         EOS_THROW(fail_to_resolve_host, "Error resolving \"${server}:${port}\" : ${m}", ("server", url.server)("port",url.port)("m",ec.message()));
      }

      // non error results are guaranteed to return a non-empty range
      vector<string> resolved_addresses;
      resolved_addresses.reserve(result.size());
      optional<uint16_t> resolved_port;
      bool is_loopback = true;

      for(const auto& r : result) {
         const auto& addr = r.endpoint().address();
         if (addr.is_v6()) continue;
         uint16_t port = r.endpoint().port();
         resolved_addresses.emplace_back(addr.to_string());
         is_loopback = is_loopback && addr.is_loopback();

         if (resolved_port) {
            EOS_ASSERT(*resolved_port == port, resolved_to_multiple_ports, "Service name \"${port}\" resolved to multiple ports and this is not supported!", ("port",url.port));
         } else {
            resolved_port = port;
         }
      }

      return resolved_url(url, std::move(resolved_addresses), *resolved_port, is_loopback);
   }

   string format_host_header(const resolved_url& url) {
      // common practice is to only make the port explicit when it is the non-default port
      if (
         (url.scheme == "https" && url.resolved_port == 443) ||
         (url.scheme == "http" && url.resolved_port == 80)
      ) {
         return url.server;
      } else {
         return url.server + ":" + url.port;
      }
   }

   fc::variant do_http_call( const connection_param& cp,
                             const fc::variant& postdata,
                             bool print_request,
                             bool print_response ) {
   std::string postjson;
   if( !postdata.is_null() ) {
      postjson = print_request ? fc::json::to_pretty_string( postdata ) : fc::json::to_string( postdata );
   }

   const auto& url = cp.url;

   boost::asio::streambuf request;
   std::ostream request_stream(&request);
   auto host_header_value = format_host_header(url);
   request_stream << "POST " << url.path << " HTTP/1.0\r\n";
   request_stream << "Host: " << host_header_value << "\r\n";
   request_stream << "content-length: " << postjson.size() << "\r\n";
   request_stream << "Accept: */*\r\n";
   request_stream << "Connection: close\r\n";
   request_stream << "\r\n";
   // append more customized headers
   std::vector<string>::iterator itr;
   for (itr = cp.headers.begin(); itr != cp.headers.end(); itr++) {
      request_stream << *itr << "\r\n";
   }
   request_stream << postjson;

   if ( print_request ) {
      string s(request.size(), '\0');
      buffer_copy(boost::asio::buffer(s), request.data());
      std::cerr << "REQUEST:" << std::endl
                << "---------------------" << std::endl
                << s << std::endl
                << "---------------------" << std::endl;
   }

   unsigned int status_code;
   std::string re;

   try {
      if(url.scheme == "unix") {
         boost::asio::local::stream_protocol::socket unix_socket(cp.context->ios);
         unix_socket.connect(boost::asio::local::stream_protocol::endpoint(url.server));
         re = do_txrx(unix_socket, request, status_code);
      }
      else if(url.scheme == "http") {
         tcp::socket socket(cp.context->ios);
         do_connect(socket, url);
         re = do_txrx(socket, request, status_code);
      }
      else { //https
         boost::asio::ssl::context ssl_context(boost::asio::ssl::context::sslv23_client);
         fc::add_platform_root_cas_to_context(ssl_context);

         boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket(cp.context->ios, ssl_context);
         SSL_set_tlsext_host_name(socket.native_handle(), url.server.c_str());
         if(cp.verify_cert) {
            socket.set_verify_mode(boost::asio::ssl::verify_peer);
            socket.set_verify_callback(boost::asio::ssl::rfc2818_verification(url.server));
         }
         do_connect(socket.next_layer(), url);
         socket.handshake(boost::asio::ssl::stream_base::client);
         re = do_txrx(socket, request, status_code);
         //try and do a clean shutdown; but swallow if this fails (other side could have already gave TCP the ax)
         try {socket.shutdown();} catch(...) {}
      }
   } catch ( invalid_http_request& e ) {
      e.append_log( FC_LOG_MESSAGE( info, "Please verify this url is valid: ${url}", ("url", url.scheme + "://" + url.server + ":" + url.port + url.path) ) );
      e.append_log( FC_LOG_MESSAGE( info, "If the condition persists, please contact the RPC server administrator for ${server}!", ("server", url.server) ) );
      throw;
   }

   const auto response_result = fc::json::from_string(re);
   if( print_response ) {
      std::cerr << "RESPONSE:" << std::endl
                << "---------------------" << std::endl
                << fc::json::to_pretty_string( response_result ) << std::endl
                << "---------------------" << std::endl;
   }
   if( status_code == 200 || status_code == 201 || status_code == 202 ) {
      return response_result;
   } else if( status_code == 404 ) {
      // Unknown endpoint
      if (url.path.compare(0, chain_func_base.size(), chain_func_base) == 0) {
         throw chain::missing_chain_api_plugin_exception(FC_LOG_MESSAGE(error, "Chain API plugin is not enabled"));
      } else if (url.path.compare(0, wallet_func_base.size(), wallet_func_base) == 0) {
         throw chain::missing_wallet_api_plugin_exception(FC_LOG_MESSAGE(error, "Wallet is not available"));
      } else if (url.path.compare(0, history_func_base.size(), history_func_base) == 0) {
         throw chain::missing_history_api_plugin_exception(FC_LOG_MESSAGE(error, "History API plugin is not enabled"));
      } else if (url.path.compare(0, net_func_base.size(), net_func_base) == 0) {
         throw chain::missing_net_api_plugin_exception(FC_LOG_MESSAGE(error, "Net API plugin is not enabled"));
      }
   } else {
      auto &&error_info = response_result.as<eosio::error_results>().error;
      // Construct fc exception from error
      const auto &error_details = error_info.details;

      fc::log_messages logs;
      for (auto itr = error_details.begin(); itr != error_details.end(); itr++) {
         const auto& context = fc::log_context(fc::log_level::error, itr->file.data(), itr->line_number, itr->method.data());
         logs.emplace_back(fc::log_message(context, itr->message));
      }

      throw fc::exception(logs, error_info.code, error_info.name, error_info.what);
   }

   EOS_ASSERT( status_code == 200, http_request_fail, "Error code ${c}\n: ${msg}\n", ("c", status_code)("msg", re) );
   return response_result;
   }
}}}
<commit_msg>Fix -r,--header, that was passing headers after CRLF<commit_after>//
// sync_client.cpp
// ~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2012 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#include <iostream>
#include <istream>
#include <ostream>
#include <string>
#include <regex>
#include <boost/algorithm/string.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <fc/variant.hpp>
#include <fc/io/json.hpp>
#include <fc/network/platform_root_ca.hpp>
#include <eosio/chain/exceptions.hpp>
#include <eosio/http_plugin/http_plugin.hpp>
#include <eosio/chain_plugin/chain_plugin.hpp>
#include <boost/asio/ssl/rfc2818_verification.hpp>
#include "httpc.hpp"

using boost::asio::ip::tcp;
using namespace eosio::chain;
namespace eosio { namespace client { namespace http {

   namespace detail {
      class http_context_impl {
         public:
            boost::asio::io_service ios;
      };

      void http_context_deleter::operator()(http_context_impl* p) const {
         delete p;
      }
   }

   http_context create_http_context() {
      return http_context(new detail::http_context_impl, detail::http_context_deleter());
   }

   void do_connect(tcp::socket& sock, const resolved_url& url) {
      // Get a list of endpoints corresponding to the server name.
      vector<tcp::endpoint> endpoints;
      endpoints.reserve(url.resolved_addresses.size());
      for (const auto& addr: url.resolved_addresses) {
         endpoints.emplace_back(boost::asio::ip::make_address(addr), url.resolved_port);
      }
      boost::asio::connect(sock, endpoints);
   }

   template<class T>
   std::string do_txrx(T& socket, boost::asio::streambuf& request_buff, unsigned int& status_code) {
      // Send the request.
      boost::asio::write(socket, request_buff);

      // Read the response status line. The response streambuf will automatically
      // grow to accommodate the entire line. The growth may be limited by passing
      // a maximum size to the streambuf constructor.
      boost::asio::streambuf response;
      boost::asio::read_until(socket, response, "\r\n");

      // Check that response is OK.
      std::istream response_stream(&response);
      std::string http_version;
      response_stream >> http_version;
      response_stream >> status_code;

      EOS_ASSERT( status_code != 400, invalid_http_request, "The server has rejected the request as invalid!");

      std::string status_message;
      std::getline(response_stream, status_message);
      EOS_ASSERT( !(!response_stream || http_version.substr(0, 5) != "HTTP/"), invalid_http_response, "Invalid Response" );

      // Read the response headers, which are terminated by a blank line.
      boost::asio::read_until(socket, response, "\r\n\r\n");

      // Process the response headers.
      std::string header;
      int response_content_length = -1;
      std::regex clregex(R"xx(^content-length:\s+(\d+))xx", std::regex_constants::icase);
      while (std::getline(response_stream, header) && header != "\r") {
         std::smatch match;
         if(std::regex_search(header, match, clregex))
            response_content_length = std::stoi(match[1]);
      }
      EOS_ASSERT(response_content_length >= 0, invalid_http_response, "Invalid content-length response");

      std::stringstream re;
      // Write whatever content we already have to output.
      response_content_length -= response.size();
      if (response.size() > 0)
         re << &response;

      boost::asio::read(socket, response, boost::asio::transfer_exactly(response_content_length));
      re << &response;

      return re.str();
   }

   parsed_url parse_url( const string& server_url ) {
      parsed_url res;

      //unix socket doesn't quite follow classical "URL" rules so deal with it manually
      if(boost::algorithm::starts_with(server_url, "unix://")) {
         res.scheme = "unix";
         res.server = server_url.substr(strlen("unix://"));
         return res;
      }

      //via rfc3986 and modified a bit to suck out the port number
      //Sadly this doesn't work for ipv6 addresses
      std::regex rgx(R"xx(^(([^:/?#]+):)?(//([^:/?#]*)(:(\d+))?)?([^?#]*)(\?([^#]*))?(#(.*))?)xx");
      std::smatch match;
      if(std::regex_search(server_url.begin(), server_url.end(), match, rgx)) {
         res.scheme = match[2];
         res.server = match[4];
         res.port = match[6];
         res.path = match[7];
      }
      if(res.scheme != "http" && res.scheme != "https")
         EOS_THROW(fail_to_resolve_host, "Unrecognized URL scheme (${s}) in URL \"${u}\"", ("s", res.scheme)("u", server_url));
      if(res.server.empty())
         EOS_THROW(fail_to_resolve_host, "No server parsed from URL \"${u}\"", ("u", server_url));
      if(res.port.empty())
         res.port = res.scheme == "http" ? "80" : "443";
      boost::trim_right_if(res.path, boost::is_any_of("/"));
      return res;
   }

   resolved_url resolve_url( const http_context& context, const parsed_url& url ) {
      if(url.scheme == "unix")
         return resolved_url(url);

      tcp::resolver resolver(context->ios);
      boost::system::error_code ec;
      auto result = resolver.resolve(tcp::v4(), url.server, url.port, ec);
      if (ec) {
         EOS_THROW(fail_to_resolve_host, "Error resolving \"${server}:${port}\" : ${m}", ("server", url.server)("port",url.port)("m",ec.message()));
      }

      // non error results are guaranteed to return a non-empty range
      vector<string> resolved_addresses;
      resolved_addresses.reserve(result.size());
      optional<uint16_t> resolved_port;
      bool is_loopback = true;

      for(const auto& r : result) {
         const auto& addr = r.endpoint().address();
         if (addr.is_v6()) continue;
         uint16_t port = r.endpoint().port();
         resolved_addresses.emplace_back(addr.to_string());
         is_loopback = is_loopback && addr.is_loopback();

         if (resolved_port) {
            EOS_ASSERT(*resolved_port == port, resolved_to_multiple_ports, "Service name \"${port}\" resolved to multiple ports and this is not supported!", ("port",url.port));
         } else {
            resolved_port = port;
         }
      }

      return resolved_url(url, std::move(resolved_addresses), *resolved_port, is_loopback);
   }

   string format_host_header(const resolved_url& url) {
      // common practice is to only make the port explicit when it is the non-default port
      if (
         (url.scheme == "https" && url.resolved_port == 443) ||
         (url.scheme == "http" && url.resolved_port == 80)
      ) {
         return url.server;
      } else {
         return url.server + ":" + url.port;
      }
   }

   fc::variant do_http_call( const connection_param& cp,
                             const fc::variant& postdata,
                             bool print_request,
                             bool print_response ) {
   std::string postjson;
   if( !postdata.is_null() ) {
      postjson = print_request ? fc::json::to_pretty_string( postdata ) : fc::json::to_string( postdata );
   }

   const auto& url = cp.url;

   boost::asio::streambuf request;
   std::ostream request_stream(&request);
   auto host_header_value = format_host_header(url);
   request_stream << "POST " << url.path << " HTTP/1.0\r\n";
   request_stream << "Host: " << host_header_value << "\r\n";
   request_stream << "content-length: " << postjson.size() << "\r\n";
   request_stream << "Accept: */*\r\n";
   request_stream << "Connection: close\r\n";
   // append more customized headers
   std::vector<string>::iterator itr;
   for (itr = cp.headers.begin(); itr != cp.headers.end(); itr++) {
      request_stream << *itr << "\r\n";
   }
   request_stream << "\r\n";
   request_stream << postjson;

   if ( print_request ) {
      string s(request.size(), '\0');
      buffer_copy(boost::asio::buffer(s), request.data());
      std::cerr << "REQUEST:" << std::endl
                << "---------------------" << std::endl
                << s << std::endl
                << "---------------------" << std::endl;
   }

   unsigned int status_code;
   std::string re;

   try {
      if(url.scheme == "unix") {
         boost::asio::local::stream_protocol::socket unix_socket(cp.context->ios);
         unix_socket.connect(boost::asio::local::stream_protocol::endpoint(url.server));
         re = do_txrx(unix_socket, request, status_code);
      }
      else if(url.scheme == "http") {
         tcp::socket socket(cp.context->ios);
         do_connect(socket, url);
         re = do_txrx(socket, request, status_code);
      }
      else { //https
         boost::asio::ssl::context ssl_context(boost::asio::ssl::context::sslv23_client);
         fc::add_platform_root_cas_to_context(ssl_context);

         boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket(cp.context->ios, ssl_context);
         SSL_set_tlsext_host_name(socket.native_handle(), url.server.c_str());
         if(cp.verify_cert) {
            socket.set_verify_mode(boost::asio::ssl::verify_peer);
            socket.set_verify_callback(boost::asio::ssl::rfc2818_verification(url.server));
         }
         do_connect(socket.next_layer(), url);
         socket.handshake(boost::asio::ssl::stream_base::client);
         re = do_txrx(socket, request, status_code);
         //try and do a clean shutdown; but swallow if this fails (other side could have already gave TCP the ax)
         try {socket.shutdown();} catch(...) {}
      }
   } catch ( invalid_http_request& e ) {
      e.append_log( FC_LOG_MESSAGE( info, "Please verify this url is valid: ${url}", ("url", url.scheme + "://" + url.server + ":" + url.port + url.path) ) );
      e.append_log( FC_LOG_MESSAGE( info, "If the condition persists, please contact the RPC server administrator for ${server}!", ("server", url.server) ) );
      throw;
   }

   const auto response_result = fc::json::from_string(re);
   if( print_response ) {
      std::cerr << "RESPONSE:" << std::endl
                << "---------------------" << std::endl
                << fc::json::to_pretty_string( response_result ) << std::endl
                << "---------------------" << std::endl;
   }
   if( status_code == 200 || status_code == 201 || status_code == 202 ) {
      return response_result;
   } else if( status_code == 404 ) {
      // Unknown endpoint
      if (url.path.compare(0, chain_func_base.size(), chain_func_base) == 0) {
         throw chain::missing_chain_api_plugin_exception(FC_LOG_MESSAGE(error, "Chain API plugin is not enabled"));
      } else if (url.path.compare(0, wallet_func_base.size(), wallet_func_base) == 0) {
         throw chain::missing_wallet_api_plugin_exception(FC_LOG_MESSAGE(error, "Wallet is not available"));
      } else if (url.path.compare(0, history_func_base.size(), history_func_base) == 0) {
         throw chain::missing_history_api_plugin_exception(FC_LOG_MESSAGE(error, "History API plugin is not enabled"));
      } else if (url.path.compare(0, net_func_base.size(), net_func_base) == 0) {
         throw chain::missing_net_api_plugin_exception(FC_LOG_MESSAGE(error, "Net API plugin is not enabled"));
      }
   } else {
      auto &&error_info = response_result.as<eosio::error_results>().error;
      // Construct fc exception from error
      const auto &error_details = error_info.details;

      fc::log_messages logs;
      for (auto itr = error_details.begin(); itr != error_details.end(); itr++) {
         const auto& context = fc::log_context(fc::log_level::error, itr->file.data(), itr->line_number, itr->method.data());
         logs.emplace_back(fc::log_message(context, itr->message));
      }

      throw fc::exception(logs, error_info.code, error_info.name, error_info.what);
   }

   EOS_ASSERT( status_code == 200, http_request_fail, "Error code ${c}\n: ${msg}\n", ("c", status_code)("msg", re) );
   return response_result;
   }
}}}
<|endoftext|>


<commit_before>//
// sync_client.cpp
// ~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2012 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#include <iostream>
#include <istream>
#include <ostream>
#include <string>
#include <regex>
#include <boost/algorithm/string.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <fc/variant.hpp>
#include <fc/io/json.hpp>
#include <fc/network/platform_root_ca.hpp>
#include <eosio/chain/exceptions.hpp>
#include <eosio/http_plugin/http_plugin.hpp>
#include <eosio/chain_plugin/chain_plugin.hpp>
#include <boost/asio/ssl/rfc2818_verification.hpp>
#include "httpc.hpp"

using boost::asio::ip::tcp;
using namespace eosio::chain;
namespace eosio { namespace client { namespace http {

   namespace detail {
      class http_context_impl {
         public:
            boost::asio::io_service ios;
      };

      void http_context_deleter::operator()(http_context_impl* p) const {
         delete p;
      }
   }

   http_context create_http_context() {
      return http_context(new detail::http_context_impl, detail::http_context_deleter());
   }

   void do_connect(tcp::socket& sock, const resolved_url& url) {
      // Get a list of endpoints corresponding to the server name.
      vector<tcp::endpoint> endpoints;
      endpoints.reserve(url.resolved_addresses.size());
      for (const auto& addr: url.resolved_addresses) {
         endpoints.emplace_back(boost::asio::ip::make_address(addr), url.resolved_port);
      }
      boost::asio::connect(sock, endpoints);
   }

   template<class T>
   std::string do_txrx(T& socket, boost::asio::streambuf& request_buff, unsigned int& status_code) {
      // Send the request.
      boost::asio::write(socket, request_buff);

      // Read the response status line. The response streambuf will automatically
      // grow to accommodate the entire line. The growth may be limited by passing
      // a maximum size to the streambuf constructor.
      boost::asio::streambuf response;
      boost::asio::read_until(socket, response, "\r\n");

      // Check that response is OK.
      std::istream response_stream(&response);
      std::string http_version;
      response_stream >> http_version;
      response_stream >> status_code;

      EOS_ASSERT( status_code != 500, invalid_http_request, "The server has rejected the request as invalid!");

      std::string status_message;
      std::getline(response_stream, status_message);
      EOS_ASSERT( !(!response_stream || http_version.substr(0, 5) != "HTTP/"), invalid_http_response, "Invalid Response" );

      // Read the response headers, which are terminated by a blank line.
      boost::asio::read_until(socket, response, "\r\n\r\n");

      // Process the response headers.
      std::string header;
      int response_content_length = -1;
      std::regex clregex(R"xx(^content-length:\s+(\d+))xx", std::regex_constants::icase);
      while (std::getline(response_stream, header) && header != "\r") {
         std::smatch match;
         if(std::regex_search(header, match, clregex))
            response_content_length = std::stoi(match[1]);
      }

      // Attempt to read the response body using the length indicated by the
      // Content-length header. If the header was not present just read all available bytes.
      if( response_content_length != -1 ) {
         response_content_length -= response.size();
         if( response_content_length > 0 )
            boost::asio::read(socket, response, boost::asio::transfer_exactly(response_content_length));
      } else {
         boost::system::error_code ec;
         boost::asio::read(socket, response, boost::asio::transfer_all(), ec);
         EOS_ASSERT(!ec || ec == boost::asio::ssl::error::stream_truncated, http_exception, "Unable to read http response: ${err}", ("err",ec.message()));
      }

      std::stringstream re;
      re << &response;
      return re.str();
   }

   parsed_url parse_url( const string& server_url ) {
      parsed_url res;

      //unix socket doesn't quite follow classical "URL" rules so deal with it manually
      if(boost::algorithm::starts_with(server_url, "unix://")) {
         res.scheme = "unix";
         res.server = server_url.substr(strlen("unix://"));
         return res;
      }

      //via rfc3986 and modified a bit to suck out the port number
      //Sadly this doesn't work for ipv6 addresses
      std::regex rgx(R"xx(^(([^:/?#]+):)?(//([^:/?#]*)(:(\d+))?)?([^?#]*)(\?([^#]*))?(#(.*))?)xx");
      std::smatch match;
      if(std::regex_search(server_url.begin(), server_url.end(), match, rgx)) {
         res.scheme = match[2];
         res.server = match[4];
         res.port = match[6];
         res.path = match[7];
      }
      if(res.scheme != "http" && res.scheme != "https")
         EOS_THROW(fail_to_resolve_host, "Unrecognized URL scheme (${s}) in URL \"${u}\"", ("s", res.scheme)("u", server_url));
      if(res.server.empty())
         EOS_THROW(fail_to_resolve_host, "No server parsed from URL \"${u}\"", ("u", server_url));
      if(res.port.empty())
         res.port = res.scheme == "http" ? "80" : "443";
      boost::trim_right_if(res.path, boost::is_any_of("/"));
      return res;
   }

   resolved_url resolve_url( const http_context& context, const parsed_url& url ) {
      if(url.scheme == "unix")
         return resolved_url(url);

      tcp::resolver resolver(context->ios);
      boost::system::error_code ec;
      auto result = resolver.resolve(tcp::v4(), url.server, url.port, ec);
      if (ec) {
         EOS_THROW(fail_to_resolve_host, "Error resolving \"${server}:${port}\" : ${m}", ("server", url.server)("port",url.port)("m",ec.message()));
      }

      // non error results are guaranteed to return a non-empty range
      vector<string> resolved_addresses;
      resolved_addresses.reserve(result.size());
      optional<uint16_t> resolved_port;
      bool is_loopback = true;

      for(const auto& r : result) {
         const auto& addr = r.endpoint().address();
         if (addr.is_v6()) continue;
         uint16_t port = r.endpoint().port();
         resolved_addresses.emplace_back(addr.to_string());
         is_loopback = is_loopback && addr.is_loopback();

         if (resolved_port) {
            EOS_ASSERT(*resolved_port == port, resolved_to_multiple_ports, "Service name \"${port}\" resolved to multiple ports and this is not supported!", ("port",url.port));
         } else {
            resolved_port = port;
         }
      }

      return resolved_url(url, std::move(resolved_addresses), *resolved_port, is_loopback);
   }

   string format_host_header(const resolved_url& url) {
      // common practice is to only make the port explicit when it is the non-default port
      if (
         (url.scheme == "https" && url.resolved_port == 443) ||
         (url.scheme == "http" && url.resolved_port == 80)
      ) {
         return url.server;
      } else {
         return url.server + ":" + url.port;
      }
   }

   fc::variant do_http_call( const connection_param& cp,
                             const fc::variant& postdata,
                             bool print_request,
                             bool print_response ) {
   std::string postjson;
   if( !postdata.is_null() ) {
      postjson = print_request ? fc::json::to_pretty_string( postdata ) : fc::json::to_string( postdata, fc::time_point::maximum() );
   }

   const auto& url = cp.url;

   boost::asio::streambuf request;
   std::ostream request_stream(&request);
   auto host_header_value = format_host_header(url);
   request_stream << "POST " << url.path << " HTTP/1.0\r\n";
   request_stream << "Host: " << host_header_value << "\r\n";
   request_stream << "content-length: " << postjson.size() << "\r\n";
   request_stream << "Accept: */*\r\n";
   request_stream << "Connection: close\r\n";
   // append more customized headers
   std::vector<string>::iterator itr;
   for (itr = cp.headers.begin(); itr != cp.headers.end(); itr++) {
      request_stream << *itr << "\r\n";
   }
   request_stream << "\r\n";
   request_stream << postjson;

   if ( print_request ) {
      string s(request.size(), '\0');
      buffer_copy(boost::asio::buffer(s), request.data());
      std::cerr << "REQUEST:" << std::endl
                << "---------------------" << std::endl
                << s << std::endl
                << "---------------------" << std::endl;
   }

   unsigned int status_code;
   std::string re;

   try {
      if(url.scheme == "unix") {
         boost::asio::local::stream_protocol::socket unix_socket(cp.context->ios);
         unix_socket.connect(boost::asio::local::stream_protocol::endpoint(url.server));
         re = do_txrx(unix_socket, request, status_code);
      }
      else if(url.scheme == "http") {
         tcp::socket socket(cp.context->ios);
         do_connect(socket, url);
         re = do_txrx(socket, request, status_code);
      }
      else { //https
         boost::asio::ssl::context ssl_context(boost::asio::ssl::context::sslv23_client);
         fc::add_platform_root_cas_to_context(ssl_context);

         boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket(cp.context->ios, ssl_context);
         SSL_set_tlsext_host_name(socket.native_handle(), url.server.c_str());
         if(cp.verify_cert) {
            socket.set_verify_mode(boost::asio::ssl::verify_peer);
            socket.set_verify_callback(boost::asio::ssl::rfc2818_verification(url.server));
         }
         do_connect(socket.next_layer(), url);
         socket.handshake(boost::asio::ssl::stream_base::client);
         re = do_txrx(socket, request, status_code);
         //try and do a clean shutdown; but swallow if this fails (other side could have already gave TCP the ax)
         try {socket.shutdown();} catch(...) {}
      }
   } catch ( invalid_http_request& e ) {
      e.append_log( FC_LOG_MESSAGE( info, "Please verify this url is valid: ${url}", ("url", url.scheme + "://" + url.server + ":" + url.port + url.path) ) );
      e.append_log( FC_LOG_MESSAGE( info, "If the condition persists, please contact the RPC server administrator for ${server}!", ("server", url.server) ) );
      throw;
   }

   const auto response_result = fc::json::from_string(re);
   if( print_response ) {
      std::cerr << "RESPONSE:" << std::endl
                << "---------------------" << std::endl
                << fc::json::to_pretty_string( response_result ) << std::endl
                << "---------------------" << std::endl;
   }
   if( status_code == 200 || status_code == 201 || status_code == 202 ) {
      return response_result;
   } else if( status_code == 404 ) {
      // Unknown endpoint
      if (url.path.compare(0, chain_func_base.size(), chain_func_base) == 0) {
         throw chain::missing_chain_api_plugin_exception(FC_LOG_MESSAGE(error, "Chain API plugin is not enabled"));
      } else if (url.path.compare(0, wallet_func_base.size(), wallet_func_base) == 0) {
         throw chain::missing_wallet_api_plugin_exception(FC_LOG_MESSAGE(error, "Wallet is not available"));
      } else if (url.path.compare(0, history_func_base.size(), history_func_base) == 0) {
         throw chain::missing_history_api_plugin_exception(FC_LOG_MESSAGE(error, "History API plugin is not enabled"));
      } else if (url.path.compare(0, net_func_base.size(), net_func_base) == 0) {
         throw chain::missing_net_api_plugin_exception(FC_LOG_MESSAGE(error, "Net API plugin is not enabled"));
      }
   } else {
      auto &&error_info = response_result.as<eosio::error_results>().error;
      // Construct fc exception from error
      const auto &error_details = error_info.details;

      fc::log_messages logs;
      for (auto itr = error_details.begin(); itr != error_details.end(); itr++) {
         const auto& context = fc::log_context(fc::log_level::error, itr->file.data(), itr->line_number, itr->method.data());
         logs.emplace_back(fc::log_message(context, itr->message));
      }

      throw fc::exception(logs, error_info.code, error_info.name, error_info.what);
   }

   EOS_ASSERT( status_code == 200, http_request_fail, "Error code ${c}\n: ${msg}\n", ("c", status_code)("msg", re) );
   return response_result;
   }
}}}
<commit_msg>Remove the assert of 400 error code<commit_after>//
// sync_client.cpp
// ~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2012 Christopher M. Kohlhoff (chris at kohlhoff dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//

#include <iostream>
#include <istream>
#include <ostream>
#include <string>
#include <regex>
#include <boost/algorithm/string.hpp>
#include <boost/asio.hpp>
#include <boost/asio/ssl.hpp>
#include <fc/variant.hpp>
#include <fc/io/json.hpp>
#include <fc/network/platform_root_ca.hpp>
#include <eosio/chain/exceptions.hpp>
#include <eosio/http_plugin/http_plugin.hpp>
#include <eosio/chain_plugin/chain_plugin.hpp>
#include <boost/asio/ssl/rfc2818_verification.hpp>
#include "httpc.hpp"

using boost::asio::ip::tcp;
using namespace eosio::chain;
namespace eosio { namespace client { namespace http {

   namespace detail {
      class http_context_impl {
         public:
            boost::asio::io_service ios;
      };

      void http_context_deleter::operator()(http_context_impl* p) const {
         delete p;
      }
   }

   http_context create_http_context() {
      return http_context(new detail::http_context_impl, detail::http_context_deleter());
   }

   void do_connect(tcp::socket& sock, const resolved_url& url) {
      // Get a list of endpoints corresponding to the server name.
      vector<tcp::endpoint> endpoints;
      endpoints.reserve(url.resolved_addresses.size());
      for (const auto& addr: url.resolved_addresses) {
         endpoints.emplace_back(boost::asio::ip::make_address(addr), url.resolved_port);
      }
      boost::asio::connect(sock, endpoints);
   }

   template<class T>
   std::string do_txrx(T& socket, boost::asio::streambuf& request_buff, unsigned int& status_code) {
      // Send the request.
      boost::asio::write(socket, request_buff);

      // Read the response status line. The response streambuf will automatically
      // grow to accommodate the entire line. The growth may be limited by passing
      // a maximum size to the streambuf constructor.
      boost::asio::streambuf response;
      boost::asio::read_until(socket, response, "\r\n");

      // Check that response is OK.
      std::istream response_stream(&response);
      std::string http_version;
      response_stream >> http_version;
      response_stream >> status_code;

//      EOS_ASSERT( status_code != 400, invalid_http_request, "The server has rejected the request as invalid!");
      std::string status_message;
      std::getline(response_stream, status_message);
      EOS_ASSERT( !(!response_stream || http_version.substr(0, 5) != "HTTP/"), invalid_http_response, "Invalid Response" );

      // Read the response headers, which are terminated by a blank line.
      boost::asio::read_until(socket, response, "\r\n\r\n");

      // Process the response headers.
      std::string header;
      int response_content_length = -1;
      std::regex clregex(R"xx(^content-length:\s+(\d+))xx", std::regex_constants::icase);
      while (std::getline(response_stream, header) && header != "\r") {
         std::smatch match;
         if(std::regex_search(header, match, clregex))
            response_content_length = std::stoi(match[1]);
      }

      // Attempt to read the response body using the length indicated by the
      // Content-length header. If the header was not present just read all available bytes.
      if( response_content_length != -1 ) {
         response_content_length -= response.size();
         if( response_content_length > 0 )
            boost::asio::read(socket, response, boost::asio::transfer_exactly(response_content_length));
      } else {
         boost::system::error_code ec;
         boost::asio::read(socket, response, boost::asio::transfer_all(), ec);
         EOS_ASSERT(!ec || ec == boost::asio::ssl::error::stream_truncated, http_exception, "Unable to read http response: ${err}", ("err",ec.message()));
      }

      std::stringstream re;
      re << &response;
      return re.str();
   }

   parsed_url parse_url( const string& server_url ) {
      parsed_url res;

      //unix socket doesn't quite follow classical "URL" rules so deal with it manually
      if(boost::algorithm::starts_with(server_url, "unix://")) {
         res.scheme = "unix";
         res.server = server_url.substr(strlen("unix://"));
         return res;
      }

      //via rfc3986 and modified a bit to suck out the port number
      //Sadly this doesn't work for ipv6 addresses
      std::regex rgx(R"xx(^(([^:/?#]+):)?(//([^:/?#]*)(:(\d+))?)?([^?#]*)(\?([^#]*))?(#(.*))?)xx");
      std::smatch match;
      if(std::regex_search(server_url.begin(), server_url.end(), match, rgx)) {
         res.scheme = match[2];
         res.server = match[4];
         res.port = match[6];
         res.path = match[7];
      }
      if(res.scheme != "http" && res.scheme != "https")
         EOS_THROW(fail_to_resolve_host, "Unrecognized URL scheme (${s}) in URL \"${u}\"", ("s", res.scheme)("u", server_url));
      if(res.server.empty())
         EOS_THROW(fail_to_resolve_host, "No server parsed from URL \"${u}\"", ("u", server_url));
      if(res.port.empty())
         res.port = res.scheme == "http" ? "80" : "443";
      boost::trim_right_if(res.path, boost::is_any_of("/"));
      return res;
   }

   resolved_url resolve_url( const http_context& context, const parsed_url& url ) {
      if(url.scheme == "unix")
         return resolved_url(url);

      tcp::resolver resolver(context->ios);
      boost::system::error_code ec;
      auto result = resolver.resolve(tcp::v4(), url.server, url.port, ec);
      if (ec) {
         EOS_THROW(fail_to_resolve_host, "Error resolving \"${server}:${port}\" : ${m}", ("server", url.server)("port",url.port)("m",ec.message()));
      }

      // non error results are guaranteed to return a non-empty range
      vector<string> resolved_addresses;
      resolved_addresses.reserve(result.size());
      optional<uint16_t> resolved_port;
      bool is_loopback = true;

      for(const auto& r : result) {
         const auto& addr = r.endpoint().address();
         if (addr.is_v6()) continue;
         uint16_t port = r.endpoint().port();
         resolved_addresses.emplace_back(addr.to_string());
         is_loopback = is_loopback && addr.is_loopback();

         if (resolved_port) {
            EOS_ASSERT(*resolved_port == port, resolved_to_multiple_ports, "Service name \"${port}\" resolved to multiple ports and this is not supported!", ("port",url.port));
         } else {
            resolved_port = port;
         }
      }

      return resolved_url(url, std::move(resolved_addresses), *resolved_port, is_loopback);
   }

   string format_host_header(const resolved_url& url) {
      // common practice is to only make the port explicit when it is the non-default port
      if (
         (url.scheme == "https" && url.resolved_port == 443) ||
         (url.scheme == "http" && url.resolved_port == 80)
      ) {
         return url.server;
      } else {
         return url.server + ":" + url.port;
      }
   }

   fc::variant do_http_call( const connection_param& cp,
                             const fc::variant& postdata,
                             bool print_request,
                             bool print_response ) {
   std::string postjson;
   if( !postdata.is_null() ) {
      postjson = print_request ? fc::json::to_pretty_string( postdata ) : fc::json::to_string( postdata, fc::time_point::maximum() );
   }

   const auto& url = cp.url;

   boost::asio::streambuf request;
   std::ostream request_stream(&request);
   auto host_header_value = format_host_header(url);
   request_stream << "POST " << url.path << " HTTP/1.0\r\n";
   request_stream << "Host: " << host_header_value << "\r\n";
   request_stream << "content-length: " << postjson.size() << "\r\n";
   request_stream << "Accept: */*\r\n";
   request_stream << "Connection: close\r\n";
   // append more customized headers
   std::vector<string>::iterator itr;
   for (itr = cp.headers.begin(); itr != cp.headers.end(); itr++) {
      request_stream << *itr << "\r\n";
   }
   request_stream << "\r\n";
   request_stream << postjson;

   if ( print_request ) {
      string s(request.size(), '\0');
      buffer_copy(boost::asio::buffer(s), request.data());
      std::cerr << "REQUEST:" << std::endl
                << "---------------------" << std::endl
                << s << std::endl
                << "---------------------" << std::endl;
   }

   unsigned int status_code;
   std::string re;

   try {
      if(url.scheme == "unix") {
         boost::asio::local::stream_protocol::socket unix_socket(cp.context->ios);
         unix_socket.connect(boost::asio::local::stream_protocol::endpoint(url.server));
         re = do_txrx(unix_socket, request, status_code);
      }
      else if(url.scheme == "http") {
         tcp::socket socket(cp.context->ios);
         do_connect(socket, url);
         re = do_txrx(socket, request, status_code);
      }
      else { //https
         boost::asio::ssl::context ssl_context(boost::asio::ssl::context::sslv23_client);
         fc::add_platform_root_cas_to_context(ssl_context);

         boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket(cp.context->ios, ssl_context);
         SSL_set_tlsext_host_name(socket.native_handle(), url.server.c_str());
         if(cp.verify_cert) {
            socket.set_verify_mode(boost::asio::ssl::verify_peer);
            socket.set_verify_callback(boost::asio::ssl::rfc2818_verification(url.server));
         }
         do_connect(socket.next_layer(), url);
         socket.handshake(boost::asio::ssl::stream_base::client);
         re = do_txrx(socket, request, status_code);
         //try and do a clean shutdown; but swallow if this fails (other side could have already gave TCP the ax)
         try {socket.shutdown();} catch(...) {}
      }
   } catch ( invalid_http_request& e ) {
      e.append_log( FC_LOG_MESSAGE( info, "Please verify this url is valid: ${url}", ("url", url.scheme + "://" + url.server + ":" + url.port + url.path) ) );
      e.append_log( FC_LOG_MESSAGE( info, "If the condition persists, please contact the RPC server administrator for ${server}!", ("server", url.server) ) );
      throw;
   }

   const auto response_result = fc::json::from_string(re);
   if( print_response ) {
      std::cerr << "RESPONSE:" << std::endl
                << "---------------------" << std::endl
                << fc::json::to_pretty_string( response_result ) << std::endl
                << "---------------------" << std::endl;
   }
   if( status_code == 200 || status_code == 201 || status_code == 202 ) {
      return response_result;
   } else if( status_code == 404 ) {
      // Unknown endpoint
      if (url.path.compare(0, chain_func_base.size(), chain_func_base) == 0) {
         throw chain::missing_chain_api_plugin_exception(FC_LOG_MESSAGE(error, "Chain API plugin is not enabled"));
      } else if (url.path.compare(0, wallet_func_base.size(), wallet_func_base) == 0) {
         throw chain::missing_wallet_api_plugin_exception(FC_LOG_MESSAGE(error, "Wallet is not available"));
      } else if (url.path.compare(0, history_func_base.size(), history_func_base) == 0) {
         throw chain::missing_history_api_plugin_exception(FC_LOG_MESSAGE(error, "History API plugin is not enabled"));
      } else if (url.path.compare(0, net_func_base.size(), net_func_base) == 0) {
         throw chain::missing_net_api_plugin_exception(FC_LOG_MESSAGE(error, "Net API plugin is not enabled"));
      }
   } else {
      auto &&error_info = response_result.as<eosio::error_results>().error;
      // Construct fc exception from error
      const auto &error_details = error_info.details;

      fc::log_messages logs;
      for (auto itr = error_details.begin(); itr != error_details.end(); itr++) {
         const auto& context = fc::log_context(fc::log_level::error, itr->file.data(), itr->line_number, itr->method.data());
         logs.emplace_back(fc::log_message(context, itr->message));
      }

      throw fc::exception(logs, error_info.code, error_info.name, error_info.what);
   }

   EOS_ASSERT( status_code == 200, http_request_fail, "Error code ${c}\n: ${msg}\n", ("c", status_code)("msg", re) );
   return response_result;
   }
}}}
<|endoftext|>


<commit_before>//=======================================================================
// Copyright Baptiste Wicht 2013-2016.
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://www.opensource.org/licenses/MIT)
//=======================================================================

#include <tlib/system.hpp>
#include <tlib/errors.hpp>
#include <tlib/print.hpp>
#include <tlib/net.hpp>
#include <tlib/dns.hpp>

static constexpr const size_t timeout_ms = 5000;
static constexpr const size_t server_timeout_ms = 10000;

namespace {

std::string ip_to_str(tlib::ip::address ip){
    std::string value;
    value += std::to_string(ip(0));
    value += '.';
    value += std::to_string(ip(1));
    value += '.';
    value += std::to_string(ip(2));
    value += '.';
    value += std::to_string(ip(3));
    return value;
}

int netcat_tcp_client(const tlib::ip::address& server, size_t port){
    auto ip_str = ip_to_str(server);
    tlib::printf("netcat TCP client %s:%u\n", ip_str.c_str(), port);

    tlib::socket sock(tlib::socket_domain::AF_INET, tlib::socket_type::STREAM, tlib::socket_protocol::TCP);

    sock.connect(server, port);
    sock.listen(true);

    if (!sock) {
        tlib::printf("nc: socket error: %s\n", std::error_message(sock.error()));
        return 1;
    }

    // Send a packet to the server

    {
        auto message = "THOR";

        sock.send(message, 4);

        if (!sock) {
            tlib::printf("nc: socket error: %s\n", std::error_message(sock.error()));
            return 1;
        }
    }

    // Listen for packets from the server

    char message_buffer[2049];

    auto before = tlib::ms_time();
    auto after  = before;

    while (true) {
        // Make sure we don't wait for more than the timeout
        if (after > before + timeout_ms) {
            break;
        }

        auto remaining = timeout_ms - (after - before);

        auto size = sock.receive(message_buffer, 2048, remaining);
        if (!sock) {
            if (sock.error() == std::ERROR_SOCKET_TIMEOUT) {
                sock.clear();
                break;
            }

            tlib::printf("nc: receive error: %s\n", std::error_message(sock.error()));
            return 1;
        } else {
            message_buffer[size] = '\0';
            tlib::print(message_buffer);
        }

        after = tlib::ms_time();
    }

    sock.listen(false);

    if (!sock) {
        tlib::printf("nc: socket error: %s\n", std::error_message(sock.error()));
        return 1;
    }

    return 0;
}

int netcat_udp_client(const tlib::ip::address& server, size_t port){
    auto ip_str = ip_to_str(server);
    tlib::printf("netcat UDP client %s:%u\n", ip_str.c_str(), port);

    tlib::socket sock(tlib::socket_domain::AF_INET, tlib::socket_type::DGRAM, tlib::socket_protocol::UDP);

    sock.client_bind(server, port);
    sock.listen(true);

    if (!sock) {
        tlib::printf("nc: listen error: %s\n", std::error_message(sock.error()));
        return 1;
    }

    // Send a packet to the server

    {
        auto message = "THOR";

        sock.send(message, 4);

        if (!sock) {
            tlib::printf("nc: socket error: %s\n", std::error_message(sock.error()));
            return 1;
        }
    }

    // Listen for packets from the server

    char message_buffer[2049];

    auto before = tlib::ms_time();
    auto after  = before;

    while (true) {
        // Make sure we don't wait for more than the timeout
        if (after > before + timeout_ms) {
            break;
        }

        auto remaining = timeout_ms - (after - before);

        auto size = sock.receive(message_buffer, 2048, remaining);
        if (!sock) {
            if (sock.error() == std::ERROR_SOCKET_TIMEOUT) {
                sock.clear();
                break;
            }

            tlib::printf("nc: receive error: %s\n", std::error_message(sock.error()));
            return 1;
        } else {
            message_buffer[size] = '\0';
            tlib::print(message_buffer);
        }

        after = tlib::ms_time();
    }

    sock.listen(false);

    if (!sock) {
        tlib::printf("nc: listen error: %s\n", std::error_message(sock.error()));
        return 1;
    }

    return 0;
}

int netcat_tcp_server(const tlib::ip::address& local, size_t port){
    auto ip_str = ip_to_str(local);
    tlib::printf("netcat TCP server %s:%u\n", ip_str.c_str(), port);

    tlib::socket sock(tlib::socket_domain::AF_INET, tlib::socket_type::STREAM, tlib::socket_protocol::TCP);

    sock.server_start(local, port);

    if (!sock) {
        tlib::printf("nc: socket error: %s\n", std::error_message(sock.error()));
        return 1;
    }

    auto child = sock.accept();

    if (!sock) {
        tlib::printf("nc: accept error: %s\n", std::error_message(sock.error()));
        return 1;
    }

    if (!child) {
        tlib::printf("nc: accept error: %s\n", std::error_message(sock.error()));
        return 1;
    }

    child.listen(true);

    tlib::printf("nc: Received connection\n");

    if (!child) {
        tlib::printf("nc: listen error: %s\n", std::error_message(sock.error()));
        return 1;
    }

    // Listen for packets from the client

    char message_buffer[2049];

    auto before = tlib::ms_time();
    auto after  = before;

    while (true) {
        // Make sure we don't wait for more than the timeout
        if (after > before + server_timeout_ms) {
            break;
        }

        auto remaining = server_timeout_ms - (after - before);

        tlib::printf("nc: Wait for message\n");

        auto size = child.receive(message_buffer, 2048, remaining);
        if (!sock) {
            if (sock.error() == std::ERROR_SOCKET_TIMEOUT) {
                sock.clear();
                break;
            }

            tlib::printf("nc: receive error: %s\n", std::error_message(sock.error()));
            return 1;
        } else {
            message_buffer[size] = '\0';
            tlib::print(message_buffer);

            tlib::printf("nc: Send response\n");

            sock.send(message_buffer, size);

            if (!sock) {
                tlib::printf("nc: send error: %s\n", std::error_message(sock.error()));
                return 1;
            }
        }

        after = tlib::ms_time();
    }

    tlib::printf("nc: done... disconnecting\n");

    child.listen(false);

    if (!child) {
        tlib::printf("nc: listen error: %s\n", std::error_message(sock.error()));
        return 1;
    }

    return 0;
}

int netcat_udp_server(const tlib::ip::address& local, size_t port){
    auto ip_str = ip_to_str(local);
    tlib::printf("netcat UDP server %s:%u\n", ip_str.c_str(), port);

    tlib::socket sock(tlib::socket_domain::AF_INET, tlib::socket_type::DGRAM, tlib::socket_protocol::UDP);

    sock.server_bind(local, port);
    sock.listen(true);

    if (!sock) {
        tlib::printf("nc: listen error: %s\n", std::error_message(sock.error()));
        return 1;
    }

    // Listen for packets from the client

    char message_buffer[2049];

    auto before = tlib::ms_time();
    auto after  = before;

    while (true) {
        // Make sure we don't wait for more than the timeout
        if (after > before + server_timeout_ms) {
            break;
        }

        auto remaining = server_timeout_ms - (after - before);

        tlib::inet_address address;

        auto size = sock.receive_from(message_buffer, 2048, remaining, &address);
        if (!sock) {
            if (sock.error() == std::ERROR_SOCKET_TIMEOUT) {
                sock.clear();
                break;
            }

            tlib::printf("nc: receive_from error: %s\n", std::error_message(sock.error()));
            return 1;
        } else {
            message_buffer[size] = '\0';
            tlib::print(message_buffer);

            sock.send_to(message_buffer, size, &address);

            if (!sock) {
                tlib::printf("nc: send_to error: %s\n", std::error_message(sock.error()));
                return 1;
            }
        }

        after = tlib::ms_time();
    }

    sock.listen(false);

    if (!sock) {
        tlib::printf("nc: listen error: %s\n", std::error_message(sock.error()));
        return 1;
    }

    return 0;
}

} // end of anonymous namespace

int main(int argc, char* argv[]) {

    bool udp = false;
    bool server = false;

    size_t i = 1;
    for(; i < size_t(argc); ++i){
        std::string param(argv[i]);

        if(param == "-u"){
            udp = true;
        } else if(param == "-l"){
            server = true;
        } else {
            break;
        }
    }

    if(server){
        if (argc - i != 2) {
            tlib::print_line("usage: nc -l local port");
            return 1;
        }

        std::string local(argv[i]);
        std::string port_str(argv[i + 1]);

        auto port = std::atoui(port_str);

        auto ip_parts = std::split(local, '.');

        if (ip_parts.size() != 4) {
            tlib::print_line("Invalid address IP for the local interface");
            return 1;
        }

        auto local_ip = tlib::ip::make_address(std::atoui(ip_parts[0]), std::atoui(ip_parts[1]), std::atoui(ip_parts[2]), std::atoui(ip_parts[3]));

        if (udp) {
            return netcat_udp_server(local_ip, port);
        } else {
            return netcat_tcp_server(local_ip, port);
        }
    } else {
        if (argc - i != 2) {
            tlib::print_line("usage: nc server port");
            return 1;
        }

        std::string server(argv[i]);
        std::string port_str(argv[i + 1]);

        auto port = std::atoui(port_str);

        auto ip_parts = std::split(server, '.');

        if (ip_parts.size() != 4) {
            tlib::print_line("Invalid address IP for the server");
            return 1;
        }

        auto server_ip = tlib::ip::make_address(std::atoui(ip_parts[0]), std::atoui(ip_parts[1]), std::atoui(ip_parts[2]), std::atoui(ip_parts[3]));

        if (udp) {
            return netcat_udp_client(server_ip, port);
        } else {
            return netcat_tcp_client(server_ip, port);
        }
    }
}
<commit_msg>Better messaging<commit_after>//=======================================================================
// Copyright Baptiste Wicht 2013-2016.
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://www.opensource.org/licenses/MIT)
//=======================================================================

#include <tlib/system.hpp>
#include <tlib/errors.hpp>
#include <tlib/print.hpp>
#include <tlib/net.hpp>
#include <tlib/dns.hpp>

static constexpr const size_t timeout_ms = 5000;
static constexpr const size_t server_timeout_ms = 10000;

namespace {

std::string ip_to_str(tlib::ip::address ip){
    std::string value;
    value += std::to_string(ip(0));
    value += '.';
    value += std::to_string(ip(1));
    value += '.';
    value += std::to_string(ip(2));
    value += '.';
    value += std::to_string(ip(3));
    return value;
}

int netcat_tcp_client(const tlib::ip::address& server, size_t port){
    auto ip_str = ip_to_str(server);
    tlib::printf("netcat TCP client %s:%u\n", ip_str.c_str(), port);

    tlib::socket sock(tlib::socket_domain::AF_INET, tlib::socket_type::STREAM, tlib::socket_protocol::TCP);

    sock.connect(server, port);
    sock.listen(true);

    if (!sock) {
        tlib::printf("nc: socket error: %s\n", std::error_message(sock.error()));
        return 1;
    }

    // Send a packet to the server

    {
        auto message = "THOR";

        sock.send(message, 4);

        if (!sock) {
            tlib::printf("nc: socket error: %s\n", std::error_message(sock.error()));
            return 1;
        }
    }

    // Listen for packets from the server

    char message_buffer[2049];

    auto before = tlib::ms_time();
    auto after  = before;

    while (true) {
        // Make sure we don't wait for more than the timeout
        if (after > before + timeout_ms) {
            break;
        }

        auto remaining = timeout_ms - (after - before);

        auto size = sock.receive(message_buffer, 2048, remaining);
        if (!sock) {
            if (sock.error() == std::ERROR_SOCKET_TIMEOUT) {
                sock.clear();
                break;
            }

            tlib::printf("nc: receive error: %s\n", std::error_message(sock.error()));
            return 1;
        } else {
            message_buffer[size] = '\0';
            tlib::print(message_buffer);
        }

        after = tlib::ms_time();
    }

    sock.listen(false);

    if (!sock) {
        tlib::printf("nc: socket error: %s\n", std::error_message(sock.error()));
        return 1;
    }

    return 0;
}

int netcat_udp_client(const tlib::ip::address& server, size_t port){
    auto ip_str = ip_to_str(server);
    tlib::printf("netcat UDP client %s:%u\n", ip_str.c_str(), port);

    tlib::socket sock(tlib::socket_domain::AF_INET, tlib::socket_type::DGRAM, tlib::socket_protocol::UDP);

    sock.client_bind(server, port);
    sock.listen(true);

    if (!sock) {
        tlib::printf("nc: listen error: %s\n", std::error_message(sock.error()));
        return 1;
    }

    // Send a packet to the server

    {
        auto message = "THOR";

        sock.send(message, 4);

        if (!sock) {
            tlib::printf("nc: socket error: %s\n", std::error_message(sock.error()));
            return 1;
        }
    }

    // Listen for packets from the server

    char message_buffer[2049];

    auto before = tlib::ms_time();
    auto after  = before;

    while (true) {
        // Make sure we don't wait for more than the timeout
        if (after > before + timeout_ms) {
            break;
        }

        auto remaining = timeout_ms - (after - before);

        auto size = sock.receive(message_buffer, 2048, remaining);
        if (!sock) {
            if (sock.error() == std::ERROR_SOCKET_TIMEOUT) {
                sock.clear();
                break;
            }

            tlib::printf("nc: receive error: %s\n", std::error_message(sock.error()));
            return 1;
        } else {
            message_buffer[size] = '\0';
            tlib::print(message_buffer);
        }

        after = tlib::ms_time();
    }

    sock.listen(false);

    if (!sock) {
        tlib::printf("nc: listen error: %s\n", std::error_message(sock.error()));
        return 1;
    }

    return 0;
}

int netcat_tcp_server(const tlib::ip::address& local, size_t port){
    auto ip_str = ip_to_str(local);
    tlib::printf("netcat TCP server %s:%u\n", ip_str.c_str(), port);

    tlib::socket sock(tlib::socket_domain::AF_INET, tlib::socket_type::STREAM, tlib::socket_protocol::TCP);

    sock.server_start(local, port);

    if (!sock) {
        tlib::printf("nc: socket error: %s\n", std::error_message(sock.error()));
        return 1;
    }

    auto child = sock.accept();

    if (!sock) {
        tlib::printf("nc: accept error: %s\n", std::error_message(sock.error()));
        return 1;
    }

    if (!child) {
        tlib::printf("nc: accept error: %s\n", std::error_message(sock.error()));
        return 1;
    }

    child.listen(true);

    tlib::printf("nc: Received connection\n");

    if (!child) {
        tlib::printf("nc: listen error: %s\n", std::error_message(sock.error()));
        return 1;
    }

    // Listen for packets from the client

    char message_buffer[2049];

    auto before = tlib::ms_time();
    auto after  = before;

    while (true) {
        // Make sure we don't wait for more than the timeout
        if (after > before + server_timeout_ms) {
            break;
        }

        auto remaining = server_timeout_ms - (after - before);

        tlib::printf("nc: Wait for message\n");

        auto size = child.receive(message_buffer, 2048, remaining);
        if (!sock) {
            if (sock.error() == std::ERROR_SOCKET_TIMEOUT) {
                sock.clear();
                break;
            }

            tlib::printf("nc: receive error: %s\n", std::error_message(sock.error()));
            return 1;
        } else {
            tlib::printf("nc: received message of size %u\n", size);

            message_buffer[size] = '\0';
            tlib::print(message_buffer);

            tlib::printf("nc: Send response\n");

            sock.send(message_buffer, size);

            if (!sock) {
                tlib::printf("nc: send error: %s\n", std::error_message(sock.error()));
                return 1;
            }
        }

        after = tlib::ms_time();
    }

    tlib::printf("nc: done... disconnecting\n");

    child.listen(false);

    if (!child) {
        tlib::printf("nc: listen error: %s\n", std::error_message(sock.error()));
        return 1;
    }

    return 0;
}

int netcat_udp_server(const tlib::ip::address& local, size_t port){
    auto ip_str = ip_to_str(local);
    tlib::printf("netcat UDP server %s:%u\n", ip_str.c_str(), port);

    tlib::socket sock(tlib::socket_domain::AF_INET, tlib::socket_type::DGRAM, tlib::socket_protocol::UDP);

    sock.server_bind(local, port);
    sock.listen(true);

    if (!sock) {
        tlib::printf("nc: listen error: %s\n", std::error_message(sock.error()));
        return 1;
    }

    // Listen for packets from the client

    char message_buffer[2049];

    auto before = tlib::ms_time();
    auto after  = before;

    while (true) {
        // Make sure we don't wait for more than the timeout
        if (after > before + server_timeout_ms) {
            break;
        }

        auto remaining = server_timeout_ms - (after - before);

        tlib::inet_address address;

        auto size = sock.receive_from(message_buffer, 2048, remaining, &address);
        if (!sock) {
            if (sock.error() == std::ERROR_SOCKET_TIMEOUT) {
                sock.clear();
                break;
            }

            tlib::printf("nc: receive_from error: %s\n", std::error_message(sock.error()));
            return 1;
        } else {
            message_buffer[size] = '\0';
            tlib::print(message_buffer);

            sock.send_to(message_buffer, size, &address);

            if (!sock) {
                tlib::printf("nc: send_to error: %s\n", std::error_message(sock.error()));
                return 1;
            }
        }

        after = tlib::ms_time();
    }

    sock.listen(false);

    if (!sock) {
        tlib::printf("nc: listen error: %s\n", std::error_message(sock.error()));
        return 1;
    }

    return 0;
}

} // end of anonymous namespace

int main(int argc, char* argv[]) {

    bool udp = false;
    bool server = false;

    size_t i = 1;
    for(; i < size_t(argc); ++i){
        std::string param(argv[i]);

        if(param == "-u"){
            udp = true;
        } else if(param == "-l"){
            server = true;
        } else {
            break;
        }
    }

    if(server){
        if (argc - i != 2) {
            tlib::print_line("usage: nc -l local port");
            return 1;
        }

        std::string local(argv[i]);
        std::string port_str(argv[i + 1]);

        auto port = std::atoui(port_str);

        auto ip_parts = std::split(local, '.');

        if (ip_parts.size() != 4) {
            tlib::print_line("Invalid address IP for the local interface");
            return 1;
        }

        auto local_ip = tlib::ip::make_address(std::atoui(ip_parts[0]), std::atoui(ip_parts[1]), std::atoui(ip_parts[2]), std::atoui(ip_parts[3]));

        if (udp) {
            return netcat_udp_server(local_ip, port);
        } else {
            return netcat_tcp_server(local_ip, port);
        }
    } else {
        if (argc - i != 2) {
            tlib::print_line("usage: nc server port");
            return 1;
        }

        std::string server(argv[i]);
        std::string port_str(argv[i + 1]);

        auto port = std::atoui(port_str);

        auto ip_parts = std::split(server, '.');

        if (ip_parts.size() != 4) {
            tlib::print_line("Invalid address IP for the server");
            return 1;
        }

        auto server_ip = tlib::ip::make_address(std::atoui(ip_parts[0]), std::atoui(ip_parts[1]), std::atoui(ip_parts[2]), std::atoui(ip_parts[3]));

        if (udp) {
            return netcat_udp_client(server_ip, port);
        } else {
            return netcat_tcp_client(server_ip, port);
        }
    }
}
<|endoftext|>


<commit_before>/**
 *  @file
 *  @copyright defined in eosio/LICENSE.txt
 */
#include <appbase/application.hpp>

#include <eosio/chain_plugin/chain_plugin.hpp>
#include <eosio/http_plugin/http_plugin.hpp>
#include <eosio/net_plugin/net_plugin.hpp>
#include <eosio/producer_plugin/producer_plugin.hpp>

#include <fc/log/logger_config.hpp>
#include <fc/log/appender.hpp>
#include <fc/exception/exception.hpp>

#include <boost/dll/runtime_symbol_info.hpp>
#include <boost/exception/diagnostic_information.hpp>

#include "config.hpp"

using namespace appbase;
using namespace eosio;

namespace fc {
   std::unordered_map<std::string,appender::ptr>& get_appender_map();
}

namespace detail {

void configure_logging(const bfs::path& config_path)
{
   try {
      try {
         fc::configure_logging(config_path);
      } catch (...) {
         elog("Error reloading logging.json");
         throw;
      }
   } catch (const fc::exception& e) {
      elog("${e}", ("e",e.to_detail_string()));
   } catch (const boost::exception& e) {
      elog("${e}", ("e",boost::diagnostic_information(e)));
   } catch (const std::exception& e) {
      elog("${e}", ("e",e.what()));
   } catch (...) {
      // empty
   }
}

} // namespace detail

void logging_conf_handler()
{
   ilog("Received HUP.  Reloading logging configuration.");
   auto config_path = app().get_logging_conf();
   if(fc::exists(config_path))
      ::detail::configure_logging(config_path);
   for(auto iter : fc::get_appender_map())
      iter.second->initialize(app().get_io_service());
}

void initialize_logging()
{
   auto config_path = app().get_logging_conf();
   if(fc::exists(config_path))
     fc::configure_logging(config_path); // intentionally allowing exceptions to escape
   for(auto iter : fc::get_appender_map())
     iter.second->initialize(app().get_io_service());

   app().set_sighup_callback(logging_conf_handler);
}

enum return_codes {
   OTHER_FAIL        = -2,
   INITIALIZE_FAIL   = -1,
   SUCCESS           = 0,
   BAD_ALLOC         = 1,
   DATABASE_DIRTY    = 2,
   FIXED_REVERSIBLE  = 3,
   EXTRACTED_GENESIS = 4,
   NODE_MANAGEMENT_SUCCESS = 5
};

int main(int argc, char** argv)
{
   try {
      app().set_version(eosio::nodeos::config::version);

      auto root = fc::app_path();
      app().set_default_data_dir(root / "eosio" / nodeos::config::node_executable_name / "data" );
      app().set_default_config_dir(root / "eosio" / nodeos::config::node_executable_name / "config" );
      http_plugin::set_defaults({
         .default_unix_socket_path = "",
         .default_http_port = 8888
      });
      if(!app().initialize<chain_plugin, net_plugin, producer_plugin>(argc, argv))
         return INITIALIZE_FAIL;
      initialize_logging();
      ilog("${name} version ${ver}", ("name", nodeos::config::node_executable_name)("ver", app().version_string()));
      ilog("${name} using configuration file ${c}", ("name", nodeos::config::node_executable_name)("c", app().full_config_file_path().string()));
      ilog("${name} data directory is ${d}", ("name", nodeos::config::node_executable_name)("d", app().data_dir().string()));
      app().startup();
      app().exec();
   } catch( const extract_genesis_state_exception& e ) {
      return EXTRACTED_GENESIS;
   } catch( const fixed_reversible_db_exception& e ) {
      return FIXED_REVERSIBLE;
   } catch( const node_management_success& e ) {
      return NODE_MANAGEMENT_SUCCESS;
   } catch( const fc::exception& e ) {
      if( e.code() == fc::std_exception_code ) {
         if( e.top_message().find( "database dirty flag set" ) != std::string::npos ) {
            elog( "database dirty flag set (likely due to unclean shutdown): replay required" );
            return DATABASE_DIRTY;
         } else if( e.top_message().find( "database metadata dirty flag set" ) != std::string::npos ) {
            elog( "database metadata dirty flag set (likely due to unclean shutdown): replay required" );
            return DATABASE_DIRTY;
         }
      }
      elog( "${e}", ("e", e.to_detail_string()));
      return OTHER_FAIL;
   } catch( const boost::interprocess::bad_alloc& e ) {
      elog("bad alloc");
      return BAD_ALLOC;
   } catch( const boost::exception& e ) {
      elog("${e}", ("e",boost::diagnostic_information(e)));
      return OTHER_FAIL;
   } catch( const std::runtime_error& e ) {
      if( std::string(e.what()) == "database dirty flag set" ) {
         elog( "database dirty flag set (likely due to unclean shutdown): replay required" );
         return DATABASE_DIRTY;
      } else if( std::string(e.what()) == "database metadata dirty flag set" ) {
         elog( "database metadata dirty flag set (likely due to unclean shutdown): replay required" );
         return DATABASE_DIRTY;
      } else {
         elog( "${e}", ("e",e.what()));
      }
      return OTHER_FAIL;
   } catch( const std::exception& e ) {
      elog("${e}", ("e",e.what()));
      return OTHER_FAIL;
   } catch( ... ) {
      elog("unknown exception");
      return OTHER_FAIL;
   }

   ilog("${name} successfully exiting", ("name", nodeos::config::node_executable_name));
   return SUCCESS;
}
<commit_msg>Call new fc::log_config::initialize_appenders<commit_after>/**
 *  @file
 *  @copyright defined in eosio/LICENSE.txt
 */
#include <appbase/application.hpp>

#include <eosio/chain_plugin/chain_plugin.hpp>
#include <eosio/http_plugin/http_plugin.hpp>
#include <eosio/net_plugin/net_plugin.hpp>
#include <eosio/producer_plugin/producer_plugin.hpp>

#include <fc/log/logger_config.hpp>
#include <fc/log/appender.hpp>
#include <fc/exception/exception.hpp>

#include <boost/dll/runtime_symbol_info.hpp>
#include <boost/exception/diagnostic_information.hpp>

#include "config.hpp"

using namespace appbase;
using namespace eosio;

namespace detail {

void configure_logging(const bfs::path& config_path)
{
   try {
      try {
         fc::configure_logging(config_path);
      } catch (...) {
         elog("Error reloading logging.json");
         throw;
      }
   } catch (const fc::exception& e) {
      elog("${e}", ("e",e.to_detail_string()));
   } catch (const boost::exception& e) {
      elog("${e}", ("e",boost::diagnostic_information(e)));
   } catch (const std::exception& e) {
      elog("${e}", ("e",e.what()));
   } catch (...) {
      // empty
   }
}

} // namespace detail

void logging_conf_handler()
{
   auto config_path = app().get_logging_conf();
   ilog("Received HUP.  Reloading logging configuration from ${p}.", ("p", config_path.string()));
   if(fc::exists(config_path))
      ::detail::configure_logging(config_path);
   fc::log_config::initialize_appenders( app().get_io_service() );
}

void initialize_logging()
{
   auto config_path = app().get_logging_conf();
   if(fc::exists(config_path))
     fc::configure_logging(config_path); // intentionally allowing exceptions to escape
   fc::log_config::initialize_appenders( app().get_io_service() );

   app().set_sighup_callback(logging_conf_handler);
}

enum return_codes {
   OTHER_FAIL        = -2,
   INITIALIZE_FAIL   = -1,
   SUCCESS           = 0,
   BAD_ALLOC         = 1,
   DATABASE_DIRTY    = 2,
   FIXED_REVERSIBLE  = 3,
   EXTRACTED_GENESIS = 4,
   NODE_MANAGEMENT_SUCCESS = 5
};

int main(int argc, char** argv)
{
   try {
      app().set_version(eosio::nodeos::config::version);

      auto root = fc::app_path();
      app().set_default_data_dir(root / "eosio" / nodeos::config::node_executable_name / "data" );
      app().set_default_config_dir(root / "eosio" / nodeos::config::node_executable_name / "config" );
      http_plugin::set_defaults({
         .default_unix_socket_path = "",
         .default_http_port = 8888
      });
      if(!app().initialize<chain_plugin, net_plugin, producer_plugin>(argc, argv))
         return INITIALIZE_FAIL;
      initialize_logging();
      ilog("${name} version ${ver}", ("name", nodeos::config::node_executable_name)("ver", app().version_string()));
      ilog("${name} using configuration file ${c}", ("name", nodeos::config::node_executable_name)("c", app().full_config_file_path().string()));
      ilog("${name} data directory is ${d}", ("name", nodeos::config::node_executable_name)("d", app().data_dir().string()));
      app().startup();
      app().exec();
   } catch( const extract_genesis_state_exception& e ) {
      return EXTRACTED_GENESIS;
   } catch( const fixed_reversible_db_exception& e ) {
      return FIXED_REVERSIBLE;
   } catch( const node_management_success& e ) {
      return NODE_MANAGEMENT_SUCCESS;
   } catch( const fc::exception& e ) {
      if( e.code() == fc::std_exception_code ) {
         if( e.top_message().find( "database dirty flag set" ) != std::string::npos ) {
            elog( "database dirty flag set (likely due to unclean shutdown): replay required" );
            return DATABASE_DIRTY;
         } else if( e.top_message().find( "database metadata dirty flag set" ) != std::string::npos ) {
            elog( "database metadata dirty flag set (likely due to unclean shutdown): replay required" );
            return DATABASE_DIRTY;
         }
      }
      elog( "${e}", ("e", e.to_detail_string()));
      return OTHER_FAIL;
   } catch( const boost::interprocess::bad_alloc& e ) {
      elog("bad alloc");
      return BAD_ALLOC;
   } catch( const boost::exception& e ) {
      elog("${e}", ("e",boost::diagnostic_information(e)));
      return OTHER_FAIL;
   } catch( const std::runtime_error& e ) {
      if( std::string(e.what()) == "database dirty flag set" ) {
         elog( "database dirty flag set (likely due to unclean shutdown): replay required" );
         return DATABASE_DIRTY;
      } else if( std::string(e.what()) == "database metadata dirty flag set" ) {
         elog( "database metadata dirty flag set (likely due to unclean shutdown): replay required" );
         return DATABASE_DIRTY;
      } else {
         elog( "${e}", ("e",e.what()));
      }
      return OTHER_FAIL;
   } catch( const std::exception& e ) {
      elog("${e}", ("e",e.what()));
      return OTHER_FAIL;
   } catch( ... ) {
      elog("unknown exception");
      return OTHER_FAIL;
   }

   ilog("${name} successfully exiting", ("name", nodeos::config::node_executable_name));
   return SUCCESS;
}
<|endoftext|>


<commit_before>//
// Created by kyle on 10/12/16.
//
#include "tansa/jocsParser.h"
namespace tansa {
const std::string Jocs::HOME_KEY = "startPosition";
const std::string Jocs::DRONE_KEY = "drones";
const std::string Jocs::ID_KEY = "id";
const std::string Jocs::CHOREOGRAPHY_KEY = "chor";
const std::string Jocs::STARTPOS_KEY = "startPointCenter";
const std::string Jocs::ENDPOS_KEY = "endPointCenter";
const std::string Jocs::DURATION_KEY = "duration";
const std::string Jocs::ACTION_ROOT_KEY = "action";
const std::string Jocs::ACTION_TIME_KEY = "time";
const std::string Jocs::ACTION_TYPE_KEY = "type";
const std::string Jocs::DRONE_ARRAY_KEY = "drones";
const std::string Jocs::ACTION_DATA_KEY = "data";
const std::string Jocs::CIRCLE_ORIGIN_KEY = "originPointCenter";
const std::string Jocs::CIRCLE_RADIUS_KEY = "radius";
const std::string Jocs::CIRCLE_THETA1_KEY = "theta1";
const std::string Jocs::CIRCLE_THETA2_KEY = "theta2";

std::vector< vector<Action*> > Jocs::Parse(const std::string &jocsPath) {
	ifstream jocsStream(jocsPath);
	std::string jocsData((std::istreambuf_iterator<char>(jocsStream)), std::istreambuf_iterator<char>());
	auto rawJson = nlohmann::json::parse(jocsData);
	std::vector< vector<Action*> > actions;
	parseActions(rawJson, actions);
	return actions;
}

void Jocs::parseActions(const nlohmann::json &data, std::vector< vector<Action*> >& actions) {
	auto actionsJson = data[CHOREOGRAPHY_KEY];
	//This must be an array
	assert(actionsJson.is_array());

	auto length = actionsJson.size();
	actions.reserve(length);

	// For each timeslot, gather actions in a vector and add as entry in "actions" 2d vector
	for(int i = 0; i < length; i++){
		parseAction(actionsJson[i], actions);
	}

	// Go back and review actions such that transitions can be created with polynomial trajectories
	for(int i = 0; i < actions.size(); i++){

		// Each entry in "actions" has a vector full of the actions that occur at that time
		for (int j=0; j < actions[i].size(); j++){
			if(!actions[i][j]->IsCalculated()){
				if(i == 0){
					//TODO: Handle the case of the first action being a transition where our initial velocity and acceleration are 0 and position is equal to home.
				} else if (i == (actions[i].size() - 1)){
					//TODO: Handle the case of the last action being a transition, where our ending velocity and acceleration are 0 and destination is home.
				} else{
					// Calculate previous and next motion to generate what's needed for the transition
					MotionAction *ref = FindPreviousAction(actions[i][j].GetDrone(), actions, i);
					MotionAction *next = FindNextAction(actions[i][j].GetDrone(), actions, i);
					// TODO: it should be possible that the previous time doesn't have the drone in it,
					// since different timeslots can command different drones. Must continue search to find drone.

					// Get states from the previous and next state that were found
					auto startState = ref->GetPathState(ref->GetEndTime());
					auto endState = next->GetPathState(next->GetStartTime());

					// Store start and end time from EmptyAction to carry over to MotionAction's trajectory
					double thisStart = actions[i][j]->GetStartTime();
					double thisEnd = actions[i][j]->GetEndTime();

					// Cleanup object and replace with a new MotionAction
					delete actions[i][j];
					actions[i][j] = new MotionAction(actions[i][j]->GetDrone(),
													 std::move(std::unique_ptr<PolynomialTrajectory>(
															 PolynomialTrajectory::compute(
																	 {startState.position, startState.velocity,
																	  startState.acceleration},
																	 thisStart,
																	 {endState.position, endState.velocity,
																	  endState.acceleration},
																	 thisEnd))));
				}
			}
		}
	}
}

void Jocs::parseAction(const nlohmann::json::reference data, std::vector<Action*>& actions){
	auto actionsArray = data[ACTION_ROOT_KEY];
	assert(actionsArray.is_array());
	double startTime = data[ACTION_TIME_KEY];
	for(int i = 0; i < actionsArray.size(); i++) {
		auto actionsArrayElement = actionsArray[i];
		double duration = actionsArrayElement[DURATION_KEY];
		unsigned type = convertToActionType(actionsArrayElement[ACTION_TYPE_KEY]);
		//TODO: Assuming no grouping right now. Will have to make this a loop to create actions for each drone and calc offset
		assert(actionsArrayElement[DRONE_ARRAY_KEY].is_array());
		unsigned drone = actionsArrayElement[DRONE_ARRAY_KEY][0];
		//Switch on type of Action
		switch (type) {
			//Transitional case: Put a placeholder action to be post-processed away
			case ActionTypes::Transition: {
				// Actual calculation will be processed after this loop
				// For now, there is no trajectory !
				actions.push_back(new EmptyAction(drone, startTime, startTime + duration));
				break;
			}
			//Simple line action
			case ActionTypes::Line: {
				Point start(
						actionsArrayElement[ACTION_DATA_KEY][STARTPOS_KEY][0],
						actionsArrayElement[ACTION_DATA_KEY][STARTPOS_KEY][1],
						actionsArrayElement[ACTION_DATA_KEY][STARTPOS_KEY][2]);

				Point end(
						actionsArrayElement[ACTION_DATA_KEY][ENDPOS_KEY][0],
						actionsArrayElement[ACTION_DATA_KEY][ENDPOS_KEY][1],
						actionsArrayElement[ACTION_DATA_KEY][ENDPOS_KEY][2]);

				actions.push_back(new MotionAction(
						drone, std::move(std::make_unique<LinearTrajectory>(start, startTime, end, startTime + duration))));
				break;
			}
			//Circle Action
			case ActionTypes::Circle: {
				Point origin(
						actionsArrayElement[ACTION_DATA_KEY][CIRCLE_ORIGIN_KEY][0],
						actionsArrayElement[ACTION_DATA_KEY][CIRCLE_ORIGIN_KEY][1],
						actionsArrayElement[ACTION_DATA_KEY][CIRCLE_ORIGIN_KEY][2]);
				double radius = actionsArrayElement[ACTION_DATA_KEY][CIRCLE_RADIUS_KEY];
				double theta1 = actionsArrayElement[ACTION_DATA_KEY][CIRCLE_THETA1_KEY];
				double theta2 = actionsArrayElement[ACTION_DATA_KEY][CIRCLE_THETA2_KEY];
				actions.push_back(new MotionAction(
						drone, std::move(std::make_unique<CircleTrajectory>(origin, radius, theta1, startTime, theta2, startTime + duration))));
				break;
			}
			default: {
				break;
			}
		}
	}
}

ActionTypes Jocs::convertToActionType(const std::string& data){
	if(data == "transition")
		return ActionTypes::Transition;
	else if (data == "line")
		return ActionTypes::Line;
	else if (data == "circle")
		return ActionTypes::Circle;
}

//TODO: maybe instead of returning a pointer we take it as a parameter which we modify?
MotionAction* Jocs::FindPreviousAction(DroneId id, std::vector< vector<Action*> >& actions, int currentLocation) {
	for(int i = currentLocation-1; i >= 0; i--){
		MotionAction* actionFound = nullptr;
		bool success = FindMotionForDrone(id, actions[i], actionFound);
		if (success) {
			return actionFound;
		}
	}
	// This means there never *was* a previous motion for this drone
	//TODO: maybe should throw exception here?
	return nullptr;
}

//TODO: maybe instead of returning a pointer we take it as a parameter which we modify?
MotionAction* Jocs::FindNextAction(DroneId id, std::vector< vector<Action*> >& actions, int currentLocation) {
	for(int i = currentLocation+1; i < actions.size(); i++){
		MotionAction* actionFound = nullptr;
		bool success = FindMotionForDrone(id, actions[i], actionFound);
		if (success) {
			return actionFound;
		}
	}
	// This means there never *will be* a next motion for this drone
	//TODO: maybe should throw exception here?
	return nullptr;
}

bool Jocs::FindMotionForDrone(DroneId id, vector<Action*> curActions, MotionAction& actionFound) {
	for(int i = 0; i < curActions.size(); i++){
		if (curActions[i].GetDrone() == id) {
			// We found the drone we were looking for *wink wink*
			actionFound = static_cast<MotionAction*>(curActions[i]);
			return true;
		}
	}
	return false; // This means that the previous time didn't have the drone in it.
}
}
<commit_msg>Delete incorrect comment<commit_after>//
// Created by kyle on 10/12/16.
//
#include "tansa/jocsParser.h"
namespace tansa {
const std::string Jocs::HOME_KEY = "startPosition";
const std::string Jocs::DRONE_KEY = "drones";
const std::string Jocs::ID_KEY = "id";
const std::string Jocs::CHOREOGRAPHY_KEY = "chor";
const std::string Jocs::STARTPOS_KEY = "startPointCenter";
const std::string Jocs::ENDPOS_KEY = "endPointCenter";
const std::string Jocs::DURATION_KEY = "duration";
const std::string Jocs::ACTION_ROOT_KEY = "action";
const std::string Jocs::ACTION_TIME_KEY = "time";
const std::string Jocs::ACTION_TYPE_KEY = "type";
const std::string Jocs::DRONE_ARRAY_KEY = "drones";
const std::string Jocs::ACTION_DATA_KEY = "data";
const std::string Jocs::CIRCLE_ORIGIN_KEY = "originPointCenter";
const std::string Jocs::CIRCLE_RADIUS_KEY = "radius";
const std::string Jocs::CIRCLE_THETA1_KEY = "theta1";
const std::string Jocs::CIRCLE_THETA2_KEY = "theta2";

std::vector< vector<Action*> > Jocs::Parse(const std::string &jocsPath) {
	ifstream jocsStream(jocsPath);
	std::string jocsData((std::istreambuf_iterator<char>(jocsStream)), std::istreambuf_iterator<char>());
	auto rawJson = nlohmann::json::parse(jocsData);
	std::vector< vector<Action*> > actions;
	parseActions(rawJson, actions);
	return actions;
}

void Jocs::parseActions(const nlohmann::json &data, std::vector< vector<Action*> >& actions) {
	auto actionsJson = data[CHOREOGRAPHY_KEY];
	//This must be an array
	assert(actionsJson.is_array());

	auto length = actionsJson.size();
	actions.reserve(length);

	// For each timeslot, gather actions in a vector and add as entry in "actions" 2d vector
	for(int i = 0; i < length; i++){
		parseAction(actionsJson[i], actions);
	}

	// Go back and review actions such that transitions can be created with polynomial trajectories
	for(int i = 0; i < actions.size(); i++){

		// Each entry in "actions" has a vector full of the actions that occur at that time
		for (int j=0; j < actions[i].size(); j++){
			if(!actions[i][j]->IsCalculated()){
				if(i == 0){
					//TODO: Handle the case of the first action being a transition where our initial velocity and acceleration are 0 and position is equal to home.
				} else if (i == (actions[i].size() - 1)){
					//TODO: Handle the case of the last action being a transition, where our ending velocity and acceleration are 0 and destination is home.
				} else{
					// Calculate previous and next motion to generate what's needed for the transition
					MotionAction *ref = FindPreviousAction(actions[i][j].GetDrone(), actions, i);
					MotionAction *next = FindNextAction(actions[i][j].GetDrone(), actions, i);

					// Get states from the previous and next state that were found
					auto startState = ref->GetPathState(ref->GetEndTime());
					auto endState = next->GetPathState(next->GetStartTime());

					// Store start and end time from EmptyAction to carry over to MotionAction's trajectory
					double thisStart = actions[i][j]->GetStartTime();
					double thisEnd = actions[i][j]->GetEndTime();

					// Cleanup object and replace with a new MotionAction
					delete actions[i][j];
					actions[i][j] = new MotionAction(actions[i][j]->GetDrone(),
													 std::move(std::unique_ptr<PolynomialTrajectory>(
															 PolynomialTrajectory::compute(
																	 {startState.position, startState.velocity,
																	  startState.acceleration},
																	 thisStart,
																	 {endState.position, endState.velocity,
																	  endState.acceleration},
																	 thisEnd))));
				}
			}
		}
	}
}

void Jocs::parseAction(const nlohmann::json::reference data, std::vector<Action*>& actions){
	auto actionsArray = data[ACTION_ROOT_KEY];
	assert(actionsArray.is_array());
	double startTime = data[ACTION_TIME_KEY];
	for(int i = 0; i < actionsArray.size(); i++) {
		auto actionsArrayElement = actionsArray[i];
		double duration = actionsArrayElement[DURATION_KEY];
		unsigned type = convertToActionType(actionsArrayElement[ACTION_TYPE_KEY]);
		//TODO: Assuming no grouping right now. Will have to make this a loop to create actions for each drone and calc offset
		assert(actionsArrayElement[DRONE_ARRAY_KEY].is_array());
		unsigned drone = actionsArrayElement[DRONE_ARRAY_KEY][0];
		//Switch on type of Action
		switch (type) {
			//Transitional case: Put a placeholder action to be post-processed away
			case ActionTypes::Transition: {
				// Actual calculation will be processed after this loop
				// For now, there is no trajectory !
				actions.push_back(new EmptyAction(drone, startTime, startTime + duration));
				break;
			}
			//Simple line action
			case ActionTypes::Line: {
				Point start(
						actionsArrayElement[ACTION_DATA_KEY][STARTPOS_KEY][0],
						actionsArrayElement[ACTION_DATA_KEY][STARTPOS_KEY][1],
						actionsArrayElement[ACTION_DATA_KEY][STARTPOS_KEY][2]);

				Point end(
						actionsArrayElement[ACTION_DATA_KEY][ENDPOS_KEY][0],
						actionsArrayElement[ACTION_DATA_KEY][ENDPOS_KEY][1],
						actionsArrayElement[ACTION_DATA_KEY][ENDPOS_KEY][2]);

				actions.push_back(new MotionAction(
						drone, std::move(std::make_unique<LinearTrajectory>(start, startTime, end, startTime + duration))));
				break;
			}
			//Circle Action
			case ActionTypes::Circle: {
				Point origin(
						actionsArrayElement[ACTION_DATA_KEY][CIRCLE_ORIGIN_KEY][0],
						actionsArrayElement[ACTION_DATA_KEY][CIRCLE_ORIGIN_KEY][1],
						actionsArrayElement[ACTION_DATA_KEY][CIRCLE_ORIGIN_KEY][2]);
				double radius = actionsArrayElement[ACTION_DATA_KEY][CIRCLE_RADIUS_KEY];
				double theta1 = actionsArrayElement[ACTION_DATA_KEY][CIRCLE_THETA1_KEY];
				double theta2 = actionsArrayElement[ACTION_DATA_KEY][CIRCLE_THETA2_KEY];
				actions.push_back(new MotionAction(
						drone, std::move(std::make_unique<CircleTrajectory>(origin, radius, theta1, startTime, theta2, startTime + duration))));
				break;
			}
			default: {
				break;
			}
		}
	}
}

ActionTypes Jocs::convertToActionType(const std::string& data){
	if(data == "transition")
		return ActionTypes::Transition;
	else if (data == "line")
		return ActionTypes::Line;
	else if (data == "circle")
		return ActionTypes::Circle;
}

//TODO: maybe instead of returning a pointer we take it as a parameter which we modify?
MotionAction* Jocs::FindPreviousAction(DroneId id, std::vector< vector<Action*> >& actions, int currentLocation) {
	for(int i = currentLocation-1; i >= 0; i--){
		MotionAction* actionFound = nullptr;
		bool success = FindMotionForDrone(id, actions[i], actionFound);
		if (success) {
			return actionFound;
		}
	}
	// This means there never *was* a previous motion for this drone
	//TODO: maybe should throw exception here?
	return nullptr;
}

//TODO: maybe instead of returning a pointer we take it as a parameter which we modify?
MotionAction* Jocs::FindNextAction(DroneId id, std::vector< vector<Action*> >& actions, int currentLocation) {
	for(int i = currentLocation+1; i < actions.size(); i++){
		MotionAction* actionFound = nullptr;
		bool success = FindMotionForDrone(id, actions[i], actionFound);
		if (success) {
			return actionFound;
		}
	}
	// This means there never *will be* a next motion for this drone
	//TODO: maybe should throw exception here?
	return nullptr;
}

bool Jocs::FindMotionForDrone(DroneId id, vector<Action*> curActions, MotionAction& actionFound) {
	for(int i = 0; i < curActions.size(); i++){
		if (curActions[i].GetDrone() == id) {
			// We found the drone we were looking for *wink wink*
			actionFound = static_cast<MotionAction*>(curActions[i]);
			return true;
		}
	}
	return false; // This means that the previous time didn't have the drone in it.
}
}
<|endoftext|>