| // 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> | |
| #include <cstdlib> | |
| #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 (); | |
| } | |
| } |