lokinet/libabyss/src/server.cpp

395 lines
11 KiB
C++
Raw Normal View History

#include <abyss/server.hpp>
2018-11-01 12:47:14 +00:00
#include <abyss/http.hpp>
2018-10-19 11:41:36 +00:00
#include <llarp/time.h>
2018-10-23 11:29:37 +00:00
#include <sstream>
#include <unordered_map>
#include <string>
#include <llarp/logger.hpp>
#include <algorithm>
2018-10-19 11:41:36 +00:00
namespace abyss
{
2018-11-01 12:47:14 +00:00
namespace httpd
2018-10-19 11:41:36 +00:00
{
2018-11-01 12:47:14 +00:00
struct ConnImpl : abyss::http::HeaderReader
2018-10-23 11:29:37 +00:00
{
llarp_tcp_conn* _conn;
IRPCHandler* handler;
BaseReqHandler* _parent;
2018-10-19 11:41:36 +00:00
llarp_time_t m_LastActive;
llarp_time_t m_ReadTimeout;
2018-10-23 11:29:37 +00:00
bool m_Bad;
2018-10-25 17:03:25 +00:00
std::unique_ptr< abyss::json::IParser > m_BodyParser;
json::Document m_Request;
json::Document m_Response;
2018-10-19 11:41:36 +00:00
2018-10-23 11:29:37 +00:00
enum HTTPState
2018-10-19 11:41:36 +00:00
{
2018-10-23 11:29:37 +00:00
eReadHTTPMethodLine,
eReadHTTPHeaders,
eReadHTTPBody,
eWriteHTTPStatusLine,
eWriteHTTPHeaders,
eWriteHTTPBody,
eCloseMe
};
HTTPState m_State;
ConnImpl(BaseReqHandler* p, llarp_tcp_conn* c, llarp_time_t readtimeout)
: _conn(c), _parent(p)
{
handler = nullptr;
2018-10-29 16:48:36 +00:00
m_LastActive = p->now();
2018-10-19 11:41:36 +00:00
m_ReadTimeout = readtimeout;
// set up tcp members
_conn->user = this;
_conn->read = &ConnImpl::OnRead;
_conn->tick = &ConnImpl::OnTick;
_conn->closed = &ConnImpl::OnClosed;
2018-10-23 11:29:37 +00:00
m_Bad = false;
m_State = eReadHTTPMethodLine;
}
~ConnImpl()
{
}
bool
FeedLine(std::string& line)
{
2018-11-01 12:47:14 +00:00
bool done = false;
switch(m_State)
{
case eReadHTTPMethodLine:
return ProcessMethodLine(line);
case eReadHTTPHeaders:
2018-11-01 12:47:14 +00:00
if(!ProcessHeaderLine(line, done))
return false;
if(done)
m_State = eReadHTTPBody;
return true;
default:
return false;
}
}
bool
ProcessMethodLine(string_view line)
{
auto idx = line.find_first_of(' ');
if(idx == string_view::npos)
return false;
2018-11-01 12:47:14 +00:00
Header.Method = line.substr(0, idx);
line = line.substr(idx + 1);
idx = line.find_first_of(' ');
if(idx == string_view::npos)
return false;
2018-11-01 12:47:14 +00:00
Header.Path = line.substr(0, idx);
m_State = eReadHTTPHeaders;
return true;
}
bool
ShouldProcessHeader(const string_view& name) const
{
// TODO: header whitelist
2018-10-25 17:03:25 +00:00
return name == "content-type" || name == "content-length";
}
bool
WriteResponseSimple(int code, const std::string& msg,
const char* contentType, const char* content)
{
char buf[512] = {0};
size_t contentLength = strlen(content);
int sz = snprintf(buf, sizeof(buf),
"HTTP/1.0 %d %s\r\nContent-Type: "
"%s\r\nContent-Length: %zu\r\n\r\n",
code, msg.c_str(), contentType, contentLength);
if(sz <= 0)
return false;
if(!llarp_tcp_conn_async_write(_conn, buf, sz))
return false;
m_State = eWriteHTTPBody;
return llarp_tcp_conn_async_write(_conn, content, contentLength);
}
bool
FeedBody(const char* buf, size_t sz)
2018-10-23 11:29:37 +00:00
{
2018-11-01 12:47:14 +00:00
if(Header.Method != "POST")
{
2018-10-25 17:03:25 +00:00
return WriteResponseSimple(405, "Method Not Allowed", "text/plain",
"nope");
}
{
2018-11-01 12:47:14 +00:00
auto itr = Header.Headers.find("content-type");
if(itr == Header.Headers.end())
2018-10-25 17:03:25 +00:00
{
return WriteResponseSimple(415, "Unsupported Media Type",
"text/plain",
"no content type provided");
}
else if(itr->second != "application/json")
{
return WriteResponseSimple(415, "Unsupported Media Type",
"text/plain",
"this does not look like jsonrpc 2.0");
}
}
// initialize body parser
if(m_BodyParser == nullptr)
{
ssize_t contentLength = 0;
2018-11-01 12:47:14 +00:00
auto itr = Header.Headers.find("content-length");
if(itr == Header.Headers.end())
2018-10-25 17:03:25 +00:00
{
return WriteResponseSimple(400, "Bad Request", "text/plain",
"no content length");
}
contentLength = std::stoll(itr->second);
if(contentLength <= 0)
{
return WriteResponseSimple(400, "Bad Request", "text/plain",
"bad content length");
}
else
{
m_BodyParser.reset(abyss::json::MakeParser(contentLength));
}
}
if(!m_BodyParser->FeedData(buf, sz))
{
return WriteResponseSimple(400, "Bad Request", "text/plain",
"invalid body size");
}
2018-10-25 17:03:25 +00:00
switch(m_BodyParser->Parse(m_Request))
{
case json::IParser::eNeedData:
return true;
case json::IParser::eParseError:
return WriteResponseSimple(400, "Bad Request", "text/plain",
"bad json object");
case json::IParser::eDone:
if(m_Request.IsObject() && m_Request.HasMember("params")
2018-10-25 17:19:53 +00:00
&& m_Request.HasMember("method") && m_Request.HasMember("id")
&& (m_Request["id"].IsString() || m_Request["id"].IsNumber())
2018-10-25 17:03:25 +00:00
&& m_Request["method"].IsString()
&& m_Request["params"].IsObject())
{
m_Response.SetObject();
2018-10-25 17:19:53 +00:00
m_Response.AddMember("jsonrpc",
abyss::json::Value().SetString("2.0"),
m_Response.GetAllocator());
m_Response.AddMember("id", m_Request["id"],
m_Response.GetAllocator());
2018-10-25 17:03:25 +00:00
if(handler->HandleJSONRPC(m_Request["method"].GetString(),
m_Request["params"], m_Response))
2018-10-25 17:19:53 +00:00
{
2018-10-25 17:03:25 +00:00
return WriteResponseJSON();
2018-10-25 17:19:53 +00:00
}
2018-10-25 17:03:25 +00:00
}
return WriteResponseSimple(500, "internal error", "text/plain",
"nope");
default:
return false;
}
2018-10-25 17:03:25 +00:00
}
bool
WriteResponseJSON()
{
std::string response;
json::ToString(m_Response, response);
return WriteResponseSimple(200, "OK", "application/json",
response.c_str());
2018-10-23 11:29:37 +00:00
}
bool
ProcessRead(const char* buf, size_t sz)
{
if(m_Bad)
{
2018-10-23 11:29:37 +00:00
return false;
}
if(sz == 0)
return true;
2018-11-01 12:47:14 +00:00
bool done = false;
2018-10-29 16:48:36 +00:00
m_LastActive = _parent->now();
if(m_State < eReadHTTPBody)
{
const char* end = strstr(buf, "\r\n");
while(end)
{
2018-11-01 12:47:14 +00:00
string_view line(buf, end - buf);
switch(m_State)
{
case eReadHTTPMethodLine:
if(!ProcessMethodLine(line))
return false;
2018-10-25 17:03:25 +00:00
sz -= line.size() + (2 * sizeof(char));
break;
case eReadHTTPHeaders:
2018-11-01 12:47:14 +00:00
if(!ProcessHeaderLine(line, done))
return false;
2018-10-25 17:03:25 +00:00
sz -= line.size() + (2 * sizeof(char));
2018-11-01 12:47:14 +00:00
if(done)
m_State = eReadHTTPBody;
break;
default:
break;
}
2018-10-25 17:03:25 +00:00
buf = end + (2 * sizeof(char));
end = strstr(buf, "\r\n");
}
}
if(m_State == eReadHTTPBody)
return FeedBody(buf, sz);
return false;
2018-10-23 11:29:37 +00:00
}
static void
OnRead(llarp_tcp_conn* conn, const void* buf, size_t sz)
{
ConnImpl* self = static_cast< ConnImpl* >(conn->user);
if(!self->ProcessRead((const char*)buf, sz))
self->MarkBad();
}
static void
OnClosed(llarp_tcp_conn* conn)
{
ConnImpl* self = static_cast< ConnImpl* >(conn->user);
self->_conn = nullptr;
}
2018-10-23 11:29:37 +00:00
static void
OnTick(llarp_tcp_conn* conn)
{
ConnImpl* self = static_cast< ConnImpl* >(conn->user);
self->Tick();
}
void
Tick()
{
}
/// mark bad so next tick we are closed
void
MarkBad()
{
m_Bad = true;
2018-10-19 11:41:36 +00:00
}
bool
ShouldClose(llarp_time_t now) const
{
return now - m_LastActive > m_ReadTimeout || m_Bad
|| m_State == eCloseMe;
2018-10-19 11:41:36 +00:00
}
void
2018-10-23 11:29:37 +00:00
Close()
2018-10-19 11:41:36 +00:00
{
if(_conn)
{
llarp_tcp_conn_close(_conn);
_conn = nullptr;
}
2018-10-19 11:41:36 +00:00
}
}; // namespace http
2018-10-19 11:41:36 +00:00
2018-10-23 11:29:37 +00:00
IRPCHandler::IRPCHandler(ConnImpl* conn) : m_Impl(conn)
{
}
IRPCHandler::~IRPCHandler()
{
m_Impl->Close();
delete m_Impl;
}
bool
IRPCHandler::ShouldClose(llarp_time_t now) const
{
return m_Impl->ShouldClose(now);
}
2018-10-19 11:41:36 +00:00
BaseReqHandler::BaseReqHandler(llarp_time_t reqtimeout)
: m_ReqTimeout(reqtimeout)
{
m_loop = nullptr;
m_Logic = nullptr;
m_acceptor.accepted = &BaseReqHandler::OnAccept;
m_acceptor.user = this;
m_acceptor.tick = &OnTick;
m_acceptor.closed = nullptr;
2018-10-19 11:41:36 +00:00
}
2018-10-23 11:29:37 +00:00
bool
BaseReqHandler::ServeAsync(llarp_ev_loop* loop, llarp_logic* logic,
const sockaddr* bindaddr)
{
m_loop = loop;
m_Logic = logic;
return llarp_tcp_serve(m_loop, &m_acceptor, bindaddr);
2018-10-23 11:29:37 +00:00
}
void
BaseReqHandler::OnTick(llarp_tcp_acceptor* tcp)
2018-10-23 11:29:37 +00:00
{
BaseReqHandler* self = static_cast< BaseReqHandler* >(tcp->user);
2018-10-23 11:29:37 +00:00
self->Tick();
}
void
BaseReqHandler::Tick()
{
2018-10-29 16:48:36 +00:00
auto _now = now();
auto itr = m_Conns.begin();
2018-10-23 11:29:37 +00:00
while(itr != m_Conns.end())
{
if((*itr)->ShouldClose(_now))
2018-10-23 11:29:37 +00:00
itr = m_Conns.erase(itr);
else
++itr;
}
}
2018-11-02 17:08:01 +00:00
void
BaseReqHandler::Close()
2018-10-19 11:41:36 +00:00
{
llarp_tcp_acceptor_close(&m_acceptor);
}
2018-11-02 17:08:01 +00:00
BaseReqHandler::~BaseReqHandler()
{
}
2018-10-19 11:41:36 +00:00
void
BaseReqHandler::OnAccept(llarp_tcp_acceptor* acceptor, llarp_tcp_conn* conn)
{
2018-10-23 11:29:37 +00:00
BaseReqHandler* self = static_cast< BaseReqHandler* >(acceptor->user);
ConnImpl* connimpl = new ConnImpl(self, conn, self->m_ReqTimeout);
IRPCHandler* rpcHandler = self->CreateHandler(connimpl);
if(rpcHandler == nullptr)
{
connimpl->Close();
delete connimpl;
return;
}
connimpl->handler = rpcHandler;
self->m_Conns.emplace_back(rpcHandler);
2018-10-19 11:41:36 +00:00
}
2018-11-01 12:47:14 +00:00
} // namespace httpd
2018-10-19 11:41:36 +00:00
} // namespace abyss