Convert from rapidjson to use nlohmann::json

This commit is contained in:
Michael 2019-03-02 02:27:38 +00:00
parent 93399fdbb4
commit 31441b46dc
No known key found for this signature in database
GPG Key ID: 2D51757B47E2434C
11 changed files with 174 additions and 352 deletions

View File

@ -18,8 +18,8 @@ namespace abyss
namespace http
{
using RPC_Method_t = std::string;
using RPC_Params = llarp::json::Value;
using RPC_Response = llarp::json::Document;
using RPC_Params = nlohmann::json;
using RPC_Response = nlohmann::json;
using Headers_t = std::unordered_multimap< std::string, std::string >;
struct ConnImpl;

View File

@ -7,6 +7,7 @@
#include <util/string_view.hpp>
#include <util/time.hpp>
#include <absl/types/optional.h>
#include <list>
#include <memory>
#include <string>
@ -21,14 +22,13 @@ namespace abyss
struct IRPCHandler
{
using Method_t = std::string;
using Params = llarp::json::Value;
using Response = llarp::json::Writer;
using Params = nlohmann::json;
using Response = nlohmann::json;
IRPCHandler(ConnImpl* impl);
virtual bool
HandleJSONRPC(Method_t method, const Params& params,
Response& response) = 0;
virtual absl::optional< Response >
HandleJSONRPC(Method_t method, const Params& params) = 0;
virtual ~IRPCHandler();

View File

@ -11,14 +11,12 @@ struct DemoHandler : public abyss::httpd::IRPCHandler
{
}
bool
HandleJSONRPC(Method_t method, __attribute__((unused)) const Params& params,
Response& resp) override
absl::optional< Response >
HandleJSONRPC(Method_t method,
__attribute__((unused)) const Params& params) override
{
llarp::LogInfo("method: ", method);
resp.StartObject();
resp.EndObject();
return true;
return Response::object();
}
};
@ -81,9 +79,7 @@ struct DemoClient : public abyss::http::JSONRPC
void
DoDemoRequest()
{
llarp::json::Value params;
params.SetObject();
QueueRPC("test", std::move(params),
QueueRPC("test", nlohmann::json::object(),
std::bind(&DemoClient::NewConn, this, std::placeholders::_1));
Flush();
};

View File

@ -14,11 +14,11 @@ namespace abyss
// big
static const size_t MAX_BODY_SIZE = (1024 * 1024);
llarp_tcp_conn* m_Conn;
json::Document m_RequestBody;
nlohmann::json m_RequestBody;
Headers_t m_SendHeaders;
IRPCClientHandler* handler;
std::unique_ptr< json::IParser > m_BodyParser;
json::Document m_Response;
nlohmann::json m_Response;
enum State
{
@ -31,9 +31,12 @@ namespace abyss
State state;
ConnImpl(llarp_tcp_conn* conn, RPC_Method_t method, RPC_Params params,
JSONRPC::HandlerFactory factory)
: m_Conn(conn), state(eInitial)
ConnImpl(llarp_tcp_conn* conn, const RPC_Method_t& method,
const RPC_Params& params, JSONRPC::HandlerFactory factory)
: m_Conn(conn)
, m_RequestBody(nlohmann::json::object())
, m_Response(nlohmann::json::object())
, state(eInitial)
{
srand(time(nullptr));
conn->user = this;
@ -43,18 +46,12 @@ namespace abyss
handler = factory(this);
m_RequestBody.SetObject();
auto& alloc = m_RequestBody.GetAllocator();
m_RequestBody.AddMember("jsonrpc", json::Value().SetString("2.0"),
alloc);
m_RequestBody["jsonrpc"] = "2.0";
llarp::AlignedBuffer< 8 > p;
p.Randomize();
std::string str = p.ToHex();
m_RequestBody.AddMember(
"id", json::Value().SetString(str.c_str(), alloc), alloc);
m_RequestBody.AddMember(
"method", json::Value().SetString(method.c_str(), alloc), alloc);
m_RequestBody.AddMember("params", params, alloc);
m_RequestBody["id"] = p.ToHex();
m_RequestBody["method"] = method;
m_RequestBody["params"] = params;
}
static void
@ -220,8 +217,7 @@ namespace abyss
// create request body
std::string body;
std::stringstream ss;
json::ToString(m_RequestBody, ss);
body = ss.str();
body = m_RequestBody.dump();
m_SendHeaders.emplace("Content-Type", "application/json");
m_SendHeaders.emplace("Content-Length", std::to_string(body.size()));
m_SendHeaders.emplace("Accept", "application/json");
@ -347,8 +343,7 @@ namespace abyss
}
auto& front = m_PendingCalls.front();
ConnImpl* connimpl =
new ConnImpl(conn, std::move(front.method), std::move(front.params),
std::move(front.createHandler));
new ConnImpl(conn, front.method, front.params, front.createHandler);
m_PendingCalls.pop_front();
m_Conns.emplace_back(connimpl->handler);
connimpl->SendRequest();

View File

@ -24,10 +24,7 @@ namespace abyss
llarp_time_t m_ReadTimeout;
bool m_Bad;
std::unique_ptr< json::IParser > m_BodyParser;
json::Document m_Request;
std::stringstream m_ResponseBuffer;
json::Stream m_ResponseStream;
json::Writer m_Response;
nlohmann::json m_Request;
enum HTTPState
{
@ -43,10 +40,7 @@ namespace abyss
HTTPState m_State;
ConnImpl(BaseReqHandler* p, llarp_tcp_conn* c, llarp_time_t readtimeout)
: _conn(c)
, _parent(p)
, m_ResponseStream(m_ResponseBuffer)
, m_Response(m_ResponseStream)
: _conn(c), _parent(p)
{
handler = nullptr;
m_LastActive = p->now();
@ -154,14 +148,13 @@ namespace abyss
// initialize body parser
if(m_BodyParser == nullptr)
{
ssize_t contentLength = 0;
auto itr = Header.Headers.find("content-length");
auto itr = Header.Headers.find("content-length");
if(itr == Header.Headers.end())
{
return WriteResponseSimple(400, "Bad Request", "text/plain",
"no content length");
}
contentLength = std::stoll(itr->second);
ssize_t contentLength = std::stoll(itr->second);
if(contentLength <= 0)
{
return WriteResponseSimple(400, "Bad Request", "text/plain",
@ -186,22 +179,21 @@ namespace abyss
return WriteResponseSimple(400, "Bad Request", "text/plain",
"bad json object");
case json::IParser::eDone:
if(m_Request.IsObject() && m_Request.HasMember("params")
&& m_Request.HasMember("method") && m_Request.HasMember("id")
&& m_Request["id"].IsString() && m_Request["method"].IsString()
&& m_Request["params"].IsObject())
if(m_Request.is_object() && m_Request.count("params")
&& m_Request.count("method") && m_Request.count("id")
&& m_Request["id"].is_string() && m_Request["method"].is_string()
&& m_Request["params"].is_object())
{
m_Response.StartObject();
m_Response.Key("jsonrpc");
m_Response.String("2.0");
m_Response.Key("id");
m_Response.String(m_Request["id"].GetString());
m_Response.Key("result");
if(handler->HandleJSONRPC(m_Request["method"].GetString(),
m_Request["params"], m_Response))
nlohmann::json response;
response["jsonrpc"] = "2.0";
response["id"] = m_Request["id"];
auto value = handler->HandleJSONRPC(
m_Request["method"].get< std::string >(),
m_Request["params"]);
if(value)
{
m_Response.EndObject();
return WriteResponseJSON();
response["result"] = value.value();
return WriteResponseJSON(response);
}
}
return WriteResponseSimple(500, "internal error", "text/plain",
@ -212,13 +204,11 @@ namespace abyss
}
bool
WriteResponseJSON()
WriteResponseJSON(const nlohmann::json& response)
{
std::string response;
m_ResponseStream.Flush();
response = m_ResponseBuffer.str();
std::string responseStr = response.dump();
return WriteResponseSimple(200, "OK", "application/json",
response.c_str());
responseStr.c_str());
}
bool

View File

@ -25,23 +25,28 @@ namespace llarp
}
virtual bool
HandleJSONResult(const json::Value& val) = 0;
HandleJSONResult(const nlohmann::json& val) = 0;
bool
HandleResponse(::abyss::http::RPC_Response response)
{
if(!response.IsObject())
if(!response.is_object())
{
return HandleJSONResult({});
}
const auto itr = response.FindMember("result");
if(itr == response.MemberEnd())
const auto itr = response.find("result");
if(itr == response.end())
{
return HandleJSONResult({});
}
if(itr->value.IsObject())
return HandleJSONResult(itr->value);
return false;
else if(itr.value().is_object())
{
return HandleJSONResult(itr.value());
}
else
{
return false;
}
}
void
@ -65,42 +70,40 @@ namespace llarp
}
bool
HandleJSONResult(const json::Value& result) override
HandleJSONResult(const nlohmann::json& result) override
{
PubkeyList_t keys;
if(!result.IsObject())
if(!result.is_object())
{
LogWarn("Invalid result: not an object");
handler({}, false);
return false;
}
const auto itr = result.FindMember("keys");
if(itr == result.MemberEnd())
const auto itr = result.find("keys");
if(itr == result.end())
{
LogWarn("Invalid result: no keys member");
handler({}, false);
return false;
}
if(!itr->value.IsArray())
if(!itr.value().is_array())
{
LogWarn("Invalid result: keys is not an array");
handler({}, false);
return false;
}
auto key_itr = itr->value.Begin();
while(key_itr != itr->value.End())
for(const auto item : itr.value())
{
if(key_itr->IsString())
if(item.is_string())
{
keys.emplace_back();
std::string str = key_itr->GetString();
std::string str = item.get< std::string >();
if(!Base32Decode(str, keys.back()))
{
LogWarn("Invalid key: ", str);
keys.pop_back();
}
}
++key_itr;
}
handler(keys, true);
return true;
@ -149,9 +152,7 @@ namespace llarp
AsyncUpdatePubkeyList()
{
LogInfo("Updating service node list");
json::Value params;
params.SetObject();
QueueRPC("get_all_service_nodes_keys", std::move(params),
QueueRPC("get_all_service_nodes_keys", nlohmann::json::object(),
std::bind(&CallerImpl::NewAsyncUpdatePubkeyListConn, this,
std::placeholders::_1));
}
@ -211,75 +212,59 @@ namespace llarp
{
}
bool
DumpState(Response& resp) const
Response
DumpState() const
{
util::StatusObject dump = router->ExtractStatus();
dump.Impl.Accept(resp);
return true;
return dump.get();
}
bool
ListExitLevels(Response& resp) const
Response
ListExitLevels() const
{
exit::Context::TrafficStats stats;
router->exitContext().CalculateExitTraffic(stats);
resp.StartArray();
auto itr = stats.begin();
while(itr != stats.end())
Response resp;
for(const auto& stat : stats)
{
resp.StartObject();
resp.Key("ident");
resp.String(itr->first.ToHex().c_str());
resp.Key("tx");
resp.Uint64(itr->second.first);
resp.Key("rx");
resp.Uint64(itr->second.second);
resp.EndObject();
++itr;
resp.emplace_back(Response{
{"ident", stat.first.ToHex()},
{"tx", stat.second.first},
{"rx", stat.second.second},
});
}
resp.EndArray();
return true;
return resp;
}
bool
ListNeighboors(Response& resp) const
Response
ListNeighboors() const
{
resp.StartArray();
Response resp = Response::array();
router->ForEachPeer([&](const ILinkSession* session, bool outbound) {
resp.StartObject();
auto ident = RouterID(session->GetPubKey()).ToString();
resp.Key("ident");
resp.String(ident.c_str());
auto addr = session->GetRemoteEndpoint().ToString();
resp.Key("addr");
resp.String(addr.c_str());
resp.Key("outbound");
resp.Bool(outbound);
resp.EndObject();
resp.emplace_back(
Response{{"ident", RouterID(session->GetPubKey()).ToString()},
{"addr", session->GetRemoteEndpoint().ToString()},
{"outbound", outbound}});
});
resp.EndArray();
return true;
return resp;
}
bool
absl::optional< Response >
HandleJSONRPC(Method_t method,
__attribute__((unused)) const Params& params,
Response& response)
__attribute__((unused)) const Params& params)
{
if(method == "llarp.admin.link.neighboors")
{
return ListNeighboors(response);
return ListNeighboors();
}
else if(method == "llarp.admin.exit.list")
{
return ListExitLevels(response);
return ListExitLevels();
}
else if(method == "llarp.admin.dumpstate")
{
return DumpState(response);
return DumpState();
}
return false;
}

View File

@ -9,10 +9,12 @@ namespace llarp
{
namespace json
{
struct RapidJSONParser : public IParser
struct NlohmannJSONParser : public IParser
{
RapidJSONParser(size_t contentSize) : m_Buf(contentSize + 1), m_Offset(0)
NlohmannJSONParser(size_t contentSize)
: m_Buf(contentSize + 1), m_Offset(0)
{
assert(contentSize != 0);
}
std::vector< char > m_Buf;
@ -23,36 +25,34 @@ namespace llarp
{
if(m_Offset + sz > m_Buf.size() - 1)
return false;
memcpy(m_Buf.data() + m_Offset, buf, sz);
std::copy(buf, buf + sz, m_Buf.begin());
m_Offset += sz;
m_Buf[m_Offset] = 0;
return true;
}
Result
Parse(Document& obj) const
Parse(nlohmann::json& obj) const
{
if(m_Offset < m_Buf.size() - 1)
return eNeedData;
obj.Parse(m_Buf.data());
if(obj.HasParseError())
try
{
obj = nlohmann::json::parse(m_Buf.data());
return eDone;
}
catch(const nlohmann::json::exception&)
{
return eParseError;
return eDone;
}
}
};
IParser*
MakeParser(size_t contentSize)
{
return new RapidJSONParser(contentSize);
}
void
ToString(const json::Document& val, std::ostream& out)
{
Stream s(out);
rapidjson::Writer< Stream > writer(s);
val.Accept(writer);
return new NlohmannJSONParser(contentSize);
}
} // namespace json

View File

@ -6,81 +6,13 @@
#include <rapidjson/document.h>
#include <rapidjson/writer.h>
#include <nlohmann/json.hpp>
#include <memory>
#include <iostream>
namespace llarp
{
namespace json
{
/// add this because debian stable doesn't have it
template < typename StreamType >
class BasicOStreamWrapper
{
public:
typedef typename StreamType::char_type Ch;
BasicOStreamWrapper(StreamType& stream) : stream_(stream)
{
}
void
Put(Ch c)
{
stream_.put(c);
}
void
Flush()
{
stream_.flush();
}
// Not implemented
char
Peek() const
{
RAPIDJSON_ASSERT(false);
return 0;
}
char
Take()
{
RAPIDJSON_ASSERT(false);
return 0;
}
size_t
Tell() const
{
RAPIDJSON_ASSERT(false);
return 0;
}
char*
PutBegin()
{
RAPIDJSON_ASSERT(false);
return 0;
}
size_t
PutEnd(char*)
{
RAPIDJSON_ASSERT(false);
return 0;
}
private:
BasicOStreamWrapper(const BasicOStreamWrapper&);
BasicOStreamWrapper&
operator=(const BasicOStreamWrapper&);
StreamType& stream_;
};
using Document = rapidjson::Document;
using Value = rapidjson::Value;
using Stream = BasicOStreamWrapper< std::ostream >;
using Writer = rapidjson::Writer< Stream >;
} // namespace json
namespace json
{
struct IParser
@ -103,16 +35,13 @@ namespace llarp
FeedData(const char* buf, size_t sz) = 0;
/// parse internal buffer
virtual Result
Parse(Document& obj) const = 0;
Parse(nlohmann::json& obj) const = 0;
};
/// create new parser
IParser*
MakeParser(size_t contentSize);
void
ToString(const json::Document& obj, std::ostream& out);
} // namespace json
} // namespace llarp

View File

@ -1,21 +1,54 @@
#include <util/status.hpp>
#include <util/traits.hpp>
namespace llarp
{
namespace util
{
StatusObject::StatusObject(const StatusObject& other)
struct StatusVisitor
{
Impl.SetObject();
auto& a = Impl.GetAllocator();
Impl.CopyFrom(other.Impl, a);
}
StatusObject::~StatusObject()
{
Impl.RemoveAllMembers();
}
std::string name;
std::reference_wrapper< nlohmann::json > data;
StatusVisitor(StatusObject::String_t n, nlohmann::json& d)
: name(n), data(d)
{
}
void
operator()(uint64_t val)
{
data.get()[name] = val;
}
void
operator()(const std::string& val)
{
data.get()[name] = val;
}
void
operator()(bool val)
{
data.get()[name] = val;
}
void
operator()(const StatusObject& obj)
{
data.get()[name] = obj.Impl;
}
void
operator()(const std::vector< std::string >& val)
{
data.get()[name] = val;
}
void
operator()(const std::vector< StatusObject >& val)
{
auto arr = nlohmann::json::array();
std::transform(val.begin(), val.end(), std::back_inserter(arr),
[](const auto& x) { return x.Impl; });
data.get()[name] = arr;
}
};
void
StatusObject::Put(const value_type& val)
{
@ -23,99 +56,9 @@ namespace llarp
}
void
StatusObject::Put(String_t name, const Variant& val)
StatusObject::Put(String_t name, const Variant& data)
{
if(absl::holds_alternative< uint64_t >(val))
PutInt(name, absl::get< uint64_t >(val));
else if(absl::holds_alternative< std::string >(val))
PutString(name, absl::get< std::string >(val));
else if(absl::holds_alternative< bool >(val))
PutBool(name, absl::get< bool >(val));
else if(absl::holds_alternative< StatusObject >(val))
PutObject(name, absl::get< StatusObject >(val));
else if(absl::holds_alternative< std::vector< std::string > >(val))
PutStringArray(name, absl::get< std::vector< std::string > >(val));
else if(absl::holds_alternative< std::vector< StatusObject > >(val))
PutObjectArray(name, absl::get< std::vector< StatusObject > >(val));
}
void
StatusObject::PutBool(String_t name, bool val)
{
auto& a = Impl.GetAllocator();
Value_t v;
v.SetBool(val);
auto s = llarp::string_view_string(name);
Value_t k(s.c_str(), a);
Impl.AddMember(k, v, a);
}
void
StatusObject::PutInt(String_t name, uint64_t val)
{
auto& a = Impl.GetAllocator();
Value_t v;
v.SetUint64(val);
auto s = llarp::string_view_string(name);
Value_t k(s.c_str(), a);
Impl.AddMember(k, v, a);
}
void
StatusObject::PutObject(String_t name, const StatusObject& val)
{
auto& a = Impl.GetAllocator();
Value_t v;
v.SetObject();
v.CopyFrom(val.Impl, a);
auto s = llarp::string_view_string(name);
Value_t k(s.c_str(), a);
Impl.AddMember(k, v, a);
}
void
StatusObject::PutObjectArray(String_t name,
const std::vector< StatusObject >& arr)
{
auto& a = Impl.GetAllocator();
Value_t v;
v.SetArray();
Value_t i;
for(const auto& obj : arr)
{
v.PushBack(i.SetObject().CopyFrom(obj.Impl, a), a);
}
auto s = llarp::string_view_string(name);
Value_t k(s.c_str(), a);
Impl.AddMember(k, v, a);
}
void
StatusObject::PutStringArray(String_t name,
const std::vector< std::string >& arr)
{
auto& a = Impl.GetAllocator();
Value_t v;
v.SetArray();
Value_t i;
for(const auto& str : arr)
{
v.PushBack(i.SetString(str.c_str(), a), a);
}
auto s = llarp::string_view_string(name);
Value_t k(s.c_str(), a);
Impl.AddMember(k, v, a);
}
void
StatusObject::PutString(String_t name, const std::string& val)
{
auto& a = Impl.GetAllocator();
Value_t v;
v.SetString(val.c_str(), a);
auto s = llarp::string_view_string(name);
Value_t k(s.c_str(), a);
Impl.AddMember(k, v, a);
absl::visit(StatusVisitor{name, Impl}, data);
}
} // namespace util
} // namespace llarp

View File

@ -1,9 +1,10 @@
#ifndef LLARP_UTIL_STATUS_HPP
#define LLARP_UTIL_STATUS_HPP
#include <util/json.hpp>
#include <util/string_view.hpp>
#include <nlohmann/json.hpp>
#include <vector>
#include <string>
#include <algorithm>
@ -13,23 +14,17 @@ namespace llarp
{
namespace util
{
using StatusObject_Impl = json::Document;
using Value_t = json::Value;
struct StatusVisitor;
struct StatusObject
{
using String_t = string_view;
using Variant = absl::variant< uint64_t, std::string, bool, StatusObject,
std::vector< std::string >,
std::vector< StatusObject > >;
using value_type = std::tuple< String_t, Variant >;
StatusObject(const StatusObject&);
~StatusObject();
using value_type = std::pair< String_t, Variant >;
StatusObject(std::initializer_list< value_type > vals)
{
Impl.SetObject();
std::for_each(vals.begin(), vals.end(),
[&](const value_type& item) { Put(item); });
}
@ -40,21 +35,15 @@ namespace llarp
void
Put(const value_type& value);
StatusObject_Impl Impl;
nlohmann::json
get() const
{
return Impl;
}
private:
void
PutBool(String_t name, bool val);
void
PutInt(String_t name, uint64_t val);
void
PutObject(String_t name, const StatusObject& val);
void
PutObjectArray(String_t name, const std::vector< StatusObject >& arr);
void
PutStringArray(String_t name, const std::vector< std::string >& arr);
void
PutString(String_t name, const std::string& val);
friend struct StatusVisitor;
nlohmann::json Impl;
};
/// an entity that has a status that can be extracted

View File

@ -110,7 +110,7 @@ struct ClientHandler : public abyss::http::IRPCClientHandler
void
HandleError()
{
ASSERT_TRUE(false);
FAIL() << "unexpected error";
}
void
@ -134,15 +134,12 @@ struct ServerHandler : public abyss::httpd::IRPCHandler
{
}
bool
HandleJSONRPC(Method_t method, __attribute__((unused)) const Params& params,
Response& response)
absl::optional< Response >
HandleJSONRPC(Method_t method, __attribute__((unused)) const Params& params)
{
test->AssertMethod(method);
test->called = true;
response.StartObject();
response.EndObject();
return true;
return Response();
}
};
@ -200,9 +197,7 @@ struct AbyssTest : public AbyssTestBase,
TEST_F(AbyssTest, TestClientAndServer)
{
Start();
llarp::json::Value params;
params.SetObject();
QueueRPC(method, std::move(params),
QueueRPC(method, nlohmann::json::object(),
std::bind(&AbyssTest::NewConn, this, std::placeholders::_1));
AsyncFlush();