mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
Work on WS server.
This commit is contained in:
196
src/WSDoor.cpp
196
src/WSDoor.cpp
@@ -1,18 +1,31 @@
|
|||||||
|
|
||||||
#include "WSDoor.h"
|
#include "WSDoor.h"
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
#include <boost/bind.hpp>
|
|
||||||
#include <boost/mem_fn.hpp>
|
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "Config.h"
|
#include "Config.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
#include "NetworkOPs.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
using namespace std;
|
#include <iostream>
|
||||||
using namespace boost::asio::ip;
|
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
#include <boost/foreach.hpp>
|
||||||
|
#include <boost/mem_fn.hpp>
|
||||||
|
|
||||||
|
#include "../json/reader.h"
|
||||||
|
#include "../json/writer.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// This is a light weight, untrusted interface for web clients.
|
||||||
|
// For now we don't provide proof. Later we will.
|
||||||
|
//
|
||||||
|
|
||||||
|
//
|
||||||
|
// Strategy:
|
||||||
|
// - We only talk to NetworkOPs (so we will work even in thin mode)
|
||||||
|
// - NetworkOPs is smart enough to subscribe and or pass back messages
|
||||||
|
//
|
||||||
|
|
||||||
// Generate DH for SSL connection.
|
// Generate DH for SSL connection.
|
||||||
static DH* handleTmpDh(SSL* ssl, int is_export, int iKeyLength)
|
static DH* handleTmpDh(SSL* ssl, int is_export, int iKeyLength)
|
||||||
@@ -20,17 +33,64 @@ static DH* handleTmpDh(SSL* ssl, int is_export, int iKeyLength)
|
|||||||
return 512 == iKeyLength ? theApp->getWallet().getDh512() : theApp->getWallet().getDh1024();
|
return 512 == iKeyLength ? theApp->getWallet().getDh512() : theApp->getWallet().getDh1024();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename endpoint_type>
|
||||||
|
class WSServerHandler;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Storage for connection specific info
|
||||||
|
// - Subscriptions
|
||||||
|
//
|
||||||
|
class WSConnection : public InfoSub
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef typename websocketpp::WSDOOR_SERVER::handler::connection_ptr connection_ptr;
|
||||||
|
typedef typename websocketpp::WSDOOR_SERVER::handler::message_ptr message_ptr;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
WSServerHandler<websocketpp::WSDOOR_SERVER>* mHandler;
|
||||||
|
connection_ptr mConnection;
|
||||||
|
|
||||||
|
public:
|
||||||
|
WSConnection()
|
||||||
|
: mHandler((WSServerHandler<websocketpp::WSDOOR_SERVER>*)(NULL)),
|
||||||
|
mConnection(connection_ptr()) { ; }
|
||||||
|
|
||||||
|
WSConnection(WSServerHandler<websocketpp::WSDOOR_SERVER>* wshpHandler, connection_ptr cpConnection)
|
||||||
|
: mHandler(wshpHandler), mConnection(cpConnection) { ; }
|
||||||
|
|
||||||
|
~WSConnection()
|
||||||
|
{
|
||||||
|
// XXX Unsubscribe.
|
||||||
|
nothing();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement overriden functions from base class:
|
||||||
|
void send(const Json::Value& jvObj);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// A single instance of this object is made.
|
// A single instance of this object is made.
|
||||||
// This instance dispatches all events. There is no per connection persistency.
|
// This instance dispatches all events. There is no per connection persistency.
|
||||||
template <typename endpoint_type>
|
template <typename endpoint_type>
|
||||||
class WSServerHandler : public endpoint_type::handler {
|
class WSServerHandler : public endpoint_type::handler
|
||||||
private:
|
{
|
||||||
boost::shared_ptr<boost::asio::ssl::context> mCtx;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef typename endpoint_type::handler::connection_ptr connection_ptr;
|
typedef typename endpoint_type::handler::connection_ptr connection_ptr;
|
||||||
typedef typename endpoint_type::handler::message_ptr message_ptr;
|
typedef typename endpoint_type::handler::message_ptr message_ptr;
|
||||||
|
|
||||||
|
// Private reasons to close.
|
||||||
|
enum {
|
||||||
|
crTooSlow = 4000, // Client is too slow.
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
boost::shared_ptr<boost::asio::ssl::context> mCtx;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
boost::mutex mMapLock;
|
||||||
|
boost::unordered_map<connection_ptr, WSConnection> mMap;
|
||||||
|
|
||||||
|
public:
|
||||||
WSServerHandler(boost::shared_ptr<boost::asio::ssl::context> spCtx) : mCtx(spCtx) {}
|
WSServerHandler(boost::shared_ptr<boost::asio::ssl::context> spCtx) : mCtx(spCtx) {}
|
||||||
|
|
||||||
boost::shared_ptr<boost::asio::ssl::context> on_tls_init()
|
boost::shared_ptr<boost::asio::ssl::context> on_tls_init()
|
||||||
@@ -38,17 +98,103 @@ public:
|
|||||||
return mCtx;
|
return mCtx;
|
||||||
}
|
}
|
||||||
|
|
||||||
void on_message(connection_ptr con, message_ptr msg) {
|
void send(connection_ptr cpClient, message_ptr mpMessage)
|
||||||
con->send(msg->get_payload(), msg->get_opcode());
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
cpClient->send(mpMessage->get_payload(), mpMessage->get_opcode());
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
cpClient->close(websocketpp::close::status::value(crTooSlow), std::string("Client is too slow."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void send(connection_ptr cpClient, const std::string& strMessage)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Log(lsINFO) << "Ws:: Sending '" << strMessage << "'";
|
||||||
|
|
||||||
|
cpClient->send(strMessage);
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
cpClient->close(websocketpp::close::status::value(crTooSlow), std::string("Client is too slow."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void send(connection_ptr cpClient, const Json::Value& jvObj)
|
||||||
|
{
|
||||||
|
Json::FastWriter jfwWriter;
|
||||||
|
|
||||||
|
Log(lsINFO) << "Ws:: Object '" << jfwWriter.write(jvObj) << "'";
|
||||||
|
|
||||||
|
|
||||||
|
send(cpClient, jfwWriter.write(jvObj));
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_open(connection_ptr cpClient)
|
||||||
|
{
|
||||||
|
boost::mutex::scoped_lock sl(mMapLock);
|
||||||
|
|
||||||
|
mMap[cpClient] = WSConnection(this, cpClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_close(connection_ptr cpClient)
|
||||||
|
{
|
||||||
|
boost::mutex::scoped_lock sl(mMapLock);
|
||||||
|
|
||||||
|
mMap.erase(cpClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_message(connection_ptr cpClient, message_ptr mpMessage)
|
||||||
|
{
|
||||||
|
Json::Value jvRequest;
|
||||||
|
Json::Reader jrReader;
|
||||||
|
|
||||||
|
if (mpMessage->get_opcode() != websocketpp::frame::opcode::TEXT)
|
||||||
|
{
|
||||||
|
Json::Value jvResult(Json::objectValue);
|
||||||
|
|
||||||
|
jvResult["type"] = "error";
|
||||||
|
jvResult["error"] = "wsTextRequired"; // We only accept text messages.
|
||||||
|
|
||||||
|
send(cpClient, jvResult);
|
||||||
|
}
|
||||||
|
else if (!jrReader.parse(mpMessage->get_payload(), jvRequest) || jvRequest.isNull() || !jvRequest.isObject())
|
||||||
|
{
|
||||||
|
Json::Value jvResult(Json::objectValue);
|
||||||
|
|
||||||
|
jvResult["type"] = "error";
|
||||||
|
jvResult["error"] = "jsonInvalid"; // Received invalid json.
|
||||||
|
jvResult["value"] = mpMessage->get_payload();
|
||||||
|
|
||||||
|
send(cpClient, jvResult);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Json::Value jvResult(Json::objectValue);
|
||||||
|
|
||||||
|
jvResult["type"] = "success";
|
||||||
|
jvResult["value"] = mpMessage->get_payload();
|
||||||
|
|
||||||
|
send(cpClient, jvResult);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void http(connection_ptr con) {
|
// Respond to http requests.
|
||||||
con->set_body("<!DOCTYPE html><html><head><title>WebSocket++ TLS certificate test</title></head><body><h1>WebSocket++ TLS certificate test</h1><p>This is an HTTP(S) page served by a WebSocket++ server for the purposes of confirming that certificates are working since browsers normally silently ignore certificate issues.</p></body></html>");
|
void http(connection_ptr cpClient)
|
||||||
|
{
|
||||||
|
cpClient->set_body(
|
||||||
|
"<!DOCTYPE html><html><head><title>" SYSTEM_NAME " Test</title></head>"
|
||||||
|
"<body><h1>" SYSTEM_NAME " Test</h1><p>This page shows http(s) connectivity is working.</p></body></html>");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
void WSDoor::startListening()
|
void WSDoor::startListening()
|
||||||
{
|
{
|
||||||
|
// Generate a single SSL context for use by all connections.
|
||||||
boost::shared_ptr<boost::asio::ssl::context> mCtx;
|
boost::shared_ptr<boost::asio::ssl::context> mCtx;
|
||||||
mCtx = boost::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::sslv23);
|
mCtx = boost::make_shared<boost::asio::ssl::context>(boost::asio::ssl::context::sslv23);
|
||||||
|
|
||||||
@@ -59,20 +205,21 @@ void WSDoor::startListening()
|
|||||||
|
|
||||||
SSL_CTX_set_tmp_dh_callback(mCtx->native_handle(), handleTmpDh);
|
SSL_CTX_set_tmp_dh_callback(mCtx->native_handle(), handleTmpDh);
|
||||||
|
|
||||||
|
// Construct a single handler for all requests.
|
||||||
websocketpp::WSDOOR_SERVER::handler::ptr handler(new WSServerHandler<websocketpp::WSDOOR_SERVER>(mCtx));
|
websocketpp::WSDOOR_SERVER::handler::ptr handler(new WSServerHandler<websocketpp::WSDOOR_SERVER>(mCtx));
|
||||||
|
|
||||||
|
// Construct a websocket server.
|
||||||
mEndpoint = new websocketpp::WSDOOR_SERVER(handler);
|
mEndpoint = new websocketpp::WSDOOR_SERVER(handler);
|
||||||
|
|
||||||
// mEndpoint->alog().unset_level(websocketpp::log::alevel::ALL);
|
// mEndpoint->alog().unset_level(websocketpp::log::alevel::ALL);
|
||||||
// mEndpoint->elog().unset_level(websocketpp::log::elevel::ALL);
|
// mEndpoint->elog().unset_level(websocketpp::log::elevel::ALL);
|
||||||
|
|
||||||
Log(lsINFO) << "listening>";
|
// Call the main-event-loop of the websocket server.
|
||||||
|
mEndpoint->listen(
|
||||||
|
boost::asio::ip::tcp::endpoint(
|
||||||
|
boost::asio::ip::address().from_string(theConfig.WEBSOCKET_IP), theConfig.WEBSOCKET_PORT));
|
||||||
|
|
||||||
mEndpoint->listen(boost::asio::ip::tcp::endpoint(address().from_string(theConfig.WEBSOCKET_IP), theConfig.WEBSOCKET_PORT));
|
delete mEndpoint;
|
||||||
|
|
||||||
free(mEndpoint);
|
|
||||||
|
|
||||||
Log(lsINFO) << "listening<";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WSDoor* WSDoor::createWSDoor()
|
WSDoor* WSDoor::createWSDoor()
|
||||||
@@ -97,10 +244,15 @@ void WSDoor::stop()
|
|||||||
{
|
{
|
||||||
if (mThread)
|
if (mThread)
|
||||||
{
|
{
|
||||||
mEndpoint->stop(); // XXX Make this thread safe
|
mEndpoint->stop();
|
||||||
|
|
||||||
mThread->join();
|
mThread->join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WSConnection::send(const Json::Value& jvObj)
|
||||||
|
{
|
||||||
|
mHandler->send(mConnection, jvObj);
|
||||||
|
}
|
||||||
|
|
||||||
// vim:ts=4
|
// vim:ts=4
|
||||||
|
|||||||
@@ -19,8 +19,8 @@
|
|||||||
class WSDoor
|
class WSDoor
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
websocketpp::WSDOOR_SERVER* mEndpoint;
|
websocketpp::WSDOOR_SERVER* mEndpoint;
|
||||||
boost::thread* mThread;
|
boost::thread* mThread;
|
||||||
|
|
||||||
void startListening();
|
void startListening();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user