mirror of
https://github.com/XRPLF/rippled.git
synced 2025-11-29 07:25:51 +00:00
Config improvements:
* More fine-grained Section mutators * Add remap for mapping legacy single sections to key value pairs * Add output stream operators for BasicConfig and Section * Allow section values to be overwritten from command line * Update rpc key/value configs from command line * Add RPC::Setup with defaults and remap legacy rpc sections
This commit is contained in:
@@ -309,7 +309,8 @@ public:
|
|||||||
, m_deprecatedUNL (make_UniqueNodeList (*m_jobQueue))
|
, m_deprecatedUNL (make_UniqueNodeList (*m_jobQueue))
|
||||||
|
|
||||||
, m_rpcHTTPServer (make_RPCHTTPServer (*m_networkOPs,
|
, m_rpcHTTPServer (make_RPCHTTPServer (*m_networkOPs,
|
||||||
*m_jobQueue, *m_networkOPs, *m_resourceManager))
|
*m_jobQueue, *m_networkOPs, *m_resourceManager,
|
||||||
|
setup_RPC(getConfig()["rpc"])))
|
||||||
|
|
||||||
// passive object, not a Service
|
// passive object, not a Service
|
||||||
, m_rpcServerHandler (*m_networkOPs, *m_resourceManager)
|
, m_rpcServerHandler (*m_networkOPs, *m_resourceManager)
|
||||||
|
|||||||
@@ -344,6 +344,7 @@ int run (int argc, char** argv)
|
|||||||
if (vm.count ("rpc_ip"))
|
if (vm.count ("rpc_ip"))
|
||||||
{
|
{
|
||||||
getConfig ().setRpcIpAndOptionalPort (vm ["rpc_ip"].as <std::string> ());
|
getConfig ().setRpcIpAndOptionalPort (vm ["rpc_ip"].as <std::string> ());
|
||||||
|
getConfig().overwrite("rpc", "ip", vm["rpc_ip"].as<std::string>());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override the RPC destination port number
|
// Override the RPC destination port number
|
||||||
@@ -352,6 +353,7 @@ int run (int argc, char** argv)
|
|||||||
{
|
{
|
||||||
// VFALCO TODO This should be a short.
|
// VFALCO TODO This should be a short.
|
||||||
getConfig ().setRpcPort (vm ["rpc_port"].as <int> ());
|
getConfig ().setRpcPort (vm ["rpc_port"].as <int> ());
|
||||||
|
getConfig().overwrite("rpc", "port", vm["rpc_port"].as<std::string>());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vm.count ("quorum"))
|
if (vm.count ("quorum"))
|
||||||
|
|||||||
@@ -38,9 +38,11 @@ public:
|
|||||||
RPCServerHandler m_deprecatedHandler;
|
RPCServerHandler m_deprecatedHandler;
|
||||||
HTTP::Server m_server;
|
HTTP::Server m_server;
|
||||||
std::unique_ptr <RippleSSLContext> m_context;
|
std::unique_ptr <RippleSSLContext> m_context;
|
||||||
|
RPC::Setup setup_;
|
||||||
|
|
||||||
RPCHTTPServerImp (Stoppable& parent, JobQueue& jobQueue,
|
RPCHTTPServerImp (Stoppable& parent, JobQueue& jobQueue,
|
||||||
NetworkOPs& networkOPs, Resource::Manager& resourceManager)
|
NetworkOPs& networkOPs, Resource::Manager& resourceManager,
|
||||||
|
RPC::Setup const& setup)
|
||||||
: RPCHTTPServer (parent)
|
: RPCHTTPServer (parent)
|
||||||
, m_resourceManager (resourceManager)
|
, m_resourceManager (resourceManager)
|
||||||
, m_journal (deprecatedLogs().journal("HTTP-RPC"))
|
, m_journal (deprecatedLogs().journal("HTTP-RPC"))
|
||||||
@@ -48,18 +50,13 @@ public:
|
|||||||
, m_networkOPs (networkOPs)
|
, m_networkOPs (networkOPs)
|
||||||
, m_deprecatedHandler (networkOPs, resourceManager)
|
, m_deprecatedHandler (networkOPs, resourceManager)
|
||||||
, m_server (*this, deprecatedLogs().journal("HTTP"))
|
, m_server (*this, deprecatedLogs().journal("HTTP"))
|
||||||
|
, setup_ (setup)
|
||||||
{
|
{
|
||||||
if (getConfig ().RPC_SECURE == 0)
|
if (setup_.secure)
|
||||||
{
|
|
||||||
m_context.reset (RippleSSLContext::createBare ());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_context.reset (RippleSSLContext::createAuthenticated (
|
m_context.reset (RippleSSLContext::createAuthenticated (
|
||||||
getConfig ().RPC_SSL_KEY,
|
setup_.ssl_key, setup_.ssl_cert, setup_.ssl_chain));
|
||||||
getConfig ().RPC_SSL_CERT,
|
else
|
||||||
getConfig ().RPC_SSL_CHAIN));
|
m_context.reset (RippleSSLContext::createBare());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~RPCHTTPServerImp()
|
~RPCHTTPServerImp()
|
||||||
@@ -70,10 +67,9 @@ public:
|
|||||||
void
|
void
|
||||||
setup (beast::Journal journal) override
|
setup (beast::Journal journal) override
|
||||||
{
|
{
|
||||||
if (! getConfig ().getRpcIP().empty () &&
|
if (! setup_.ip.empty() && setup_.port != 0)
|
||||||
getConfig ().getRpcPort() != 0)
|
|
||||||
{
|
{
|
||||||
auto ep = beast::IP::Endpoint::from_string (getConfig().getRpcIP());
|
auto ep = beast::IP::Endpoint::from_string (setup_.ip);
|
||||||
|
|
||||||
// VFALCO TODO IP address should not have an "unspecified" state
|
// VFALCO TODO IP address should not have an "unspecified" state
|
||||||
//if (! is_unspecified (ep))
|
//if (! is_unspecified (ep))
|
||||||
@@ -81,8 +77,8 @@ public:
|
|||||||
HTTP::Port port;
|
HTTP::Port port;
|
||||||
port.security = HTTP::Port::Security::allow_ssl;
|
port.security = HTTP::Port::Security::allow_ssl;
|
||||||
port.addr = ep.at_port(0);
|
port.addr = ep.at_port(0);
|
||||||
if (getConfig ().getRpcPort() != 0)
|
if (setup_.port != 0)
|
||||||
port.port = getConfig ().getRpcPort();
|
port.port = setup_.port;
|
||||||
else
|
else
|
||||||
port.port = ep.port();
|
port.port = ep.port();
|
||||||
port.context = m_context.get ();
|
port.context = m_context.get ();
|
||||||
@@ -123,7 +119,7 @@ public:
|
|||||||
onAccept (HTTP::Session& session) override
|
onAccept (HTTP::Session& session) override
|
||||||
{
|
{
|
||||||
// Reject non-loopback connections if RPC_ALLOW_REMOTE is not set
|
// Reject non-loopback connections if RPC_ALLOW_REMOTE is not set
|
||||||
if (! getConfig().RPC_ALLOW_REMOTE &&
|
if (! setup_.allow_remote &&
|
||||||
! beast::IP::is_loopback (session.remoteAddress()))
|
! beast::IP::is_loopback (session.remoteAddress()))
|
||||||
{
|
{
|
||||||
session.close (false);
|
session.close (false);
|
||||||
@@ -295,10 +291,11 @@ RPCHTTPServer::RPCHTTPServer (Stoppable& parent)
|
|||||||
|
|
||||||
std::unique_ptr <RPCHTTPServer>
|
std::unique_ptr <RPCHTTPServer>
|
||||||
make_RPCHTTPServer (beast::Stoppable& parent, JobQueue& jobQueue,
|
make_RPCHTTPServer (beast::Stoppable& parent, JobQueue& jobQueue,
|
||||||
NetworkOPs& networkOPs, Resource::Manager& resourceManager)
|
NetworkOPs& networkOPs, Resource::Manager& resourceManager,
|
||||||
|
RPC::Setup const& setup)
|
||||||
{
|
{
|
||||||
return std::make_unique <RPCHTTPServerImp> (
|
return std::make_unique <RPCHTTPServerImp> (
|
||||||
parent, jobQueue, networkOPs, resourceManager);
|
parent, jobQueue, networkOPs, resourceManager, setup);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#ifndef RIPPLE_APP_RPCHTTPSERVER_H_INCLUDED
|
#ifndef RIPPLE_APP_RPCHTTPSERVER_H_INCLUDED
|
||||||
#define RIPPLE_APP_RPCHTTPSERVER_H_INCLUDED
|
#define RIPPLE_APP_RPCHTTPSERVER_H_INCLUDED
|
||||||
|
|
||||||
|
#include <ripple/core/Config.h>
|
||||||
#include <beast/utility/Journal.h>
|
#include <beast/utility/Journal.h>
|
||||||
#include <beast/utility/PropertyStream.h>
|
#include <beast/utility/PropertyStream.h>
|
||||||
#include <beast/cxx14/memory.h> // <memory>
|
#include <beast/cxx14/memory.h> // <memory>
|
||||||
@@ -48,7 +49,8 @@ public:
|
|||||||
|
|
||||||
std::unique_ptr <RPCHTTPServer>
|
std::unique_ptr <RPCHTTPServer>
|
||||||
make_RPCHTTPServer (beast::Stoppable& parent, JobQueue& jobQueue,
|
make_RPCHTTPServer (beast::Stoppable& parent, JobQueue& jobQueue,
|
||||||
NetworkOPs& networkOPs, Resource::Manager& resourceManager);
|
NetworkOPs& networkOPs, Resource::Manager& resourceManager,
|
||||||
|
RPC::Setup const& setup);
|
||||||
|
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|
||||||
|
|||||||
@@ -93,9 +93,30 @@ public:
|
|||||||
}
|
}
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
|
/** Overwrite a key/value pair with a command line argument
|
||||||
|
If the section does not exist it is created.
|
||||||
|
The previous value, if any, is overwritten.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
overwrite (std::string const& section, std::string const& key,
|
||||||
|
std::string const& value);
|
||||||
|
|
||||||
|
friend
|
||||||
|
std::ostream&
|
||||||
|
operator<< (std::ostream& ss, BasicConfig const& c);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void
|
void
|
||||||
build (IniFileSections const& ifs);
|
build (IniFileSections const& ifs);
|
||||||
|
|
||||||
|
/** Insert a legacy single section as a key/value pair.
|
||||||
|
Does nothing if the section does not exist, or does not contain
|
||||||
|
a single line that is not a key/value pair.
|
||||||
|
@deprecated
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
remap (std::string const& legacy_section,
|
||||||
|
std::string const& key, std::string const& new_section);
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -456,10 +477,37 @@ public:
|
|||||||
int getSize (SizedItemName);
|
int getSize (SizedItemName);
|
||||||
void setup (std::string const& strConf, bool bQuiet);
|
void setup (std::string const& strConf, bool bQuiet);
|
||||||
void load ();
|
void load ();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void build_legacy();
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Config& getConfig ();
|
extern Config& getConfig ();
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace RPC {
|
||||||
|
|
||||||
|
struct Setup
|
||||||
|
{
|
||||||
|
bool allow_remote = false;
|
||||||
|
std::string admin_user;
|
||||||
|
std::string admin_password;
|
||||||
|
std::string ip;
|
||||||
|
int port = 5001;
|
||||||
|
std::string user;
|
||||||
|
std::string password;
|
||||||
|
bool secure = false;
|
||||||
|
std::string ssl_cert;
|
||||||
|
std::string ssl_chain;
|
||||||
|
std::string ssl_key;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
RPC::Setup
|
||||||
|
setup_RPC (Section const& s);
|
||||||
|
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include <beast/utility/ci_char_traits.h>
|
#include <beast/utility/ci_char_traits.h>
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <ostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@@ -42,6 +43,32 @@ public:
|
|||||||
/** Create an empty section. */
|
/** Create an empty section. */
|
||||||
Section() = default;
|
Section() = default;
|
||||||
|
|
||||||
|
/** Returns the number of key/value pairs. */
|
||||||
|
std::size_t
|
||||||
|
keys() const
|
||||||
|
{
|
||||||
|
return map_.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns all the lines in the section. */
|
||||||
|
std::vector <std::string> const&
|
||||||
|
lines() const
|
||||||
|
{
|
||||||
|
return lines_;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set a key/value pair.
|
||||||
|
The previous value is discarded.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
set (std::string const& key, std::string const& value);
|
||||||
|
|
||||||
|
/** Append a line to this section.
|
||||||
|
If the line can be parsed as a key/value pair it is added to the map.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
append (std::string const& line);
|
||||||
|
|
||||||
/** Append a set of lines to this section.
|
/** Append a set of lines to this section.
|
||||||
Parsable key/value pairs are also added to the map.
|
Parsable key/value pairs are also added to the map.
|
||||||
*/
|
*/
|
||||||
@@ -57,6 +84,10 @@ public:
|
|||||||
*/
|
*/
|
||||||
std::pair <std::string, bool>
|
std::pair <std::string, bool>
|
||||||
find (std::string const& name) const;
|
find (std::string const& name) const;
|
||||||
|
|
||||||
|
friend
|
||||||
|
std::ostream&
|
||||||
|
operator<< (std::ostream&, Section const& section);
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Set a value from a configuration Section
|
/** Set a value from a configuration Section
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
#include <ripple/net/HTTPClient.h>
|
#include <ripple/net/HTTPClient.h>
|
||||||
#include <beast/http/ParsedURL.h>
|
#include <beast/http/ParsedURL.h>
|
||||||
#include <beast/module/core/text/LexicalCast.h>
|
#include <beast/module/core/text/LexicalCast.h>
|
||||||
|
#include <beast/streams/debug_ostream.h>
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
@@ -31,6 +32,10 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#ifndef DUMP_CONFIG
|
||||||
|
#define DUMP_CONFIG 0
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -237,17 +242,48 @@ BasicConfig::section (std::string const& name) const
|
|||||||
return iter->second;
|
return iter->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BasicConfig::remap (std::string const& legacy_section,
|
||||||
|
std::string const& key, std::string const& new_section)
|
||||||
|
{
|
||||||
|
auto const iter = map_.find (legacy_section);
|
||||||
|
if (iter == map_.end())
|
||||||
|
return;
|
||||||
|
if (iter->second.keys() != 0)
|
||||||
|
return;
|
||||||
|
if (iter->second.lines().size() != 1)
|
||||||
|
return;
|
||||||
|
auto& s = map_[new_section];
|
||||||
|
s.append (iter->second.lines().front());
|
||||||
|
s.set (key, iter->second.lines().front());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
BasicConfig::overwrite (std::string const& section, std::string const& key,
|
||||||
|
std::string const& value)
|
||||||
|
{
|
||||||
|
auto const result = map_.emplace (section, Section{});
|
||||||
|
result.first->second.set (key, value);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
BasicConfig::build (IniFileSections const& ifs)
|
BasicConfig::build (IniFileSections const& ifs)
|
||||||
{
|
{
|
||||||
for (auto const& entry : ifs)
|
for (auto const& entry : ifs)
|
||||||
{
|
{
|
||||||
auto const result = map_.insert (std::make_pair (
|
auto const result = map_.emplace (entry.first, Section{});
|
||||||
entry.first, Section{}));
|
|
||||||
result.first->second.append (entry.second);
|
result.first->second.append (entry.second);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::ostream&
|
||||||
|
operator<< (std::ostream& ss, BasicConfig const& c)
|
||||||
|
{
|
||||||
|
for (auto const& s : c.map_)
|
||||||
|
ss << "[" << s.first << "]\n" << s.second;
|
||||||
|
return ss;
|
||||||
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
//
|
//
|
||||||
// Config (DEPRECATED)
|
// Config (DEPRECATED)
|
||||||
@@ -458,6 +494,7 @@ void Config::load ()
|
|||||||
std::string strTemp;
|
std::string strTemp;
|
||||||
|
|
||||||
build (secConfig);
|
build (secConfig);
|
||||||
|
build_legacy();
|
||||||
|
|
||||||
// XXX Leak
|
// XXX Leak
|
||||||
IniFileSections::mapped_type* smtTmp;
|
IniFileSections::mapped_type* smtTmp;
|
||||||
@@ -973,4 +1010,55 @@ beast::File const& Config::getModuleDatabasePath ()
|
|||||||
return m_moduleDbPath;
|
return m_moduleDbPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
void
|
||||||
|
Config::build_legacy ()
|
||||||
|
{
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
//
|
||||||
|
// [rpc]
|
||||||
|
//
|
||||||
|
//--------------------------------------------------------------------------
|
||||||
|
|
||||||
|
remap("rpc_allow_remote", "allow_remote", "rpc");
|
||||||
|
//remap("rpc_admin_allow", "admin_allow", "rpc"); // Not a key-value pair
|
||||||
|
remap("rpc_admin_user", "admin_user", "rpc");
|
||||||
|
remap("rpc_admin_password", "admin_password", "rpc");
|
||||||
|
remap("rpc_ip", "ip", "rpc");
|
||||||
|
remap("rpc_port", "port", "rpc");
|
||||||
|
remap("rpc_user", "user", "rpc");
|
||||||
|
remap("rpc_password", "password", "rpc");
|
||||||
|
//remap("rpc_startup", "startup", "rpc"); // Not a key-value pair
|
||||||
|
remap("rpc_secure", "secure", "rpc");
|
||||||
|
remap("rpc_ssl_cert", "ssl_cert", "rpc");
|
||||||
|
remap("rpc_ssl_chain", "ssl_chain", "rpc");
|
||||||
|
remap("rpc_ssl_key", "ssl_key", "rpc");
|
||||||
|
|
||||||
|
#if DUMP_CONFIG
|
||||||
|
beast::debug_ostream log;
|
||||||
|
log << (BasicConfig const&)*this;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
RPC::Setup
|
||||||
|
setup_RPC (Section const& s)
|
||||||
|
{
|
||||||
|
RPC::Setup c;
|
||||||
|
set (c.allow_remote, "allow_remote", s);
|
||||||
|
set (c.admin_user, "admin_user", s);
|
||||||
|
set (c.admin_password, "admin_password", s);
|
||||||
|
set (c.ip, "ip", s);
|
||||||
|
set (c.port, "port", s);
|
||||||
|
set (c.user, "user", s);
|
||||||
|
set (c.password, "password", s);
|
||||||
|
set (c.secure, "secure", s);
|
||||||
|
set (c.ssl_cert, "ssl_cert", s);
|
||||||
|
set (c.ssl_chain, "ssl_chain", s);
|
||||||
|
set (c.ssl_key, "ssl_key", s);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|||||||
@@ -19,11 +19,20 @@
|
|||||||
|
|
||||||
#include <ripple/core/Section.h>
|
#include <ripple/core/Section.h>
|
||||||
#include <boost/regex.hpp>
|
#include <boost/regex.hpp>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
void
|
void
|
||||||
Section::append (std::vector <std::string> const& lines)
|
Section::set (std::string const& key, std::string const& value)
|
||||||
|
{
|
||||||
|
auto const result = map_.emplace (key, value);
|
||||||
|
if (! result.second)
|
||||||
|
result.first->second = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Section::append (std::string const& line)
|
||||||
{
|
{
|
||||||
// <key> '=' <value>
|
// <key> '=' <value>
|
||||||
static boost::regex const re1 (
|
static boost::regex const re1 (
|
||||||
@@ -38,25 +47,18 @@ Section::append (std::vector <std::string> const& lines)
|
|||||||
, boost::regex_constants::optimize
|
, boost::regex_constants::optimize
|
||||||
);
|
);
|
||||||
|
|
||||||
|
boost::smatch match;
|
||||||
|
lines_.push_back (line);
|
||||||
|
if (boost::regex_match (line, match, re1))
|
||||||
|
set (match[1], match[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
Section::append (std::vector <std::string> const& lines)
|
||||||
|
{
|
||||||
lines_.reserve (lines_.size() + lines.size());
|
lines_.reserve (lines_.size() + lines.size());
|
||||||
for (auto const& line : lines)
|
std::for_each (lines.begin(), lines.end(),
|
||||||
{
|
[&](std::string const& line) { this->append (line); });
|
||||||
boost::smatch match;
|
|
||||||
lines_.push_back (line);
|
|
||||||
if (boost::regex_match (line, match, re1))
|
|
||||||
{
|
|
||||||
/*auto const result =*/ map_.emplace (
|
|
||||||
std::make_pair (match[1], match[2]));
|
|
||||||
#if 0
|
|
||||||
if (! result.second)
|
|
||||||
{
|
|
||||||
// If we decide on how to merge values we can do it here.
|
|
||||||
}
|
|
||||||
beast::debug_ostream log;
|
|
||||||
//log << "\"" << match[1] << "\" = \"" << match[2] << "\"";
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
@@ -74,4 +76,14 @@ Section::find (std::string const& name) const
|
|||||||
return {iter->second, true};
|
return {iter->second, true};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
std::ostream&
|
||||||
|
operator<< (std::ostream& os, Section const& section)
|
||||||
|
{
|
||||||
|
for (auto const& kv : section.map_)
|
||||||
|
os << kv.first << "=" << kv.second << "\n";
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|||||||
Reference in New Issue
Block a user