100 lines
3.6 KiB
C++
100 lines
3.6 KiB
C++
@use crate::data::RPC;
|
|
@use crate::generators::cpp_s::*;
|
|
@use super::cpp_server_json_cxx;
|
|
|
|
@(header_name: &str, rpc: &RPC)
|
|
#include "@header_name"
|
|
#include <corvusoft/restbed/session.hpp>
|
|
#include <corvusoft/restbed/resource.hpp>
|
|
#include <corvusoft/restbed/request.hpp>
|
|
|
|
using namespace mrpc;
|
|
|
|
@:cpp_server_json_cxx(rpc)
|
|
|
|
template<typename T>
|
|
void send_msg(const std::shared_ptr<restbed::Session> &c, const T &v) @{
|
|
if (c->is_closed())
|
|
return;
|
|
rapidjson::StringBuffer s;
|
|
mrpc::MRPCJWriter writer@{s@};
|
|
v >> writer;
|
|
const auto body_ptr = s.GetString();
|
|
const auto body = restbed::Bytes@{body_ptr, body_ptr+s.GetLength()@};
|
|
c->yield(
|
|
200,
|
|
body,
|
|
std::multimap<std::string, std::string>@{
|
|
@{"Content-Type", "application/json"@},
|
|
@{"Content-Length", std::to_string(body.size())@}
|
|
@}
|
|
);
|
|
@}
|
|
|
|
template<typename T>
|
|
void send_sse_msg(const std::shared_ptr<restbed::Session> &c, const T &v) @{
|
|
if (c->is_closed())
|
|
return;
|
|
rapidjson::StringBuffer s;
|
|
std::memcpy(s.Push(5), "data:", 5);
|
|
mrpc::MRPCJWriter writer@{s@};
|
|
v >> writer;
|
|
std::memcpy(s.Push(2), "\n\n", 2);
|
|
const auto body_ptr = s.GetString();
|
|
const auto body = restbed::Bytes@{body_ptr, body_ptr+s.GetLength()@};
|
|
c->yield(body);
|
|
@}
|
|
|
|
mrpc::MRPCStreamImpl::MRPCStreamImpl(const std::shared_ptr<restbed::Session> &conn) : conn(conn) @{
|
|
conn->yield(
|
|
200,
|
|
std::multimap<std::string, std::string>@{
|
|
@{"Cache-Control", "no-cache"@},
|
|
@{"Content-Type", "text/event-stream"@}
|
|
@}
|
|
);
|
|
@}
|
|
|
|
void mrpc::MRPCStreamImpl::close() const noexcept @{ conn->close("data:null\n\n"); @}
|
|
bool mrpc::MRPCStreamImpl::is_open() const noexcept @{ return conn->is_open(); @}
|
|
@for s in streams_required(rpc) {template<> void MRPCStream<@s>::send(const @s &v) const noexcept @{ send_sse_msg(conn, v); @}
|
|
}
|
|
|
|
mrpc::MRPCServer::MRPCServer(std::shared_ptr<restbed::Resource> &r) @{
|
|
r->set_method_handler("POST", [this](const std::shared_ptr<restbed::Session>& s) @{
|
|
const auto req = s->get_request();
|
|
const auto body_len = req->get_header("Content-Length", 0);
|
|
s->fetch(body_len, [this](const std::shared_ptr<restbed::Session> &s, auto &&body) @{
|
|
try @{ msg_handler(s, body); @}
|
|
catch (const std::exception &_) @{ s->close(400); @}
|
|
@});
|
|
@});
|
|
@}
|
|
|
|
void mrpc::MRPCServer::msg_handler(const std::shared_ptr<restbed::Session> __c, const restbed::Bytes &__msg) @{
|
|
rapidjson::Document __j;
|
|
__j.Parse((const char*)__msg.data(), __msg.size());
|
|
if (__j.HasParseError())
|
|
throw std::exception@{@};
|
|
std::string __service, __method;
|
|
__service << json_get(__j, "service");
|
|
__method << json_get(__j, "method");
|
|
auto __data_member = __j.FindMember("data");
|
|
if (__data_member == __j.MemberEnd() || !__data_member->value.IsObject())
|
|
throw std::exception@{@};
|
|
auto &__data = __data_member->value;
|
|
@for (si, s) in rpc.services.iter().enumerate() {@if si > 0 { else }else{ }if (__service == "@s.name") @{
|
|
@for (mi, m) in s.methods.iter().enumerate() {@if mi > 0 { else }else{ }if (__method == "@m.name") @{
|
|
@if m.ret_stream {auto __stream = MRPCStream<@ty_to_str(m.ret.as_ref().unwrap())>@{__c@};
|
|
}
|
|
@for (name, ty) in m.args.iter().map(|a| (&a.name, ty_to_str(&a.ty))) { @ty @name; @name << json_get(__data, "@name");
|
|
}
|
|
@if m.ret_stream || m.ret.is_none() {@(s.name)_@(m.name)(@call_args(m));}
|
|
else {send_msg(__c, @(s.name)_@(m.name)(@call_args(m)));}
|
|
@}}
|
|
else @{ throw std::exception@{@}; @}
|
|
@}}
|
|
else @{ throw std::exception@{@}; @}
|
|
@}
|
|
@}
|