123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- // CODYlib -*- mode:c++ -*-
- // Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
- // License: Apache v2.0
- // Cody
- #include "internal.hh"
- // C
- #include <cerrno>
- #include <cstdlib>
- #include <cstring>
- // Client code
- namespace Cody {
- // These do not need to be members
- static Packet ConnectResponse (std::vector<std::string> &words);
- static Packet PathnameResponse (std::vector<std::string> &words);
- static Packet OKResponse (std::vector<std::string> &words);
- static Packet IncludeTranslateResponse (std::vector<std::string> &words);
- // Must be consistently ordered with the RequestCode enum
- static Packet (*const responseTable[Detail::RC_HWM])
- (std::vector<std::string> &) =
- {
- &ConnectResponse,
- &PathnameResponse,
- &PathnameResponse,
- &PathnameResponse,
- &OKResponse,
- &IncludeTranslateResponse,
- };
- Client::Client ()
- {
- fd.from = fd.to = -1;
- }
- Client::Client (Client &&src)
- : write (std::move (src.write)),
- read (std::move (src.read)),
- corked (std::move (src.corked)),
- is_direct (src.is_direct),
- is_connected (src.is_connected)
- {
- if (is_direct)
- server = src.server;
- else
- {
- fd.from = src.fd.from;
- fd.to = src.fd.to;
- }
- }
- Client::~Client ()
- {
- }
- Client &Client::operator= (Client &&src)
- {
- write = std::move (src.write);
- read = std::move (src.read);
- corked = std::move (src.corked);
- is_direct = src.is_direct;
- is_connected = src.is_connected;
- if (is_direct)
- server = src.server;
- else
- {
- fd.from = src.fd.from;
- fd.to = src.fd.to;
- }
- return *this;
- }
- int Client::CommunicateWithServer ()
- {
- write.PrepareToWrite ();
- read.PrepareToRead ();
- if (IsDirect ())
- server->DirectProcess (write, read);
- else
- {
- // Write the write buffer
- while (int e = write.Write (fd.to))
- if (e != EAGAIN && e != EINTR)
- return e;
- // Read the read buffer
- while (int e = read.Read (fd.from))
- if (e != EAGAIN && e != EINTR)
- return e;
- }
- return 0;
- }
- static Packet CommunicationError (int err)
- {
- std::string e {u8"communication error:"};
- e.append (strerror (err));
- return Packet (Client::PC_ERROR, std::move (e));
- }
- Packet Client::ProcessResponse (std::vector<std::string> &words,
- unsigned code, bool isLast)
- {
- if (int e = read.Lex (words))
- {
- if (e == EINVAL)
- {
- std::string msg (u8"malformed string '");
- msg.append (words[0]);
- msg.append (u8"'");
- return Packet (Client::PC_ERROR, std::move (msg));
- }
- else
- return Packet (Client::PC_ERROR, u8"missing response");
- }
- Assert (!words.empty ());
- if (words[0] == u8"ERROR")
- return Packet (Client::PC_ERROR,
- words.size () == 2 ? words[1]: u8"malformed error response");
- if (isLast && !read.IsAtEnd ())
- return Packet (Client::PC_ERROR,
- std::string (u8"unexpected extra response"));
- Assert (code < Detail::RC_HWM);
- Packet result (responseTable[code] (words));
- result.SetRequest (code);
- if (result.GetCode () == Client::PC_ERROR && result.GetString ().empty ())
- {
- std::string msg {u8"malformed response '"};
- read.LexedLine (msg);
- msg.append (u8"'");
- result.GetString () = std::move (msg);
- }
- else if (result.GetCode () == Client::PC_CONNECT)
- is_connected = true;
- return result;
- }
- Packet Client::MaybeRequest (unsigned code)
- {
- if (IsCorked ())
- {
- corked.push_back (code);
- return Packet (PC_CORKED);
- }
- if (int err = CommunicateWithServer ())
- return CommunicationError (err);
- std::vector<std::string> words;
- return ProcessResponse(words, code, true);
- }
- void Client::Cork ()
- {
- if (corked.empty ())
- corked.push_back (-1);
- }
- std::vector<Packet> Client::Uncork ()
- {
- std::vector<Packet> result;
- if (corked.size () > 1)
- {
- if (int err = CommunicateWithServer ())
- result.emplace_back (CommunicationError (err));
- else
- {
- std::vector<std::string> words;
- for (auto iter = corked.begin () + 1; iter != corked.end ();)
- {
- char code = *iter;
- ++iter;
- result.emplace_back (ProcessResponse (words, code,
- iter == corked.end ()));
- }
- }
- }
- corked.clear ();
- return result;
- }
- // Now the individual message handlers
- // HELLO $vernum $agent $ident
- Packet Client::Connect (char const *agent, char const *ident,
- size_t alen, size_t ilen)
- {
- write.BeginLine ();
- write.AppendWord (u8"HELLO");
- write.AppendInteger (Version);
- write.AppendWord (agent, true, alen);
- write.AppendWord (ident, true, ilen);
- write.EndLine ();
- return MaybeRequest (Detail::RC_CONNECT);
- }
- // HELLO $version $agent [$flags]
- Packet ConnectResponse (std::vector<std::string> &words)
- {
- if (words[0] == u8"HELLO" && (words.size () == 3 || words.size () == 4))
- {
- char *eptr;
- unsigned long val = strtoul (words[1].c_str (), &eptr, 10);
- unsigned version = unsigned (val);
- if (*eptr || version != val || version < Version)
- return Packet (Client::PC_ERROR, u8"incompatible version");
- else
- {
- unsigned flags = 0;
- if (words.size () == 4)
- {
- val = strtoul (words[3].c_str (), &eptr, 10);
- flags = unsigned (val);
- }
- return Packet (Client::PC_CONNECT, flags);
- }
- }
- return Packet (Client::PC_ERROR, u8"");
- }
- // MODULE-REPO
- Packet Client::ModuleRepo ()
- {
- write.BeginLine ();
- write.AppendWord (u8"MODULE-REPO");
- write.EndLine ();
- return MaybeRequest (Detail::RC_MODULE_REPO);
- }
- // PATHNAME $dir | ERROR
- Packet PathnameResponse (std::vector<std::string> &words)
- {
- if (words[0] == u8"PATHNAME" && words.size () == 2)
- return Packet (Client::PC_PATHNAME, std::move (words[1]));
- return Packet (Client::PC_ERROR, u8"");
- }
- // OK or ERROR
- Packet OKResponse (std::vector<std::string> &words)
- {
- if (words[0] == u8"OK")
- return Packet (Client::PC_OK);
- else
- return Packet (Client::PC_ERROR,
- words.size () == 2 ? std::move (words[1]) : "");
- }
- // MODULE-EXPORT $modulename [$flags]
- Packet Client::ModuleExport (char const *module, Flags flags, size_t mlen)
- {
- write.BeginLine ();
- write.AppendWord (u8"MODULE-EXPORT");
- write.AppendWord (module, true, mlen);
- if (flags != Flags::None)
- write.AppendInteger (unsigned (flags));
- write.EndLine ();
- return MaybeRequest (Detail::RC_MODULE_EXPORT);
- }
- // MODULE-IMPORT $modulename [$flags]
- Packet Client::ModuleImport (char const *module, Flags flags, size_t mlen)
- {
- write.BeginLine ();
- write.AppendWord (u8"MODULE-IMPORT");
- write.AppendWord (module, true, mlen);
- if (flags != Flags::None)
- write.AppendInteger (unsigned (flags));
- write.EndLine ();
- return MaybeRequest (Detail::RC_MODULE_IMPORT);
- }
- // MODULE-COMPILED $modulename [$flags]
- Packet Client::ModuleCompiled (char const *module, Flags flags, size_t mlen)
- {
- write.BeginLine ();
- write.AppendWord (u8"MODULE-COMPILED");
- write.AppendWord (module, true, mlen);
- if (flags != Flags::None)
- write.AppendInteger (unsigned (flags));
- write.EndLine ();
- return MaybeRequest (Detail::RC_MODULE_COMPILED);
- }
- // INCLUDE-TRANSLATE $includename [$flags]
- Packet Client::IncludeTranslate (char const *include, Flags flags, size_t ilen)
- {
- write.BeginLine ();
- write.AppendWord (u8"INCLUDE-TRANSLATE");
- write.AppendWord (include, true, ilen);
- if (flags != Flags::None)
- write.AppendInteger (unsigned (flags));
- write.EndLine ();
- return MaybeRequest (Detail::RC_INCLUDE_TRANSLATE);
- }
- // BOOL $knowntextualness
- // PATHNAME $cmifile
- Packet IncludeTranslateResponse (std::vector<std::string> &words)
- {
- if (words[0] == u8"BOOL" && words.size () == 2)
- {
- if (words[1] == u8"FALSE")
- return Packet (Client::PC_BOOL, 0);
- else if (words[1] == u8"TRUE")
- return Packet (Client::PC_BOOL, 1);
- else
- return Packet (Client::PC_ERROR, u8"");
- }
- else
- return PathnameResponse (words);
- }
- }
|