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 | |
16 | namespace Cody { |
17 | |
18 | // These do not need to be members |
19 | static Resolver *ConnectRequest (Server *, Resolver *, |
20 | std::vector<std::string> &words); |
21 | static int ModuleRepoRequest (Server *, Resolver *, |
22 | std::vector<std::string> &words); |
23 | static int ModuleExportRequest (Server *, Resolver *, |
24 | std::vector<std::string> &words); |
25 | static int ModuleImportRequest (Server *, Resolver *, |
26 | std::vector<std::string> &words); |
27 | static int ModuleCompiledRequest (Server *, Resolver *, |
28 | std::vector<std::string> &words); |
29 | static int IncludeTranslateRequest (Server *, Resolver *, |
30 | std::vector<std::string> &words); |
31 | |
32 | namespace { |
33 | using RequestFn = int (Server *, Resolver *, std::vector<std::string> &); |
34 | using RequestPair = std::tuple<char const *, RequestFn *>; |
35 | static 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 | |
48 | Server::Server (Resolver *r) |
49 | : resolver (r), direction (READING) |
50 | { |
51 | PrepareToRead (); |
52 | } |
53 | |
54 | Server::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 | |
65 | Server::~Server () |
66 | { |
67 | } |
68 | |
69 | Server &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 | |
82 | void 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 | |
93 | void 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). |
162 | static 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 | |
172 | Resolver *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 | |
187 | int 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 | |
195 | int 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 | |
212 | int 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 | |
229 | int 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 | |
247 | int 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 | |
265 | void 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 | |
273 | void Server::OKResponse () |
274 | { |
275 | write.BeginLine (); |
276 | write.AppendWord (str: u8"OK" ); |
277 | write.EndLine (); |
278 | } |
279 | |
280 | void 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 | |
291 | void 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 | |
299 | void 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 | |