1// CODYlib -*- mode:c++ -*-
2// Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
3// License: Apache v2.0
4
5// Cody
6#include "internal.hh"
7// C++
8#include <tuple>
9// C
10#include <cerrno>
11#include <cstdlib>
12#include <cstring>
13
14// Server code
15
16namespace Cody {
17
18// These do not need to be members
19static Resolver *ConnectRequest (Server *, Resolver *,
20 std::vector<std::string> &words);
21static int ModuleRepoRequest (Server *, Resolver *,
22 std::vector<std::string> &words);
23static int ModuleExportRequest (Server *, Resolver *,
24 std::vector<std::string> &words);
25static int ModuleImportRequest (Server *, Resolver *,
26 std::vector<std::string> &words);
27static int ModuleCompiledRequest (Server *, Resolver *,
28 std::vector<std::string> &words);
29static int IncludeTranslateRequest (Server *, Resolver *,
30 std::vector<std::string> &words);
31
32namespace {
33using RequestFn = int (Server *, Resolver *, std::vector<std::string> &);
34using RequestPair = std::tuple<char const *, RequestFn *>;
35static RequestPair
36 const requestTable[Detail::RC_HWM] =
37 {
38 // Same order as enum RequestCode
39 RequestPair {u8"HELLO", nullptr},
40 RequestPair {u8"MODULE-REPO", ModuleRepoRequest},
41 RequestPair {u8"MODULE-EXPORT", ModuleExportRequest},
42 RequestPair {u8"MODULE-IMPORT", ModuleImportRequest},
43 RequestPair {u8"MODULE-COMPILED", ModuleCompiledRequest},
44 RequestPair {u8"INCLUDE-TRANSLATE", IncludeTranslateRequest},
45 };
46}
47
48Server::Server (Resolver *r)
49 : resolver (r), direction (READING)
50{
51 PrepareToRead ();
52}
53
54Server::Server (Server &&src)
55 : write (std::move (src.write)),
56 read (std::move (src.read)),
57 resolver (src.resolver),
58 is_connected (src.is_connected),
59 direction (src.direction)
60{
61 fd.from = src.fd.from;
62 fd.to = src.fd.to;
63}
64
65Server::~Server ()
66{
67}
68
69Server &Server::operator= (Server &&src)
70{
71 write = std::move (src.write);
72 read = std::move (src.read);
73 resolver = src.resolver;
74 is_connected = src.is_connected;
75 direction = src.direction;
76 fd.from = src.fd.from;
77 fd.to = src.fd.to;
78
79 return *this;
80}
81
82void Server::DirectProcess (Detail::MessageBuffer &from,
83 Detail::MessageBuffer &to)
84{
85 read.PrepareToRead ();
86 std::swap (a&: read, b&: from);
87 ProcessRequests ();
88 resolver->WaitUntilReady (s: this);
89 write.PrepareToWrite ();
90 std::swap (a&: to, b&: write);
91}
92
93void Server::ProcessRequests (void)
94{
95 std::vector<std::string> words;
96
97 direction = PROCESSING;
98 while (!read.IsAtEnd ())
99 {
100 int err = 0;
101 unsigned ix = Detail::RC_HWM;
102 if (!read.Lex (words))
103 {
104 Assert (!words.empty ());
105 while (ix--)
106 {
107 if (words[0] != std::get<0> (t: requestTable[ix]))
108 continue; // not this one
109
110 if (ix == Detail::RC_CONNECT)
111 {
112 // CONNECT
113 if (IsConnected ())
114 err = -1;
115 else if (auto *r = ConnectRequest (this, resolver, words))
116 resolver = r;
117 else
118 err = -1;
119 }
120 else
121 {
122 if (!IsConnected ())
123 err = -1;
124 else if (int res = (std::get<1> (t: requestTable[ix])
125 (this, resolver, words)))
126 err = res;
127 }
128 break;
129 }
130 }
131
132 if (err || ix >= Detail::RC_HWM)
133 {
134 // Some kind of error
135 std::string msg;
136
137 if (err > 0)
138 msg = u8"error processing '";
139 else if (ix >= Detail::RC_HWM)
140 msg = u8"unrecognized '";
141 else if (IsConnected () && ix == Detail::RC_CONNECT)
142 msg = u8"already connected '";
143 else if (!IsConnected () && ix != Detail::RC_CONNECT)
144 msg = u8"not connected '";
145 else
146 msg = u8"malformed '";
147
148 read.LexedLine (l&: msg);
149 msg.append (s: u8"'");
150 if (err > 0)
151 {
152 msg.append (s: u8" ");
153 msg.append (s: strerror (errnum: err));
154 }
155 resolver->ErrorResponse (s: this, msg: std::move (msg));
156 }
157 }
158}
159
160// Return numeric value of STR as an unsigned. Returns ~0u on error
161// (so that value is not representable).
162static unsigned ParseUnsigned (std::string &str)
163{
164 char *eptr;
165 unsigned long val = strtoul (nptr: str.c_str (), endptr: &eptr, base: 10);
166 if (*eptr || unsigned (val) != val)
167 return ~0u;
168
169 return unsigned (val);
170}
171
172Resolver *ConnectRequest (Server *s, Resolver *r,
173 std::vector<std::string> &words)
174{
175 if (words.size () < 3 || words.size () > 4)
176 return nullptr;
177
178 if (words.size () == 3)
179 words.emplace_back (args: u8"");
180 unsigned version = ParseUnsigned (str&: words[1]);
181 if (version == ~0u)
182 return nullptr;
183
184 return r->ConnectRequest (s, version, agent&: words[2], ident&: words[3]);
185}
186
187int ModuleRepoRequest (Server *s, Resolver *r,std::vector<std::string> &words)
188{
189 if (words.size () != 1)
190 return -1;
191
192 return r->ModuleRepoRequest (s);
193}
194
195int ModuleExportRequest (Server *s, Resolver *r, std::vector<std::string> &words)
196{
197 if (words.size () < 2 || words.size () > 3 || words[1].empty ())
198 return -1;
199
200 Flags flags = Flags::None;
201 if (words.size () == 3)
202 {
203 unsigned val = ParseUnsigned (str&: words[2]);
204 if (val == ~0u)
205 return -1;
206 flags = Flags (val);
207 }
208
209 return r->ModuleExportRequest (s, flags, module&: words[1]);
210}
211
212int ModuleImportRequest (Server *s, Resolver *r, std::vector<std::string> &words)
213{
214 if (words.size () < 2 || words.size () > 3 || words[1].empty ())
215 return -1;
216
217 Flags flags = Flags::None;
218 if (words.size () == 3)
219 {
220 unsigned val = ParseUnsigned (str&: words[2]);
221 if (val == ~0u)
222 return -1;
223 flags = Flags (val);
224 }
225
226 return r->ModuleImportRequest (s, flags, module&: words[1]);
227}
228
229int ModuleCompiledRequest (Server *s, Resolver *r,
230 std::vector<std::string> &words)
231{
232 if (words.size () < 2 || words.size () > 3 || words[1].empty ())
233 return -1;
234
235 Flags flags = Flags::None;
236 if (words.size () == 3)
237 {
238 unsigned val = ParseUnsigned (str&: words[2]);
239 if (val == ~0u)
240 return -1;
241 flags = Flags (val);
242 }
243
244 return r->ModuleCompiledRequest (s, flags, module&: words[1]);
245}
246
247int IncludeTranslateRequest (Server *s, Resolver *r,
248 std::vector<std::string> &words)
249{
250 if (words.size () < 2 || words.size () > 3 || words[1].empty ())
251 return -1;
252
253 Flags flags = Flags::None;
254 if (words.size () == 3)
255 {
256 unsigned val = ParseUnsigned (str&: words[2]);
257 if (val == ~0u)
258 return -1;
259 flags = Flags (val);
260 }
261
262 return r->IncludeTranslateRequest (s, flags, include&: words[1]);
263}
264
265void Server::ErrorResponse (char const *error, size_t elen)
266{
267 write.BeginLine ();
268 write.AppendWord (str: u8"ERROR");
269 write.AppendWord (str: error, maybe_quote: true, len: elen);
270 write.EndLine ();
271}
272
273void Server::OKResponse ()
274{
275 write.BeginLine ();
276 write.AppendWord (str: u8"OK");
277 write.EndLine ();
278}
279
280void Server::ConnectResponse (char const *agent, size_t alen)
281{
282 is_connected = true;
283
284 write.BeginLine ();
285 write.AppendWord (str: u8"HELLO");
286 write.AppendInteger (u: Version);
287 write.AppendWord (str: agent, maybe_quote: true, len: alen);
288 write.EndLine ();
289}
290
291void Server::PathnameResponse (char const *cmi, size_t clen)
292{
293 write.BeginLine ();
294 write.AppendWord (str: u8"PATHNAME");
295 write.AppendWord (str: cmi, maybe_quote: true, len: clen);
296 write.EndLine ();
297}
298
299void Server::BoolResponse (bool truthiness)
300{
301 write.BeginLine ();
302 write.AppendWord (str: u8"BOOL");
303 write.AppendWord (str: truthiness ? u8"TRUE" : u8"FALSE");
304 write.EndLine ();
305}
306
307}
308

source code of libcody/server.cc