]>
Commit | Line | Data |
---|---|---|
36230329 NS |
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> | |
1467a5c5 | 11 | #include <cstdlib> |
36230329 NS |
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 (read, from); | |
87 | ProcessRequests (); | |
88 | resolver->WaitUntilReady (this); | |
89 | write.PrepareToWrite (); | |
90 | std::swap (to, 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> (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> (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 (msg); | |
149 | msg.append (u8"'"); | |
150 | if (err > 0) | |
151 | { | |
152 | msg.append (u8" "); | |
153 | msg.append (strerror (err)); | |
154 | } | |
155 | resolver->ErrorResponse (this, 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 (str.c_str (), &eptr, 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 (u8""); | |
180 | unsigned version = ParseUnsigned (words[1]); | |
181 | if (version == ~0u) | |
182 | return nullptr; | |
183 | ||
184 | return r->ConnectRequest (s, version, words[2], 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 (words[2]); | |
204 | if (val == ~0u) | |
205 | return -1; | |
206 | flags = Flags (val); | |
207 | } | |
208 | ||
209 | return r->ModuleExportRequest (s, flags, 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 (words[2]); | |
221 | if (val == ~0u) | |
222 | return -1; | |
223 | flags = Flags (val); | |
224 | } | |
225 | ||
226 | return r->ModuleImportRequest (s, flags, 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 (words[2]); | |
239 | if (val == ~0u) | |
240 | return -1; | |
241 | flags = Flags (val); | |
242 | } | |
243 | ||
244 | return r->ModuleCompiledRequest (s, flags, 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 (words[2]); | |
257 | if (val == ~0u) | |
258 | return -1; | |
259 | flags = Flags (val); | |
260 | } | |
261 | ||
262 | return r->IncludeTranslateRequest (s, flags, words[1]); | |
263 | } | |
264 | ||
265 | void Server::ErrorResponse (char const *error, size_t elen) | |
266 | { | |
267 | write.BeginLine (); | |
268 | write.AppendWord (u8"ERROR"); | |
269 | write.AppendWord (error, true, elen); | |
270 | write.EndLine (); | |
271 | } | |
272 | ||
273 | void Server::OKResponse () | |
274 | { | |
275 | write.BeginLine (); | |
276 | write.AppendWord (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 (u8"HELLO"); | |
286 | write.AppendInteger (Version); | |
287 | write.AppendWord (agent, true, alen); | |
288 | write.EndLine (); | |
289 | } | |
290 | ||
291 | void Server::PathnameResponse (char const *cmi, size_t clen) | |
292 | { | |
293 | write.BeginLine (); | |
294 | write.AppendWord (u8"PATHNAME"); | |
295 | write.AppendWord (cmi, true, clen); | |
296 | write.EndLine (); | |
297 | } | |
298 | ||
299 | void Server::BoolResponse (bool truthiness) | |
300 | { | |
301 | write.BeginLine (); | |
302 | write.AppendWord (u8"BOOL"); | |
303 | write.AppendWord (truthiness ? u8"TRUE" : u8"FALSE"); | |
304 | write.EndLine (); | |
305 | } | |
306 | ||
307 | } |