2020-12-14 17:10:27 +01:00
|
|
|
// CODYlib -*- mode:c++ -*-
|
|
|
|
// Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
|
|
|
|
// License: Apache v2.0
|
|
|
|
|
|
|
|
// Cody
|
|
|
|
#include "internal.hh"
|
|
|
|
// C++
|
|
|
|
#include <tuple>
|
|
|
|
// C
|
|
|
|
#include <cerrno>
|
2020-12-21 18:16:48 +01:00
|
|
|
#include <cstdlib>
|
2020-12-14 17:10:27 +01:00
|
|
|
#include <cstring>
|
|
|
|
|
|
|
|
// Server code
|
|
|
|
|
|
|
|
namespace Cody {
|
|
|
|
|
|
|
|
// These do not need to be members
|
|
|
|
static Resolver *ConnectRequest (Server *, Resolver *,
|
|
|
|
std::vector<std::string> &words);
|
|
|
|
static int ModuleRepoRequest (Server *, Resolver *,
|
|
|
|
std::vector<std::string> &words);
|
|
|
|
static int ModuleExportRequest (Server *, Resolver *,
|
|
|
|
std::vector<std::string> &words);
|
|
|
|
static int ModuleImportRequest (Server *, Resolver *,
|
|
|
|
std::vector<std::string> &words);
|
|
|
|
static int ModuleCompiledRequest (Server *, Resolver *,
|
|
|
|
std::vector<std::string> &words);
|
|
|
|
static int IncludeTranslateRequest (Server *, Resolver *,
|
|
|
|
std::vector<std::string> &words);
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
using RequestFn = int (Server *, Resolver *, std::vector<std::string> &);
|
|
|
|
using RequestPair = std::tuple<char const *, RequestFn *>;
|
|
|
|
static RequestPair
|
|
|
|
const requestTable[Detail::RC_HWM] =
|
|
|
|
{
|
|
|
|
// Same order as enum RequestCode
|
|
|
|
RequestPair {u8"HELLO", nullptr},
|
|
|
|
RequestPair {u8"MODULE-REPO", ModuleRepoRequest},
|
|
|
|
RequestPair {u8"MODULE-EXPORT", ModuleExportRequest},
|
|
|
|
RequestPair {u8"MODULE-IMPORT", ModuleImportRequest},
|
|
|
|
RequestPair {u8"MODULE-COMPILED", ModuleCompiledRequest},
|
|
|
|
RequestPair {u8"INCLUDE-TRANSLATE", IncludeTranslateRequest},
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
Server::Server (Resolver *r)
|
|
|
|
: resolver (r), direction (READING)
|
|
|
|
{
|
|
|
|
PrepareToRead ();
|
|
|
|
}
|
|
|
|
|
|
|
|
Server::Server (Server &&src)
|
|
|
|
: write (std::move (src.write)),
|
|
|
|
read (std::move (src.read)),
|
|
|
|
resolver (src.resolver),
|
|
|
|
is_connected (src.is_connected),
|
|
|
|
direction (src.direction)
|
|
|
|
{
|
|
|
|
fd.from = src.fd.from;
|
|
|
|
fd.to = src.fd.to;
|
|
|
|
}
|
|
|
|
|
|
|
|
Server::~Server ()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Server &Server::operator= (Server &&src)
|
|
|
|
{
|
|
|
|
write = std::move (src.write);
|
|
|
|
read = std::move (src.read);
|
|
|
|
resolver = src.resolver;
|
|
|
|
is_connected = src.is_connected;
|
|
|
|
direction = src.direction;
|
|
|
|
fd.from = src.fd.from;
|
|
|
|
fd.to = src.fd.to;
|
|
|
|
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Server::DirectProcess (Detail::MessageBuffer &from,
|
|
|
|
Detail::MessageBuffer &to)
|
|
|
|
{
|
|
|
|
read.PrepareToRead ();
|
|
|
|
std::swap (read, from);
|
|
|
|
ProcessRequests ();
|
|
|
|
resolver->WaitUntilReady (this);
|
|
|
|
write.PrepareToWrite ();
|
|
|
|
std::swap (to, write);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Server::ProcessRequests (void)
|
|
|
|
{
|
|
|
|
std::vector<std::string> words;
|
|
|
|
|
|
|
|
direction = PROCESSING;
|
|
|
|
while (!read.IsAtEnd ())
|
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
unsigned ix = Detail::RC_HWM;
|
|
|
|
if (!read.Lex (words))
|
|
|
|
{
|
|
|
|
Assert (!words.empty ());
|
|
|
|
while (ix--)
|
|
|
|
{
|
|
|
|
if (words[0] != std::get<0> (requestTable[ix]))
|
|
|
|
continue; // not this one
|
|
|
|
|
|
|
|
if (ix == Detail::RC_CONNECT)
|
|
|
|
{
|
|
|
|
// CONNECT
|
|
|
|
if (IsConnected ())
|
|
|
|
err = -1;
|
|
|
|
else if (auto *r = ConnectRequest (this, resolver, words))
|
|
|
|
resolver = r;
|
|
|
|
else
|
|
|
|
err = -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (!IsConnected ())
|
|
|
|
err = -1;
|
|
|
|
else if (int res = (std::get<1> (requestTable[ix])
|
|
|
|
(this, resolver, words)))
|
|
|
|
err = res;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err || ix >= Detail::RC_HWM)
|
|
|
|
{
|
|
|
|
// Some kind of error
|
|
|
|
std::string msg;
|
|
|
|
|
|
|
|
if (err > 0)
|
|
|
|
msg = u8"error processing '";
|
|
|
|
else if (ix >= Detail::RC_HWM)
|
|
|
|
msg = u8"unrecognized '";
|
|
|
|
else if (IsConnected () && ix == Detail::RC_CONNECT)
|
|
|
|
msg = u8"already connected '";
|
|
|
|
else if (!IsConnected () && ix != Detail::RC_CONNECT)
|
|
|
|
msg = u8"not connected '";
|
|
|
|
else
|
|
|
|
msg = u8"malformed '";
|
|
|
|
|
|
|
|
read.LexedLine (msg);
|
|
|
|
msg.append (u8"'");
|
|
|
|
if (err > 0)
|
|
|
|
{
|
|
|
|
msg.append (u8" ");
|
|
|
|
msg.append (strerror (err));
|
|
|
|
}
|
|
|
|
resolver->ErrorResponse (this, std::move (msg));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return numeric value of STR as an unsigned. Returns ~0u on error
|
|
|
|
// (so that value is not representable).
|
|
|
|
static unsigned ParseUnsigned (std::string &str)
|
|
|
|
{
|
|
|
|
char *eptr;
|
|
|
|
unsigned long val = strtoul (str.c_str (), &eptr, 10);
|
|
|
|
if (*eptr || unsigned (val) != val)
|
|
|
|
return ~0u;
|
|
|
|
|
|
|
|
return unsigned (val);
|
|
|
|
}
|
|
|
|
|
|
|
|
Resolver *ConnectRequest (Server *s, Resolver *r,
|
|
|
|
std::vector<std::string> &words)
|
|
|
|
{
|
|
|
|
if (words.size () < 3 || words.size () > 4)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
if (words.size () == 3)
|
|
|
|
words.emplace_back (u8"");
|
|
|
|
unsigned version = ParseUnsigned (words[1]);
|
|
|
|
if (version == ~0u)
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
return r->ConnectRequest (s, version, words[2], words[3]);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ModuleRepoRequest (Server *s, Resolver *r,std::vector<std::string> &words)
|
|
|
|
{
|
|
|
|
if (words.size () != 1)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
return r->ModuleRepoRequest (s);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ModuleExportRequest (Server *s, Resolver *r, std::vector<std::string> &words)
|
|
|
|
{
|
|
|
|
if (words.size () < 2 || words.size () > 3 || words[1].empty ())
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
Flags flags = Flags::None;
|
|
|
|
if (words.size () == 3)
|
|
|
|
{
|
|
|
|
unsigned val = ParseUnsigned (words[2]);
|
|
|
|
if (val == ~0u)
|
|
|
|
return -1;
|
|
|
|
flags = Flags (val);
|
|
|
|
}
|
|
|
|
|
|
|
|
return r->ModuleExportRequest (s, flags, words[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ModuleImportRequest (Server *s, Resolver *r, std::vector<std::string> &words)
|
|
|
|
{
|
|
|
|
if (words.size () < 2 || words.size () > 3 || words[1].empty ())
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
Flags flags = Flags::None;
|
|
|
|
if (words.size () == 3)
|
|
|
|
{
|
|
|
|
unsigned val = ParseUnsigned (words[2]);
|
|
|
|
if (val == ~0u)
|
|
|
|
return -1;
|
|
|
|
flags = Flags (val);
|
|
|
|
}
|
|
|
|
|
|
|
|
return r->ModuleImportRequest (s, flags, words[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
int ModuleCompiledRequest (Server *s, Resolver *r,
|
|
|
|
std::vector<std::string> &words)
|
|
|
|
{
|
|
|
|
if (words.size () < 2 || words.size () > 3 || words[1].empty ())
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
Flags flags = Flags::None;
|
|
|
|
if (words.size () == 3)
|
|
|
|
{
|
|
|
|
unsigned val = ParseUnsigned (words[2]);
|
|
|
|
if (val == ~0u)
|
|
|
|
return -1;
|
|
|
|
flags = Flags (val);
|
|
|
|
}
|
|
|
|
|
|
|
|
return r->ModuleCompiledRequest (s, flags, words[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
int IncludeTranslateRequest (Server *s, Resolver *r,
|
|
|
|
std::vector<std::string> &words)
|
|
|
|
{
|
|
|
|
if (words.size () < 2 || words.size () > 3 || words[1].empty ())
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
Flags flags = Flags::None;
|
|
|
|
if (words.size () == 3)
|
|
|
|
{
|
|
|
|
unsigned val = ParseUnsigned (words[2]);
|
|
|
|
if (val == ~0u)
|
|
|
|
return -1;
|
|
|
|
flags = Flags (val);
|
|
|
|
}
|
|
|
|
|
|
|
|
return r->IncludeTranslateRequest (s, flags, words[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Server::ErrorResponse (char const *error, size_t elen)
|
|
|
|
{
|
|
|
|
write.BeginLine ();
|
|
|
|
write.AppendWord (u8"ERROR");
|
|
|
|
write.AppendWord (error, true, elen);
|
|
|
|
write.EndLine ();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Server::OKResponse ()
|
|
|
|
{
|
|
|
|
write.BeginLine ();
|
|
|
|
write.AppendWord (u8"OK");
|
|
|
|
write.EndLine ();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Server::ConnectResponse (char const *agent, size_t alen)
|
|
|
|
{
|
|
|
|
is_connected = true;
|
|
|
|
|
|
|
|
write.BeginLine ();
|
|
|
|
write.AppendWord (u8"HELLO");
|
|
|
|
write.AppendInteger (Version);
|
|
|
|
write.AppendWord (agent, true, alen);
|
|
|
|
write.EndLine ();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Server::PathnameResponse (char const *cmi, size_t clen)
|
|
|
|
{
|
|
|
|
write.BeginLine ();
|
|
|
|
write.AppendWord (u8"PATHNAME");
|
|
|
|
write.AppendWord (cmi, true, clen);
|
|
|
|
write.EndLine ();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Server::BoolResponse (bool truthiness)
|
|
|
|
{
|
|
|
|
write.BeginLine ();
|
|
|
|
write.AppendWord (u8"BOOL");
|
|
|
|
write.AppendWord (truthiness ? u8"TRUE" : u8"FALSE");
|
|
|
|
write.EndLine ();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|