20#include <xrpld/rpc/ServerHandler.h>
22#include <xrpld/app/main/Application.h>
23#include <xrpld/app/misc/NetworkOPs.h>
24#include <xrpld/core/ConfigSections.h>
25#include <xrpld/core/JobQueue.h>
26#include <xrpld/overlay/Overlay.h>
27#include <xrpld/rpc/RPCHandler.h>
28#include <xrpld/rpc/Role.h>
29#include <xrpld/rpc/detail/RPCHelpers.h>
30#include <xrpld/rpc/detail/Tuning.h>
31#include <xrpld/rpc/json_body.h>
32#include <xrpl/basics/Log.h>
33#include <xrpl/basics/base64.h>
34#include <xrpl/basics/contract.h>
35#include <xrpl/basics/make_SSLContext.h>
36#include <xrpl/beast/net/IPAddressConversion.h>
37#include <xrpl/beast/rfc2616.h>
38#include <xrpl/json/json_reader.h>
39#include <xrpl/json/to_string.h>
40#include <xrpl/protocol/ErrorCodes.h>
41#include <xrpl/protocol/RPCErr.h>
42#include <xrpl/resource/Fees.h>
43#include <xrpl/resource/ResourceManager.h>
44#include <xrpl/server/Server.h>
45#include <xrpl/server/SimpleWriter.h>
46#include <xrpl/server/detail/JSONRPCUtil.h>
48#include <boost/algorithm/string.hpp>
49#include <boost/beast/http/fields.hpp>
50#include <boost/beast/http/string_body.hpp>
60 return request.version() >= 11 && request.target() ==
"/" &&
61 request.body().size() == 0 &&
62 request.method() == boost::beast::http::verb::get;
68 boost::beast::http::status status)
70 using namespace boost::beast::http;
72 response<string_body> msg;
73 msg.version(request.version());
76 msg.insert(
"Content-Type",
"text/html");
77 msg.insert(
"Connection",
"close");
78 msg.body() =
"Invalid protocol.";
79 msg.prepare_payload();
80 handoff.
response = std::make_shared<SimpleWriter>(msg);
91 auto const it = h.
find(
"authorization");
92 if ((it == h.
end()) || (it->second.substr(0, 6) !=
"Basic "))
95 boost::trim(strUserPass64);
97 std::string::size_type nColon = strUserPass.
find(
":");
98 if (nColon == std::string::npos)
102 return strUser == port.
user && strPassword == port.
password;
108 boost::asio::io_service& io_service,
114 , m_resourceManager(resourceManager)
115 , m_journal(app_.journal(
"Server"))
116 , m_networkOPs(networkOPs)
117 , m_server(
make_Server(*this, io_service, app_.journal(
"Server")))
118 , m_jobQueue(jobQueue)
120 auto const& group(cm.
group(
"rpc"));
142 auto const endpointPort = it->second.port();
144 port.port = endpointPort;
147 (port.protocol.count(
"http") > 0 ||
148 port.protocol.count(
"https") > 0))
151 if (!
setup_.
overlay.port() && (port.protocol.count(
"peer") > 0))
174 boost::asio::ip::tcp::endpoint endpoint)
176 auto const& port = session.
port();
178 auto const c = [
this, &port]() {
183 if (port.limit && c >= port.limit)
186 << port.name <<
" is full; dropping " << endpoint;
198 boost::asio::ip::tcp::endpoint
const& remote_address)
200 using namespace boost::beast;
203 p.
count(
"ws") > 0 || p.count(
"ws2") > 0 || p.count(
"wss") > 0 ||
204 p.count(
"wss2") > 0};
206 if (websocket::is_upgrade(request))
219 <<
"Exception upgrading websocket: " << e.
what() <<
"\n";
221 request, http::status::internal_server_error);
225 auto const beast_remote_address =
229 beast_remote_address,
234 beast_remote_address,
237 is->forwarded_for());
238 ws->appDefined = std::move(is);
242 handoff.
moved =
true;
246 if (bundle && p.count(
"peer") > 0)
248 std::move(bundle), std::move(request), remote_address);
260 return [&](boost::beast::string_view
const& b) {
261 session.
write(b.data(), b.size());
269 for (
auto const& e : h)
275 return std::tolower(static_cast<unsigned char>(kc));
282template <
class ConstBufferSequence>
286 using boost::asio::buffer_cast;
287 using boost::asio::buffer_size;
293 s.
append(buffer_cast<char const*>(b), buffer_size(b));
324 if (postResult ==
nullptr)
329 "Service Unavailable",
332 detachedSession->close(
true);
343 auto const size = boost::asio::buffer_size(buffers);
348 jvResult[jss::type] = jss::error;
349 jvResult[jss::error] =
"jsonInvalid";
351 boost::beast::multi_buffer sb;
352 Json::stream(jvResult, [&sb](
auto const p,
auto const n) {
353 sb.commit(boost::asio::buffer_copy(
354 sb.prepare(n), boost::asio::buffer(p, n)));
356 JLOG(
m_journal.
trace()) <<
"Websocket sending '" << jvResult <<
"'";
368 [
this, session, jv = std::move(jv)](
372 auto const n = s.length();
373 boost::beast::multi_buffer sb(n);
374 sb.commit(boost::asio::buffer_copy(
375 sb.prepare(n), boost::asio::buffer(s.c_str(), n)));
380 if (postResult ==
nullptr)
383 session->close({boost::beast::websocket::going_away,
"Shutting Down"});
411 using namespace std::chrono_literals;
412 auto const level = (duration >= 10s) ? journal.
error()
413 : (duration >= 1s) ? journal.
warn()
416 JLOG(level) <<
"RPC request processing duration = "
417 << std::chrono::duration_cast<std::chrono::microseconds>(
420 <<
" microseconds. request = " << request;
429 auto is = std::static_pointer_cast<WSInfoSub>(session->appDefined);
430 if (is->getConsumer().disconnect(
m_journal))
433 {boost::beast::websocket::policy_error,
"threshold exceeded"});
453 jr[jss::type] = jss::response;
454 jr[jss::status] = jss::error;
456 ? jss::invalid_API_version
457 : jss::missingCommand;
458 jr[jss::request] = jv;
460 jr[jss::id] = jv[jss::id];
462 jr[jss::jsonrpc] = jv[jss::jsonrpc];
464 jr[jss::ripplerpc] = jv[jss::ripplerpc];
466 jr[jss::api_version] = jv[jss::api_version];
502 {is->user(), is->forwarded_for()}};
514 <<
"Exception while processing WS: " << ex.
what() <<
"\n"
518 is->getConsumer().charge(loadType);
519 if (is->getConsumer().warn())
520 jr[jss::warning] = jss::load;
527 if (jr[jss::result].isMember(jss::error))
529 jr = jr[jss::result];
530 jr[jss::status] = jss::error;
536 if (rq.isMember(jss::passphrase.c_str()))
537 rq[jss::passphrase.c_str()] =
"<masked>";
538 if (rq.isMember(jss::secret.c_str()))
539 rq[jss::secret.c_str()] =
"<masked>";
540 if (rq.isMember(jss::seed.c_str()))
541 rq[jss::seed.c_str()] =
"<masked>";
542 if (rq.isMember(jss::seed_hex.c_str()))
543 rq[jss::seed_hex.c_str()] =
"<masked>";
546 jr[jss::request] = rq;
550 if (jr[jss::result].isMember(
"forwarded") &&
551 jr[jss::result][
"forwarded"])
552 jr = jr[jss::result];
553 jr[jss::status] = jss::success;
557 jr[jss::id] = jv[jss::id];
559 jr[jss::jsonrpc] = jv[jss::jsonrpc];
561 jr[jss::ripplerpc] = jv[jss::ripplerpc];
563 jr[jss::api_version] = jv[jss::api_version];
565 jr[jss::type] = jss::response;
578 session->remoteAddress().at_port(0),
583 auto const iter = session->request().find(
"X-User");
584 if (iter != session->request().end())
585 return iter->value();
586 return boost::beast::string_view{};
592 session->close(
true);
600 sub[
"message"] = std::move(message);
612ServerHandler::processRequest(
621 auto rpcJ = app_.journal(
"RPC");
626 if ((request.
size() > RPC::Tuning::maxRequestSize) ||
627 !reader.
parse(request, jsonOrig) || !jsonOrig ||
641 if (jsonOrig.
isMember(jss::method) && jsonOrig[jss::method] ==
"batch")
644 if (!jsonOrig.
isMember(jss::params) || !jsonOrig[jss::params].
isArray())
646 HTTPReply(400,
"Malformed batch request", output, rpcJ);
649 size = jsonOrig[jss::params].
size();
654 for (
unsigned i = 0; i < size; ++i)
657 batch ? jsonOrig[jss::params][i] : jsonOrig;
662 r[jss::request] = jsonRPC;
669 unsigned apiVersion = RPC::apiVersionIfUnspecified;
671 jsonRPC[jss::params].
size() > 0 &&
672 jsonRPC[jss::params][0u].
isObject())
674 apiVersion = RPC::getAPIVersionNumber(
676 app_.config().BETA_RPC_API);
679 if (apiVersion == RPC::apiVersionIfUnspecified && batch)
683 RPC::getAPIVersionNumber(jsonRPC, app_.config().BETA_RPC_API);
686 if (apiVersion == RPC::apiInvalidVersion)
690 HTTPReply(400, jss::invalid_API_version.c_str(), output, rpcJ);
694 r[jss::request] = jsonRPC;
702 auto role = Role::FORBID;
703 auto required = Role::FORBID;
705 required = RPC::roleRequired(
707 app_.config().BETA_RPC_API,
711 jsonRPC[jss::params].
size() > 0 &&
730 usage = m_resourceManager.newUnlimitedEndpoint(remoteIPAddress);
734 usage = m_resourceManager.newInboundEndpoint(
740 HTTPReply(503,
"Server is overloaded", output, rpcJ);
751 if (role == Role::FORBID)
753 usage.
charge(Resource::feeInvalidRPC);
756 HTTPReply(403,
"Forbidden", output, rpcJ);
765 if (!jsonRPC.
isMember(jss::method) || jsonRPC[jss::method].
isNull())
767 usage.
charge(Resource::feeInvalidRPC);
770 HTTPReply(400,
"Null method", output, rpcJ);
782 usage.
charge(Resource::feeInvalidRPC);
785 HTTPReply(400,
"method is not string", output, rpcJ);
796 if (strMethod.
empty())
798 usage.
charge(Resource::feeInvalidRPC);
801 HTTPReply(400,
"method is empty", output, rpcJ);
820 params = jsonRPC[jss::params];
826 usage.
charge(Resource::feeInvalidRPC);
827 HTTPReply(400,
"params unparseable", output, rpcJ);
832 params = std::move(params[0u]);
835 usage.
charge(Resource::feeInvalidRPC);
836 HTTPReply(400,
"params unparseable", output, rpcJ);
847 if (params.
isMember(jss::ripplerpc))
849 if (!params[jss::ripplerpc].isString())
851 usage.
charge(Resource::feeInvalidRPC);
854 HTTPReply(400,
"ripplerpc is not a string", output, rpcJ);
864 ripplerpc = params[jss::ripplerpc].
asString();
871 if (role != Role::IDENTIFIED && role != Role::PROXY)
877 JLOG(m_journal.debug()) <<
"Query: " << strMethod << params;
880 params[jss::command] = strMethod;
881 JLOG(m_journal.trace())
882 <<
"doRpcCommand:" << strMethod <<
":" << params;
891 app_.getLedgerMaster(),
905 RPC::doCommand(context, result);
910 JLOG(m_journal.error()) <<
"Internal error : " << ex.
what()
911 <<
" when processing request: "
921 result[jss::warning] = jss::load;
924 if (ripplerpc >=
"2.0")
928 result[jss::status] = jss::error;
929 result[
"code"] = result[jss::error_code];
930 result[
"message"] = result[jss::error_message];
932 JLOG(m_journal.debug()) <<
"rpcError: " << result[jss::error]
933 <<
": " << result[jss::error_message];
934 r[jss::error] = std::move(result);
938 result[jss::status] = jss::success;
939 r[jss::result] = std::move(result);
952 if (rq.isMember(jss::passphrase.c_str()))
953 rq[jss::passphrase.c_str()] =
"<masked>";
954 if (rq.isMember(jss::secret.c_str()))
955 rq[jss::secret.c_str()] =
"<masked>";
956 if (rq.isMember(jss::seed.c_str()))
957 rq[jss::seed.c_str()] =
"<masked>";
958 if (rq.isMember(jss::seed_hex.c_str()))
959 rq[jss::seed_hex.c_str()] =
"<masked>";
962 result[jss::status] = jss::error;
963 result[jss::request] = rq;
965 JLOG(m_journal.debug()) <<
"rpcError: " << result[jss::error]
966 <<
": " << result[jss::error_message];
970 result[jss::status] = jss::success;
972 r[jss::result] = std::move(result);
976 r[jss::jsonrpc] = params[jss::jsonrpc];
977 if (params.
isMember(jss::ripplerpc))
978 r[jss::ripplerpc] = params[jss::ripplerpc];
980 r[jss::id] = params[jss::id];
982 reply.
append(std::move(r));
984 reply = std::move(r);
987 reply[jss::result].
isMember(jss::result))
989 reply = reply[jss::result];
992 reply[jss::result][jss::status] = reply[jss::status];
999 int const httpStatus = [&reply]() {
1002 if (reply.
isMember(jss::ripplerpc) &&
1003 reply[jss::ripplerpc].
isString() &&
1004 reply[jss::ripplerpc].
asString() >=
"3.0")
1008 reply[jss::error].
isMember(jss::error_code) &&
1009 reply[jss::error][jss::error_code].
isInt())
1011 int const errCode = reply[jss::error][jss::error_code].
asInt();
1012 return RPC::error_code_http_status(
1020 auto response = to_string(reply);
1022 rpc_time_.notify(std::chrono::duration_cast<std::chrono::milliseconds>(
1029 if (
auto stream = m_journal.debug())
1031 static const int maxSize = 10000;
1032 if (response.size() <= maxSize)
1033 stream <<
"Reply: " << response;
1035 stream <<
"Reply: " << response.substr(0, maxSize);
1038 HTTPReply(httpStatus, response, output, rpcJ);
1050 using namespace boost::beast::http;
1052 response<string_body> msg;
1054 if (app_.serverOkay(reason))
1056 msg.result(boost::beast::http::status::ok);
1057 msg.body() =
"<!DOCTYPE html><html><head><title>" + systemName() +
1058 " Test page for rippled</title></head><body><h1>" + systemName() +
1059 " Test</h1><p>This page shows rippled http(s) "
1060 "connectivity is working.</p></body></html>";
1064 msg.result(boost::beast::http::status::internal_server_error);
1065 msg.body() =
"<HTML><BODY>Server cannot accept clients: " + reason +
1068 msg.version(request.version());
1069 msg.
insert(
"Server", BuildInfo::getFullVersionString());
1070 msg.insert(
"Content-Type",
"text/html");
1071 msg.insert(
"Connection",
"close");
1072 msg.prepare_payload();
1073 handoff.
response = std::make_shared<SimpleWriter>(msg);
1080ServerHandler::Setup::makeContexts()
1082 for (
auto& p : ports)
1086 if (p.ssl_key.empty() && p.ssl_cert.empty() && p.ssl_chain.empty())
1090 p.ssl_key, p.ssl_cert, p.ssl_chain, p.ssl_ciphers);
1094 p.context = std::make_shared<boost::asio::ssl::context>(
1095 boost::asio::ssl::context::sslv23);
1108 log <<
"Missing 'ip' in [" << p.
name <<
"]";
1109 Throw<std::exception>();
1115 log <<
"Missing 'port' in [" << p.
name <<
"]";
1116 Throw<std::exception>();
1122 log <<
"Missing 'protocol' in [" << p.
name <<
"]";
1123 Throw<std::exception>();
1151 if (!config.
exists(
"server"))
1153 log <<
"Required section [server] is missing";
1154 Throw<std::exception>();
1162 for (
auto const& name : names)
1164 if (!config.
exists(name))
1166 log <<
"Missing section: [" << name <<
"]";
1167 Throw<std::exception>();
1172 if (name == SECTION_PORT_GRPC)
1182 auto it = result.
begin();
1184 while (it != result.
end())
1186 auto& p = it->protocol;
1190 if (p.erase(
"peer") && p.empty())
1191 it = result.
erase(it);
1200 return p.protocol.count(
"peer") != 0;
1205 log <<
"Error: More than one peer protocol configured in [server]";
1206 Throw<std::exception>();
1210 log <<
"Warning: No peer protocol configured";
1220 decltype(setup.
ports)::const_iterator iter;
1221 for (iter = setup.
ports.cbegin(); iter != setup.
ports.cend(); ++iter)
1222 if (iter->protocol.count(
"http") > 0 ||
1223 iter->protocol.count(
"https") > 0)
1225 if (iter == setup.
ports.cend())
1227 setup.
client.
secure = iter->protocol.count(
"https") > 0;
1231 (iter->ip.is_v6() ?
"::1" :
"127.0.0.1")
1232 : iter->ip.to_string();
1246 return port.protocol.count(
"peer") != 0;
1248 if (iter == setup.
ports.cend())
1253 setup.
overlay = {iter->ip, iter->port};
1271 boost::asio::io_service& io_service,
1277 return std::make_unique<ServerHandler>(
Decorator for streaming out compact json.
Unserialize a JSON document into a Value.
std::string getFormatedErrorMessages() const
Returns a user friendly string that list errors in the parsed document.
bool parse(std::string const &document, Value &root)
Read a Value from a JSON document.
UInt size() const
Number of values in array or object.
bool isObjectOrNull() const
Value & append(const Value &value)
Append value to array at the end.
Value removeMember(const char *key)
Remove and return the named member.
std::string asString() const
Returns the unquoted string value.
bool isNull() const
isNull() tests to see if this field is null.
bool isMember(const char *key) const
Return true if the object has a member named key.
A version-independent IP address and port combination.
A generic endpoint for log messages.
Stream trace() const
Severity stream access functions.
virtual Config & config()=0
virtual Overlay & overlay()=0
virtual beast::Journal journal(std::string const &name)=0
virtual NetworkOPs & getOPs()=0
virtual LedgerMaster & getLedgerMaster()=0
bool exists(std::string const &name) const
Returns true if a section with the given name exists.
Section & section(std::string const &name)
Returns the section with the given name.
Provides the beast::insight::Collector service.
virtual beast::insight::Group::ptr const & group(std::string const &name)=0
A pool of threads to perform work.
std::shared_ptr< Coro > postCoro(JobType t, std::string const &name, F &&f)
Creates a coroutine and adds a job to the queue which will run it.
Provides server functionality for clients.
virtual Handoff onHandoff(std::unique_ptr< stream_type > &&bundle, http_request_type &&request, boost::asio::ip::tcp::endpoint remote_address)=0
Conditionally accept an incoming HTTP request.
An endpoint that consumes resources.
bool warn()
Returns true if the consumer should be warned.
bool disconnect(beast::Journal const &j)
Returns true if the consumer should be disconnected.
Disposition charge(Charge const &fee)
Apply a load charge to the consumer.
Tracks load and resource consumption.
std::vector< std::string > const & values() const
Returns all the values in the section.
Resource::Manager & m_resourceManager
std::condition_variable condition_
Json::Value processSession(std::shared_ptr< WSSession > const &session, std::shared_ptr< JobQueue::Coro > const &coro, Json::Value const &jv)
void onWSMessage(std::shared_ptr< WSSession > session, std::vector< boost::asio::const_buffer > const &buffers)
std::unique_ptr< Server > m_server
beast::insight::Event rpc_size_
Setup const & setup() const
beast::insight::Counter rpc_requests_
void onClose(Session &session, boost::system::error_code const &)
Handoff statusResponse(http_request_type const &request) const
NetworkOPs & m_networkOPs
beast::insight::Event rpc_time_
ServerHandler(ServerHandlerCreator const &, Application &app, boost::asio::io_service &io_service, JobQueue &jobQueue, NetworkOPs &networkOPs, Resource::Manager &resourceManager, CollectorManager &cm)
bool onAccept(Session &session, boost::asio::ip::tcp::endpoint endpoint)
std::map< std::reference_wrapper< Port const >, int > count_
void onRequest(Session &session)
void processRequest(Port const &port, std::string const &request, beast::IP::Endpoint const &remoteIPAddress, Output &&, std::shared_ptr< JobQueue::Coro > coro, std::string_view forwardedFor, std::string_view user)
Handoff onHandoff(Session &session, std::unique_ptr< stream_type > &&bundle, http_request_type &&request, boost::asio::ip::tcp::endpoint const &remote_address)
Persistent state information for a connection session.
virtual std::shared_ptr< WSSession > websocketUpgrade()=0
Convert the connection to WebSocket.
virtual Port const & port()=0
Returns the Port settings for this connection.
virtual std::shared_ptr< Session > detach()=0
Detach the session.
virtual void close(bool graceful)=0
Close the session.
virtual http_request_type & request()=0
Returns the current HTTP request.
void write(std::string const &s)
Send a copy of data asynchronously.
void stream(Json::Value const &jv, Write const &write)
Stream compact JSON to the specified function.
@ arrayValue
array value (ordered list)
@ objectValue
object value (collection of name/value pairs).
Endpoint from_asio(boost::asio::ip::address const &address)
Convert to Endpoint.
bool is_unspecified(Address const &addr)
Returns true if the address is unspecified.
bool is_keep_alive(boost::beast::http::message< isRequest, Body, Fields > const &m)
std::string const & getFullVersionString()
Full server version string.
static int constexpr maxRequestSize
unsigned int getAPIVersionNumber(Json::Value const &jv, bool betaEnabled)
Retrieve the api version number from the json value.
Json::Value make_error(error_code_i code)
Returns a new json object that reflects the error code.
Role roleRequired(unsigned int version, bool betaEnabled, std::string const &method)
Status doCommand(RPC::JsonContext &context, Json::Value &result)
Execute an RPC command and store the results in a Json::Value.
static constexpr auto apiInvalidVersion
Charge const feeInvalidRPC
Charge const feeReferenceRPC
TER authorized(ApplyContext const &ctx, AccountID const &dst)
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
void HTTPReply(int nStatus, std::string const &strMsg, Json::Output const &, beast::Journal j)
static std::vector< Port > parse_Ports(Config const &config, std::ostream &log)
static Port to_Port(ParsedPort const &parsed, std::ostream &log)
static Json::Output makeOutput(Session &session)
Resource::Consumer requestInboundEndpoint(Resource::Manager &manager, beast::IP::Endpoint const &remoteAddress, Role const &role, std::string_view user, std::string_view forwardedFor)
Json::Int constexpr wrong_version
static Json::Value make_json_error(Json::Int code, Json::Value &&message)
std::string base64_decode(std::string_view data)
std::unique_ptr< ServerHandler > make_ServerHandler(Application &app, boost::asio::io_service &io_service, JobQueue &jobQueue, NetworkOPs &networkOPs, Resource::Manager &resourceManager, CollectorManager &cm)
void parse_Port(ParsedPort &port, Section const §ion, std::ostream &log)
Json::Value rpcError(int iError)
bool isUnlimited(Role const &role)
ADMIN and IDENTIFIED roles shall have unlimited resources.
std::shared_ptr< boost::asio::ssl::context > make_SSLContext(std::string const &cipherList)
Create a self-signed SSL context that allows anonymous Diffie Hellman.
Json::Int constexpr method_not_found
Json::Int constexpr forbidden
ServerHandler::Setup setup_ServerHandler(Config const &config, std::ostream &&log)
void logDuration(Json::Value const &request, T const &duration, beast::Journal &journal)
std::string_view forwardedFor(http_request_type const &request)
boost::beast::http::request< boost::beast::http::dynamic_body > http_request_type
std::string to_string(base_uint< Bits, Tag > const &a)
static Handoff statusRequestResponse(http_request_type const &request, boost::beast::http::status status)
static std::string buffers_to_string(ConstBufferSequence const &bs)
static void setup_Client(ServerHandler::Setup &setup)
std::shared_ptr< boost::asio::ssl::context > make_SSLContextAuthed(std::string const &keyFile, std::string const &certFile, std::string const &chainFile, std::string const &cipherList)
Create an authenticated SSL context using the specified files.
std::unique_ptr< Server > make_Server(Handler &handler, boost::asio::io_service &io_service, beast::Journal journal)
Create the HTTP server using the specified handler.
Overlay::Setup setup_Overlay(BasicConfig const &config)
Role requestRole(Role const &required, Port const &port, Json::Value const ¶ms, beast::IP::Endpoint const &remoteIp, std::string_view user)
Return the allowed privilege role.
static std::map< std::string, std::string > build_map(boost::beast::http::fields const &h)
static bool isStatusRequest(http_request_type const &request)
Json::Int constexpr server_overloaded
T remove_suffix(T... args)
static IP::Endpoint from_asio(boost::asio::ip::address const &address)
Used to indicate the result of a server connection handoff.
std::shared_ptr< Writer > response
boost::beast::websocket::permessage_deflate pmd_options
std::optional< std::uint16_t > port
std::vector< boost::asio::ip::network_v4 > admin_nets_v4
std::uint16_t ws_queue_limit
std::vector< boost::asio::ip::network_v6 > secure_gateway_nets_v6
std::set< std::string, boost::beast::iless > protocol
std::string admin_password
std::vector< boost::asio::ip::network_v6 > admin_nets_v6
std::optional< boost::asio::ip::address > ip
std::vector< boost::asio::ip::network_v4 > secure_gateway_nets_v4
Configuration information for a Server listening port.
std::vector< boost::asio::ip::network_v6 > admin_nets_v6
std::set< std::string, boost::beast::iless > protocol
std::vector< boost::asio::ip::network_v6 > secure_gateway_nets_v6
std::vector< boost::asio::ip::network_v4 > secure_gateway_nets_v4
boost::asio::ip::address ip
std::uint16_t ws_queue_limit
std::string admin_password
std::vector< boost::asio::ip::network_v4 > admin_nets_v4
boost::beast::websocket::permessage_deflate pmd_options
std::string admin_password
boost::asio::ip::tcp::endpoint overlay
std::vector< Port > ports