mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Add support for Beast Websockets (RIPD-1097)
This commit is contained in:
committed by
Howard Hinnant
parent
f45e279e06
commit
d7a778ce6a
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
#include <ripple/json/json_forwards.h>
|
#include <ripple/json/json_forwards.h>
|
||||||
#include <ripple/json/json_value.h>
|
#include <ripple/json/json_value.h>
|
||||||
|
#include <boost/asio/buffer.hpp>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
|
||||||
namespace Json
|
namespace Json
|
||||||
@@ -64,6 +64,16 @@ public:
|
|||||||
/// \see Json::operator>>(std::istream&, Json::Value&).
|
/// \see Json::operator>>(std::istream&, Json::Value&).
|
||||||
bool parse ( std::istream& is, Value& root);
|
bool parse ( std::istream& is, Value& root);
|
||||||
|
|
||||||
|
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a> buffer sequence.
|
||||||
|
* \param root [out] Contains the root value of the document if it was
|
||||||
|
* successfully parsed.
|
||||||
|
* \param UTF-8 encoded buffer sequence.
|
||||||
|
* \return \c true if the buffer was successfully parsed, \c false if an error occurred.
|
||||||
|
*/
|
||||||
|
template<class BufferSequence>
|
||||||
|
bool
|
||||||
|
parse(Value& root, BufferSequence const& bs);
|
||||||
|
|
||||||
/** \brief Returns a user friendly string that list errors in the parsed document.
|
/** \brief Returns a user friendly string that list errors in the parsed document.
|
||||||
* \return Formatted error message with the list of errors with their location in
|
* \return Formatted error message with the list of errors with their location in
|
||||||
* the parsed document. An empty string is returned if no error occurred
|
* the parsed document. An empty string is returned if no error occurred
|
||||||
@@ -161,6 +171,20 @@ private:
|
|||||||
Value* lastValue_;
|
Value* lastValue_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<class BufferSequence>
|
||||||
|
bool
|
||||||
|
Reader::parse(Value& root, BufferSequence const& bs)
|
||||||
|
{
|
||||||
|
using namespace boost::asio;
|
||||||
|
for (auto const& b : bs)
|
||||||
|
{
|
||||||
|
auto begin = buffer_cast<const char*>(b);
|
||||||
|
if(! parse(begin, begin + buffer_size(b), root))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/** \brief Read from 'sin' into 'root'.
|
/** \brief Read from 'sin' into 'root'.
|
||||||
|
|
||||||
Always keep comments from the input JSON.
|
Always keep comments from the input JSON.
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
InfoSub (Source& source);
|
||||||
InfoSub (Source& source, Consumer consumer);
|
InfoSub (Source& source, Consumer consumer);
|
||||||
|
|
||||||
virtual ~InfoSub ();
|
virtual ~InfoSub ();
|
||||||
@@ -156,6 +157,14 @@ private:
|
|||||||
hash_set <AccountID> normalSubscriptions_;
|
hash_set <AccountID> normalSubscriptions_;
|
||||||
std::shared_ptr <PathRequest> mPathRequest;
|
std::shared_ptr <PathRequest> mPathRequest;
|
||||||
std::uint64_t mSeq;
|
std::uint64_t mSeq;
|
||||||
|
|
||||||
|
static
|
||||||
|
int
|
||||||
|
assign_id()
|
||||||
|
{
|
||||||
|
static std::atomic<std::uint64_t> id(0);
|
||||||
|
return ++id;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|||||||
@@ -43,12 +43,17 @@ InfoSub::Source::Source (char const* name, Stoppable& parent)
|
|||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
InfoSub::InfoSub (Source& source, Consumer consumer)
|
InfoSub::InfoSub(Source& source)
|
||||||
: m_consumer (consumer)
|
: m_source(source)
|
||||||
, m_source (source)
|
, mSeq(assign_id())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
InfoSub::InfoSub(Source& source, Consumer consumer)
|
||||||
|
: m_consumer(consumer)
|
||||||
|
, m_source(source)
|
||||||
|
, mSeq(assign_id())
|
||||||
{
|
{
|
||||||
static std::atomic <int> s_seq_id (0);
|
|
||||||
mSeq = ++s_seq_id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
InfoSub::~InfoSub ()
|
InfoSub::~InfoSub ()
|
||||||
|
|||||||
@@ -111,10 +111,18 @@ struct WSSession
|
|||||||
{
|
{
|
||||||
std::shared_ptr<void> appDefined;
|
std::shared_ptr<void> appDefined;
|
||||||
|
|
||||||
|
virtual
|
||||||
|
void
|
||||||
|
run() = 0;
|
||||||
|
|
||||||
virtual
|
virtual
|
||||||
Port const&
|
Port const&
|
||||||
port() const = 0;
|
port() const = 0;
|
||||||
|
|
||||||
|
virtual
|
||||||
|
http_request_type const&
|
||||||
|
request() const = 0;
|
||||||
|
|
||||||
virtual
|
virtual
|
||||||
boost::asio::ip::tcp::endpoint const&
|
boost::asio::ip::tcp::endpoint const&
|
||||||
remote_endpoint() const = 0;
|
remote_endpoint() const = 0;
|
||||||
@@ -127,6 +135,14 @@ struct WSSession
|
|||||||
virtual
|
virtual
|
||||||
void
|
void
|
||||||
close() = 0;
|
close() = 0;
|
||||||
|
|
||||||
|
/** Indicate that the response is complete.
|
||||||
|
The handler should call this when it has completed writing
|
||||||
|
the response.
|
||||||
|
*/
|
||||||
|
virtual
|
||||||
|
void
|
||||||
|
complete() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|||||||
@@ -67,7 +67,6 @@ protected:
|
|||||||
|
|
||||||
// Max seconds without completing a message
|
// Max seconds without completing a message
|
||||||
timeoutSeconds = 30
|
timeoutSeconds = 30
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct buffer
|
struct buffer
|
||||||
@@ -105,7 +104,6 @@ protected:
|
|||||||
bool complete_ = false;
|
bool complete_ = false;
|
||||||
boost::system::error_code ec_;
|
boost::system::error_code ec_;
|
||||||
|
|
||||||
clock_type::time_point when_;
|
|
||||||
int request_count_ = 0;
|
int request_count_ = 0;
|
||||||
std::size_t bytes_in_ = 0;
|
std::size_t bytes_in_ = 0;
|
||||||
std::size_t bytes_out_ = 0;
|
std::size_t bytes_out_ = 0;
|
||||||
@@ -233,7 +231,6 @@ BaseHTTPPeer<Impl>::BaseHTTPPeer (Port const& port, Handler& handler,
|
|||||||
id_ = std::string("#") + std::to_string(nid_) + " ";
|
id_ = std::string("#") + std::to_string(nid_) + " ";
|
||||||
JLOG(journal_.trace()) << id_ <<
|
JLOG(journal_.trace()) << id_ <<
|
||||||
"accept: " << remote_address_.address();
|
"accept: " << remote_address_.address();
|
||||||
when_ = clock_type::now();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class Impl>
|
template <class Impl>
|
||||||
|
|||||||
@@ -43,6 +43,12 @@ protected:
|
|||||||
using BasePeer<Impl>::fail;
|
using BasePeer<Impl>::fail;
|
||||||
using BasePeer<Impl>::strand_;
|
using BasePeer<Impl>::strand_;
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
// Max seconds without completing a message
|
||||||
|
timeoutSeconds = 30
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class BasePeer<Impl>;
|
friend class BasePeer<Impl>;
|
||||||
|
|
||||||
@@ -52,6 +58,8 @@ private:
|
|||||||
beast::streambuf wb_;
|
beast::streambuf wb_;
|
||||||
std::list<std::shared_ptr<WSMsg>> wq_;
|
std::list<std::shared_ptr<WSMsg>> wq_;
|
||||||
bool do_close_ = false;
|
bool do_close_ = false;
|
||||||
|
beast::websocket::close_reason cr_;
|
||||||
|
waitable_timer timer_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template<class Body, class Headers>
|
template<class Body, class Headers>
|
||||||
@@ -64,7 +72,7 @@ public:
|
|||||||
beast::Journal journal);
|
beast::Journal journal);
|
||||||
|
|
||||||
void
|
void
|
||||||
run();
|
run() override;
|
||||||
|
|
||||||
//
|
//
|
||||||
// WSSession
|
// WSSession
|
||||||
@@ -76,6 +84,12 @@ public:
|
|||||||
return this->port_;
|
return this->port_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
http_request_type const&
|
||||||
|
request() const override
|
||||||
|
{
|
||||||
|
return this->request_;
|
||||||
|
}
|
||||||
|
|
||||||
boost::asio::ip::tcp::endpoint const&
|
boost::asio::ip::tcp::endpoint const&
|
||||||
remote_endpoint() const override
|
remote_endpoint() const override
|
||||||
{
|
{
|
||||||
@@ -88,6 +102,9 @@ public:
|
|||||||
void
|
void
|
||||||
close() override;
|
close() override;
|
||||||
|
|
||||||
|
void
|
||||||
|
complete() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
struct identity
|
struct identity
|
||||||
{
|
{
|
||||||
@@ -138,6 +155,15 @@ protected:
|
|||||||
virtual
|
virtual
|
||||||
void
|
void
|
||||||
do_close() = 0;
|
do_close() = 0;
|
||||||
|
|
||||||
|
void
|
||||||
|
start_timer();
|
||||||
|
|
||||||
|
void
|
||||||
|
cancel_timer();
|
||||||
|
|
||||||
|
void
|
||||||
|
on_timer(error_code ec);
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -154,6 +180,7 @@ BaseWSPeer<Impl>::BaseWSPeer(
|
|||||||
: BasePeer<Impl>(port, handler, remote_address,
|
: BasePeer<Impl>(port, handler, remote_address,
|
||||||
io_service, journal)
|
io_service, journal)
|
||||||
, request_(std::move(request))
|
, request_(std::move(request))
|
||||||
|
, timer_(io_service)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -175,11 +202,21 @@ template<class Impl>
|
|||||||
void
|
void
|
||||||
BaseWSPeer<Impl>::send(std::shared_ptr<WSMsg> w)
|
BaseWSPeer<Impl>::send(std::shared_ptr<WSMsg> w)
|
||||||
{
|
{
|
||||||
|
// Maximum send queue size
|
||||||
|
static std::size_t constexpr limit = 100;
|
||||||
if(! strand_.running_in_this_thread())
|
if(! strand_.running_in_this_thread())
|
||||||
return strand_.post(std::bind(
|
return strand_.post(std::bind(
|
||||||
&BaseWSPeer::send, impl().shared_from_this(),
|
&BaseWSPeer::send, impl().shared_from_this(),
|
||||||
std::move(w)));
|
std::move(w)));
|
||||||
wq_.emplace_back(std::move(w));
|
if (wq_.size() >= limit)
|
||||||
|
{
|
||||||
|
cr_.code = static_cast<beast::websocket::close_code::value>(4000);
|
||||||
|
cr_.reason = "Client is too slow.";
|
||||||
|
do_close_ = true;
|
||||||
|
wq_.erase(std::next(wq_.begin()), wq_.end());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
wq_.emplace_back(std::move(w));
|
||||||
if(wq_.size() == 1)
|
if(wq_.size() == 1)
|
||||||
on_write({});
|
on_write({});
|
||||||
}
|
}
|
||||||
@@ -199,6 +236,16 @@ BaseWSPeer<Impl>::close()
|
|||||||
beast::asio::placeholders::error)));
|
beast::asio::placeholders::error)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class Impl>
|
||||||
|
void
|
||||||
|
BaseWSPeer<Impl>::complete()
|
||||||
|
{
|
||||||
|
if (! strand_.running_in_this_thread())
|
||||||
|
return strand_.post(std::bind(
|
||||||
|
&BaseWSPeer::complete, impl().shared_from_this()));
|
||||||
|
do_read();
|
||||||
|
}
|
||||||
|
|
||||||
template<class Impl>
|
template<class Impl>
|
||||||
void
|
void
|
||||||
BaseWSPeer<Impl>::on_write_sb(error_code const& ec)
|
BaseWSPeer<Impl>::on_write_sb(error_code const& ec)
|
||||||
@@ -222,6 +269,7 @@ template<class Impl>
|
|||||||
void
|
void
|
||||||
BaseWSPeer<Impl>::on_write(error_code const& ec)
|
BaseWSPeer<Impl>::on_write(error_code const& ec)
|
||||||
{
|
{
|
||||||
|
cancel_timer();
|
||||||
if(ec)
|
if(ec)
|
||||||
return fail(ec, "write");
|
return fail(ec, "write");
|
||||||
auto& w = *wq_.front();
|
auto& w = *wq_.front();
|
||||||
@@ -231,6 +279,7 @@ BaseWSPeer<Impl>::on_write(error_code const& ec)
|
|||||||
impl().shared_from_this()));
|
impl().shared_from_this()));
|
||||||
if(boost::indeterminate(result.first))
|
if(boost::indeterminate(result.first))
|
||||||
return;
|
return;
|
||||||
|
start_timer();
|
||||||
if(! result.first)
|
if(! result.first)
|
||||||
impl().ws_.async_write_frame(
|
impl().ws_.async_write_frame(
|
||||||
result.first, result.second, strand_.wrap(std::bind(
|
result.first, result.second, strand_.wrap(std::bind(
|
||||||
@@ -251,7 +300,7 @@ BaseWSPeer<Impl>::on_write_fin(error_code const& ec)
|
|||||||
return fail(ec, "write_fin");
|
return fail(ec, "write_fin");
|
||||||
wq_.pop_front();
|
wq_.pop_front();
|
||||||
if(do_close_)
|
if(do_close_)
|
||||||
impl().ws_.async_close({}, strand_.wrap(std::bind(
|
impl().ws_.async_close(cr_, strand_.wrap(std::bind(
|
||||||
&BaseWSPeer::on_close, impl().shared_from_this(),
|
&BaseWSPeer::on_close, impl().shared_from_this(),
|
||||||
beast::asio::placeholders::error)));
|
beast::asio::placeholders::error)));
|
||||||
else if(! wq_.empty())
|
else if(! wq_.empty())
|
||||||
@@ -269,6 +318,7 @@ BaseWSPeer<Impl>::do_read()
|
|||||||
impl().ws_.async_read(op_, rb_, strand_.wrap(
|
impl().ws_.async_read(op_, rb_, strand_.wrap(
|
||||||
std::bind(&BaseWSPeer::on_read,
|
std::bind(&BaseWSPeer::on_read,
|
||||||
impl().shared_from_this(), placeholders::error)));
|
impl().shared_from_this(), placeholders::error)));
|
||||||
|
cancel_timer();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Impl>
|
template<class Impl>
|
||||||
@@ -286,7 +336,6 @@ BaseWSPeer<Impl>::on_read(error_code const& ec)
|
|||||||
std::back_inserter(b));
|
std::back_inserter(b));
|
||||||
this->handler_.onWSMessage(impl().shared_from_this(), b);
|
this->handler_.onWSMessage(impl().shared_from_this(), b);
|
||||||
rb_.consume(rb_.size());
|
rb_.consume(rb_.size());
|
||||||
do_read();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Impl>
|
template<class Impl>
|
||||||
@@ -296,6 +345,42 @@ BaseWSPeer<Impl>::on_close(error_code const& ec)
|
|||||||
// great
|
// great
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class Impl>
|
||||||
|
void
|
||||||
|
BaseWSPeer<Impl>::start_timer()
|
||||||
|
{
|
||||||
|
// Max seconds without completing a message
|
||||||
|
static constexpr std::chrono::seconds timeout{30};
|
||||||
|
error_code ec;
|
||||||
|
timer_.expires_from_now (timeout, ec);
|
||||||
|
if (ec)
|
||||||
|
return fail (ec, "start_timer");
|
||||||
|
timer_.async_wait (strand_.wrap (std::bind (
|
||||||
|
&BaseWSPeer<Impl>::on_timer, impl().shared_from_this(),
|
||||||
|
beast::asio::placeholders::error)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience for discarding the error code
|
||||||
|
template <class Impl>
|
||||||
|
void
|
||||||
|
BaseWSPeer<Impl>::cancel_timer()
|
||||||
|
{
|
||||||
|
error_code ec;
|
||||||
|
timer_.cancel(ec);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when session times out
|
||||||
|
template <class Impl>
|
||||||
|
void
|
||||||
|
BaseWSPeer<Impl>::on_timer (error_code ec)
|
||||||
|
{
|
||||||
|
if (ec == boost::asio::error::operation_aborted)
|
||||||
|
return;
|
||||||
|
if (! ec)
|
||||||
|
ec = boost::system::errc::make_error_code (
|
||||||
|
boost::system::errc::timed_out);
|
||||||
|
fail (ec, "timer");
|
||||||
|
}
|
||||||
} // ripple
|
} // ripple
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -99,7 +99,6 @@ PlainHTTPPeer::websocketUpgrade()
|
|||||||
port_, handler_, remote_address_,
|
port_, handler_, remote_address_,
|
||||||
std::move(message_), std::move(stream_),
|
std::move(message_), std::move(stream_),
|
||||||
journal_);
|
journal_);
|
||||||
ws->run();
|
|
||||||
return ws;
|
return ws;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
|
|
||||||
namespace ripple {
|
namespace ripple {
|
||||||
|
|
||||||
|
// Detects legacy websockets only.
|
||||||
bool
|
bool
|
||||||
Port::websockets() const
|
Port::websockets() const
|
||||||
{
|
{
|
||||||
@@ -33,7 +34,9 @@ bool
|
|||||||
Port::secure() const
|
Port::secure() const
|
||||||
{
|
{
|
||||||
return protocol.count("peer") > 0 ||
|
return protocol.count("peer") > 0 ||
|
||||||
protocol.count("https") > 0 || protocol.count("wss") > 0;
|
protocol.count("https") > 0 ||
|
||||||
|
protocol.count("wss") > 0 ||
|
||||||
|
protocol.count("wss2") > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string
|
std::string
|
||||||
|
|||||||
@@ -104,7 +104,6 @@ SSLHTTPPeer::websocketUpgrade()
|
|||||||
port_, handler_, remote_address_,
|
port_, handler_, remote_address_,
|
||||||
std::move(message_), std::move(ssl_bundle_),
|
std::move(message_), std::move(ssl_bundle_),
|
||||||
journal_);
|
journal_);
|
||||||
ws->run();
|
|
||||||
return ws;
|
return ws;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,10 +117,11 @@ SSLHTTPPeer::do_handshake (yield_context yield)
|
|||||||
stream_type::server, read_buf_.data(), yield[ec]));
|
stream_type::server, read_buf_.data(), yield[ec]));
|
||||||
cancel_timer();
|
cancel_timer();
|
||||||
if (ec)
|
if (ec)
|
||||||
return fail (ec, "handshake");
|
return fail(ec, "handshake");
|
||||||
bool const http =
|
bool const http =
|
||||||
port().protocol.count("peer") > 0 ||
|
port().protocol.count("peer") > 0 ||
|
||||||
//|| port().protocol.count("wss") > 0
|
//port().protocol.count("wss") > 0 ||
|
||||||
|
port().protocol.count("wss2") > 0 ||
|
||||||
port().protocol.count("https") > 0;
|
port().protocol.count("https") > 0;
|
||||||
if (http)
|
if (http)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -124,37 +124,35 @@ ServerHandlerImp::onHandoff (Session& session,
|
|||||||
boost::asio::ip::tcp::endpoint remote_address) ->
|
boost::asio::ip::tcp::endpoint remote_address) ->
|
||||||
Handoff
|
Handoff
|
||||||
{
|
{
|
||||||
if (session.port().protocol.count("wss2") > 0 &&
|
if(isWebsocketUpgrade(request))
|
||||||
isWebsocketUpgrade (request))
|
|
||||||
{
|
{
|
||||||
// VFALCO TODO
|
|
||||||
Resource::Consumer usage;
|
|
||||||
//if (isUnlimited (role))
|
|
||||||
// usage = m_resourceManager.newUnlimitedEndpoint (
|
|
||||||
// remoteIPAddress.to_string());
|
|
||||||
//else
|
|
||||||
usage = m_resourceManager.newInboundEndpoint(
|
|
||||||
beast::IP::from_asio(remote_address));
|
|
||||||
auto const ws = session.websocketUpgrade();
|
|
||||||
ws->appDefined = std::make_shared<WSInfoSub>(
|
|
||||||
m_networkOPs, usage, ws);
|
|
||||||
Handoff handoff;
|
Handoff handoff;
|
||||||
handoff.moved = true;
|
if(session.port().protocol.count("wss2") > 0)
|
||||||
return handoff;
|
{
|
||||||
|
auto const ws = session.websocketUpgrade();
|
||||||
|
auto is = std::make_shared<WSInfoSub>(m_networkOPs, ws);
|
||||||
|
is->getConsumer() = requestInboundEndpoint(
|
||||||
|
m_resourceManager,
|
||||||
|
beast::IPAddressConversion::from_asio(remote_address),
|
||||||
|
session.port(), is->user());
|
||||||
|
ws->appDefined = std::move(is);
|
||||||
|
ws->run();
|
||||||
|
handoff.moved = true;
|
||||||
|
return handoff;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(session.port().protocol.count("wss") > 0)
|
||||||
|
return handoff; // Pass to websocket
|
||||||
}
|
}
|
||||||
if (session.port().protocol.count("wss") > 0 &&
|
|
||||||
isWebsocketUpgrade (request))
|
if(session.port().protocol.count("peer") > 0)
|
||||||
{
|
{
|
||||||
// Pass to websockets
|
return app_.overlay().onHandoff(std::move(bundle),
|
||||||
Handoff handoff;
|
|
||||||
// handoff.moved = true;
|
|
||||||
return handoff;
|
|
||||||
}
|
|
||||||
if (session.port().protocol.count("peer") > 0)
|
|
||||||
return app_.overlay().onHandoff (std::move(bundle),
|
|
||||||
std::move(request), remote_address);
|
std::move(request), remote_address);
|
||||||
// Pass through to legacy onRequest
|
}
|
||||||
return Handoff{};
|
|
||||||
|
// Pass to legacy onRequest
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto
|
auto
|
||||||
@@ -164,34 +162,22 @@ ServerHandlerImp::onHandoff (Session& session,
|
|||||||
boost::asio::ip::tcp::endpoint remote_address) ->
|
boost::asio::ip::tcp::endpoint remote_address) ->
|
||||||
Handoff
|
Handoff
|
||||||
{
|
{
|
||||||
if (session.port().protocol.count("ws2") > 0 &&
|
Handoff handoff;
|
||||||
|
if(session.port().protocol.count("ws2") > 0 &&
|
||||||
isWebsocketUpgrade (request))
|
isWebsocketUpgrade (request))
|
||||||
{
|
{
|
||||||
// VFALCO TODO
|
|
||||||
Resource::Consumer usage;
|
|
||||||
//if (isUnlimited (role))
|
|
||||||
// usage = m_resourceManager.newUnlimitedEndpoint (
|
|
||||||
// remoteIPAddress.to_string());
|
|
||||||
//else
|
|
||||||
usage = m_resourceManager.newInboundEndpoint(
|
|
||||||
beast::IP::from_asio(remote_address));
|
|
||||||
auto const ws = session.websocketUpgrade();
|
auto const ws = session.websocketUpgrade();
|
||||||
ws->appDefined = std::make_shared<WSInfoSub>(
|
auto is = std::make_shared<WSInfoSub>(m_networkOPs, ws);
|
||||||
m_networkOPs, usage, ws);
|
is->getConsumer() = requestInboundEndpoint(
|
||||||
Handoff handoff;
|
m_resourceManager,
|
||||||
|
beast::IPAddressConversion::from_asio(remote_address),
|
||||||
|
session.port(), is->user());
|
||||||
|
ws->appDefined = std::move(is);
|
||||||
|
ws->run();
|
||||||
handoff.moved = true;
|
handoff.moved = true;
|
||||||
return handoff;
|
|
||||||
}
|
}
|
||||||
if (session.port().protocol.count("ws") > 0 &&
|
// Otherwise pass to legacy onRequest or websocket
|
||||||
isWebsocketUpgrade (request))
|
return handoff;
|
||||||
{
|
|
||||||
// Pass to websockets
|
|
||||||
Handoff handoff;
|
|
||||||
// handoff.moved = true;
|
|
||||||
return handoff;
|
|
||||||
}
|
|
||||||
// Pass through to legacy onRequest
|
|
||||||
return Handoff{};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline
|
static inline
|
||||||
@@ -219,6 +205,21 @@ build_map(beast::http::headers const& h)
|
|||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class ConstBufferSequence>
|
||||||
|
static
|
||||||
|
std::string
|
||||||
|
buffers_to_string(ConstBufferSequence const& bs)
|
||||||
|
{
|
||||||
|
using boost::asio::buffer_cast;
|
||||||
|
using boost::asio::buffer_size;
|
||||||
|
std::string s;
|
||||||
|
s.reserve(buffer_size(bs));
|
||||||
|
for(auto const& b : bs)
|
||||||
|
s.append(buffer_cast<char const*>(b),
|
||||||
|
buffer_size(b));
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ServerHandlerImp::onRequest (Session& session)
|
ServerHandlerImp::onRequest (Session& session)
|
||||||
{
|
{
|
||||||
@@ -252,29 +253,37 @@ ServerHandlerImp::onWSMessage(
|
|||||||
std::shared_ptr<WSSession> session,
|
std::shared_ptr<WSSession> session,
|
||||||
std::vector<boost::asio::const_buffer> const& buffers)
|
std::vector<boost::asio::const_buffer> const& buffers)
|
||||||
{
|
{
|
||||||
// VFALCO This is inefficient, the JSON
|
|
||||||
// should be parsed from the buffer sequence.
|
|
||||||
std::string s;
|
|
||||||
s.reserve(boost::asio::buffer_size(buffers));
|
|
||||||
std::copy(boost::asio::buffers_begin(buffers),
|
|
||||||
boost::asio::buffers_end(buffers),
|
|
||||||
std::back_inserter(s));
|
|
||||||
//m_journal.error << "Recv: " << s;
|
|
||||||
Json::Value jv;
|
Json::Value jv;
|
||||||
// VFALCO should we parse a coroutine instead?
|
auto const size = boost::asio::buffer_size(buffers);
|
||||||
if(! Json::Reader{}.parse(s, jv))
|
if (size > RPC::Tuning::maxRequestSize ||
|
||||||
|
! Json::Reader{}.parse(jv, buffers) ||
|
||||||
|
! jv ||
|
||||||
|
! jv.isObject())
|
||||||
{
|
{
|
||||||
// TODO Send error
|
Json::Value jvResult(Json::objectValue);
|
||||||
|
jvResult[jss::type] = jss::error;
|
||||||
|
jvResult[jss::error] = "jsonInvalid";
|
||||||
|
jvResult[jss::value] = buffers_to_string(buffers);
|
||||||
|
beast::streambuf sb;
|
||||||
|
Json::stream(jvResult,
|
||||||
|
[&sb](auto const p, auto const n)
|
||||||
|
{
|
||||||
|
sb.commit(boost::asio::buffer_copy(
|
||||||
|
sb.prepare(n), boost::asio::buffer(p, n)));
|
||||||
|
});
|
||||||
|
session->send(std::make_shared<
|
||||||
|
StreambufWSMsg<decltype(sb)>>(std::move(sb)));
|
||||||
|
session->complete();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_jobQueue.postCoro(jtCLIENT, "WS-Client",
|
m_jobQueue.postCoro(jtCLIENT, "WS-Client",
|
||||||
[this, session = std::move(session),
|
[this, session = std::move(session),
|
||||||
jv = std::move(jv)](auto const& coro)
|
jv = std::move(jv)](auto const& jc)
|
||||||
{
|
{
|
||||||
auto const jr =
|
auto const jr =
|
||||||
this->processSession(session, coro, jv);
|
this->processSession(session, jc, jv);
|
||||||
beast::streambuf sb;
|
beast::streambuf sb;
|
||||||
//m_journal.error << "Send: " << to_string(jr);
|
|
||||||
Json::stream(jr,
|
Json::stream(jr,
|
||||||
[&sb](auto const p, auto const n)
|
[&sb](auto const p, auto const n)
|
||||||
{
|
{
|
||||||
@@ -283,6 +292,7 @@ ServerHandlerImp::onWSMessage(
|
|||||||
});
|
});
|
||||||
session->send(std::make_shared<
|
session->send(std::make_shared<
|
||||||
StreambufWSMsg<decltype(sb)>>(std::move(sb)));
|
StreambufWSMsg<decltype(sb)>>(std::move(sb)));
|
||||||
|
session->complete();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,58 +318,44 @@ ServerHandlerImp::processSession(
|
|||||||
std::shared_ptr<JobCoro> const& coro,
|
std::shared_ptr<JobCoro> const& coro,
|
||||||
Json::Value const& jv)
|
Json::Value const& jv)
|
||||||
{
|
{
|
||||||
auto is = std::static_pointer_cast<InfoSub> (session->appDefined);
|
auto is = std::static_pointer_cast<WSInfoSub> (session->appDefined);
|
||||||
/*
|
if (is->getConsumer().disconnect())
|
||||||
if (getConsumer().disconnect ())
|
|
||||||
{
|
{
|
||||||
disconnect ();
|
session->close();
|
||||||
return rpcError (rpcSLOW_DOWN);
|
return rpcError(rpcSLOW_DOWN);
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
// Requests without "command" are invalid.
|
// Requests without "command" are invalid.
|
||||||
//
|
Json::Value jr(Json::objectValue);
|
||||||
if (!jv.isMember (jss::command))
|
if (! jv.isMember (jss::command))
|
||||||
{
|
{
|
||||||
Json::Value jr (Json::objectValue);
|
jr[jss::type] = jss::response;
|
||||||
|
jr[jss::status] = jss::error;
|
||||||
jr[jss::type] = jss::response;
|
jr[jss::error] = jss::missingCommand;
|
||||||
jr[jss::status] = jss::error;
|
|
||||||
jr[jss::error] = jss::missingCommand;
|
|
||||||
jr[jss::request] = jv;
|
jr[jss::request] = jv;
|
||||||
|
|
||||||
if (jv.isMember (jss::id))
|
if (jv.isMember (jss::id))
|
||||||
{
|
|
||||||
jr[jss::id] = jv[jss::id];
|
jr[jss::id] = jv[jss::id];
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
getConsumer().charge (Resource::feeInvalidRPC);
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
is->getConsumer().charge(Resource::feeInvalidRPC);
|
||||||
return jr;
|
return jr;
|
||||||
}
|
}
|
||||||
|
|
||||||
Resource::Charge loadType = Resource::feeReferenceRPC;
|
Resource::Charge loadType = Resource::feeReferenceRPC;
|
||||||
Json::Value jr (Json::objectValue);
|
auto required = RPC::roleRequired(jv[jss::command].asString());
|
||||||
|
auto role = requestRole(
|
||||||
auto required = RPC::roleRequired (jv[jss::command].asString());
|
required,
|
||||||
// VFALCO TODO Get identity/credentials from HTTP headers
|
session->port(),
|
||||||
std::string const user = "";
|
jv,
|
||||||
std::string const fwdfor = "";
|
|
||||||
auto role = requestRole (required, session->port(), jv,
|
|
||||||
beast::IP::from_asio(session->remote_endpoint().address()),
|
beast::IP::from_asio(session->remote_endpoint().address()),
|
||||||
user);
|
is->user());
|
||||||
|
|
||||||
if (Role::FORBID == role)
|
if (Role::FORBID == role)
|
||||||
{
|
{
|
||||||
jr[jss::result] = rpcError (rpcFORBIDDEN);
|
jr[jss::result] = rpcError (rpcFORBIDDEN);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// VFALCO TODO InfoSub parameter in context
|
|
||||||
RPC::Context context{
|
RPC::Context context{
|
||||||
app_.journal ("RPCHandler"),
|
app_.journal("RPCHandler"),
|
||||||
jv,
|
jv,
|
||||||
app_,
|
app_,
|
||||||
loadType,
|
loadType,
|
||||||
@@ -369,28 +365,24 @@ ServerHandlerImp::processSession(
|
|||||||
role,
|
role,
|
||||||
coro,
|
coro,
|
||||||
is,
|
is,
|
||||||
{ user, fwdfor }
|
{is->user(), is->forwarded_for()}
|
||||||
};
|
};
|
||||||
RPC::doCommand (context, jr[jss::result]);
|
RPC::doCommand(context, jr[jss::result]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
is->getConsumer().charge(loadType);
|
||||||
getConsumer().charge (loadType);
|
if (is->getConsumer().warn())
|
||||||
if (getConsumer().warn ())
|
|
||||||
{
|
|
||||||
jr[jss::warning] = jss::load;
|
jr[jss::warning] = jss::load;
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Currently we will simply unwrap errors returned by the RPC
|
// Currently we will simply unwrap errors returned by the RPC
|
||||||
// API, in the future maybe we can make the responses
|
// API, in the future maybe we can make the responses
|
||||||
// consistent.
|
// consistent.
|
||||||
//
|
//
|
||||||
// Regularize result. This is duplicate code.
|
// Regularize result. This is duplicate code.
|
||||||
if (jr[jss::result].isMember (jss::error))
|
if (jr[jss::result].isMember(jss::error))
|
||||||
{
|
{
|
||||||
jr = jr[jss::result];
|
jr = jr[jss::result];
|
||||||
jr[jss::status] = jss::error;
|
jr[jss::status] = jss::error;
|
||||||
jr[jss::request] = jv;
|
jr[jss::request] = jv;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -399,47 +391,27 @@ ServerHandlerImp::processSession(
|
|||||||
jr[jss::status] = jss::success;
|
jr[jss::status] = jss::success;
|
||||||
|
|
||||||
// For testing resource limits on this connection.
|
// For testing resource limits on this connection.
|
||||||
if (jv[jss::command].asString() == "ping")
|
if (is->getConsumer().isUnlimited() &&
|
||||||
{
|
jv[jss::command].asString() == "ping")
|
||||||
/*
|
|
||||||
if (getConsumer().isUnlimited())
|
|
||||||
jr[jss::unlimited] = true;
|
jr[jss::unlimited] = true;
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jv.isMember (jss::id))
|
if (jv.isMember(jss::id))
|
||||||
{
|
|
||||||
jr[jss::id] = jv[jss::id];
|
jr[jss::id] = jv[jss::id];
|
||||||
}
|
|
||||||
|
|
||||||
jr[jss::type] = jss::response;
|
jr[jss::type] = jss::response;
|
||||||
|
|
||||||
return jr;
|
return jr;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class ConstBufferSequence>
|
|
||||||
static
|
|
||||||
std::string
|
|
||||||
buffers_to_string(ConstBufferSequence const& bs)
|
|
||||||
{
|
|
||||||
using boost::asio::buffer_cast;
|
|
||||||
using boost::asio::buffer_size;
|
|
||||||
std::string s;
|
|
||||||
s.reserve(buffer_size(bs));
|
|
||||||
for(auto const& b : bs)
|
|
||||||
s.append(buffer_cast<char const*>(b),
|
|
||||||
buffer_size(b));
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run as a coroutine.
|
// Run as a coroutine.
|
||||||
void
|
void
|
||||||
ServerHandlerImp::processSession (std::shared_ptr<Session> const& session,
|
ServerHandlerImp::processSession (std::shared_ptr<Session> const& session,
|
||||||
std::shared_ptr<JobCoro> jobCoro)
|
std::shared_ptr<JobCoro> jobCoro)
|
||||||
{
|
{
|
||||||
processRequest (session->port(), buffers_to_string(session->request().body.data()),
|
processRequest (
|
||||||
session->remoteAddress().at_port (0), makeOutput (*session), jobCoro,
|
session->port(), buffers_to_string(
|
||||||
|
session->request().body.data()),
|
||||||
|
session->remoteAddress().at_port (0),
|
||||||
|
makeOutput (*session), jobCoro,
|
||||||
[&]
|
[&]
|
||||||
{
|
{
|
||||||
auto const iter =
|
auto const iter =
|
||||||
@@ -506,10 +478,10 @@ ServerHandlerImp::processRequest (Port const& port,
|
|||||||
/* ---------------------------------------------------------------------- */
|
/* ---------------------------------------------------------------------- */
|
||||||
auto role = Role::FORBID;
|
auto role = Role::FORBID;
|
||||||
auto required = RPC::roleRequired(id.asString());
|
auto required = RPC::roleRequired(id.asString());
|
||||||
|
if (jsonRPC.isMember("params") &&
|
||||||
if (jsonRPC.isObject() && jsonRPC.isMember("params") &&
|
jsonRPC["params"].isArray() &&
|
||||||
jsonRPC["params"].isArray() && jsonRPC["params"].size() > 0 &&
|
jsonRPC["params"].size() > 0 &&
|
||||||
jsonRPC["params"][Json::UInt(0)].isObject())
|
jsonRPC["params"][Json::UInt(0)].isObject())
|
||||||
{
|
{
|
||||||
role = requestRole(required, port, jsonRPC["params"][Json::UInt(0)],
|
role = requestRole(required, port, jsonRPC["params"][Json::UInt(0)],
|
||||||
remoteIPAddress, user);
|
remoteIPAddress, user);
|
||||||
|
|||||||
@@ -45,13 +45,39 @@ bool operator< (Port const& lhs, Port const& rhs)
|
|||||||
class WSInfoSub : public InfoSub
|
class WSInfoSub : public InfoSub
|
||||||
{
|
{
|
||||||
std::weak_ptr<WSSession> ws_;
|
std::weak_ptr<WSSession> ws_;
|
||||||
|
std::string user_;
|
||||||
|
std::string fwdfor_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
WSInfoSub(Source& source, Consumer consumer,
|
WSInfoSub(Source& source,
|
||||||
std::shared_ptr<WSSession> const& ws)
|
std::shared_ptr<WSSession> const& ws)
|
||||||
: InfoSub(source, consumer)
|
: InfoSub(source)
|
||||||
, ws_(ws)
|
, ws_(ws)
|
||||||
{
|
{
|
||||||
|
auto const& h = ws->request().headers;
|
||||||
|
auto it = h.find("X-User");
|
||||||
|
if (it != h.end() &&
|
||||||
|
isIdentified(
|
||||||
|
ws->port(), beast::IPAddressConversion::from_asio(
|
||||||
|
ws->remote_endpoint()).address(), it->second))
|
||||||
|
{
|
||||||
|
user_ = it->second;
|
||||||
|
it = h.find("X-Forwarded-For");
|
||||||
|
if (it != h.end())
|
||||||
|
fwdfor_ = it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
user() const
|
||||||
|
{
|
||||||
|
return user_;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string
|
||||||
|
forwarded_for() const
|
||||||
|
{
|
||||||
|
return fwdfor_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|||||||
Reference in New Issue
Block a user