// CODYlib -*- mode:c++ -*- // Copyright (C) 2020 Nathan Sidwell, nathan@acm.org // License: Apache v2.0 // Cody #include "internal.hh" // C++ #include // C #include #include #include // Server code namespace Cody { // These do not need to be members static Resolver *ConnectRequest (Server *, Resolver *, std::vector &words); static int ModuleRepoRequest (Server *, Resolver *, std::vector &words); static int ModuleExportRequest (Server *, Resolver *, std::vector &words); static int ModuleImportRequest (Server *, Resolver *, std::vector &words); static int ModuleCompiledRequest (Server *, Resolver *, std::vector &words); static int IncludeTranslateRequest (Server *, Resolver *, std::vector &words); namespace { using RequestFn = int (Server *, Resolver *, std::vector &); using RequestPair = std::tuple; 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 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 &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 &words) { if (words.size () != 1) return -1; return r->ModuleRepoRequest (s); } int ModuleExportRequest (Server *s, Resolver *r, std::vector &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 &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 &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 &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 (); } }