1 // CODYlib -*- mode:c++ -*-
2 // Copyright (C) 2020 Nathan Sidwell, nathan@acm.org
3 // License: Apache v2.0
16 // These do not need to be members
17 static Packet
ConnectResponse (std::vector
<std::string
> &words
);
18 static Packet
PathnameResponse (std::vector
<std::string
> &words
);
19 static Packet
OKResponse (std::vector
<std::string
> &words
);
20 static Packet
IncludeTranslateResponse (std::vector
<std::string
> &words
);
22 // Must be consistently ordered with the RequestCode enum
23 static Packet (*const responseTable
[Detail::RC_HWM
])
24 (std::vector
<std::string
> &) =
31 &IncludeTranslateResponse
,
39 Client::Client (Client
&&src
)
40 : write (std::move (src
.write
)),
41 read (std::move (src
.read
)),
42 corked (std::move (src
.corked
)),
43 is_direct (src
.is_direct
),
44 is_connected (src
.is_connected
)
50 fd
.from
= src
.fd
.from
;
59 Client
&Client::operator= (Client
&&src
)
61 write
= std::move (src
.write
);
62 read
= std::move (src
.read
);
63 corked
= std::move (src
.corked
);
64 is_direct
= src
.is_direct
;
65 is_connected
= src
.is_connected
;
70 fd
.from
= src
.fd
.from
;
77 int Client::CommunicateWithServer ()
79 write
.PrepareToWrite ();
80 read
.PrepareToRead ();
82 server
->DirectProcess (write
, read
);
85 // Write the write buffer
86 while (int e
= write
.Write (fd
.to
))
87 if (e
!= EAGAIN
&& e
!= EINTR
)
89 // Read the read buffer
90 while (int e
= read
.Read (fd
.from
))
91 if (e
!= EAGAIN
&& e
!= EINTR
)
98 static Packet
CommunicationError (int err
)
100 std::string e
{u8
"communication error:"};
101 e
.append (strerror (err
));
103 return Packet (Client::PC_ERROR
, std::move (e
));
106 Packet
Client::ProcessResponse (std::vector
<std::string
> &words
,
107 unsigned code
, bool isLast
)
109 if (int e
= read
.Lex (words
))
113 std::string
msg (u8
"malformed string '");
114 msg
.append (words
[0]);
116 return Packet (Client::PC_ERROR
, std::move (msg
));
119 return Packet (Client::PC_ERROR
, u8
"missing response");
122 Assert (!words
.empty ());
123 if (words
[0] == u8
"ERROR")
124 return Packet (Client::PC_ERROR
,
125 words
.size () == 2 ? words
[1]: u8
"malformed error response");
127 if (isLast
&& !read
.IsAtEnd ())
128 return Packet (Client::PC_ERROR
,
129 std::string (u8
"unexpected extra response"));
131 Assert (code
< Detail::RC_HWM
);
132 Packet
result (responseTable
[code
] (words
));
133 result
.SetRequest (code
);
134 if (result
.GetCode () == Client::PC_ERROR
&& result
.GetString ().empty ())
136 std::string msg
{u8
"malformed response '"};
138 read
.LexedLine (msg
);
140 result
.GetString () = std::move (msg
);
142 else if (result
.GetCode () == Client::PC_CONNECT
)
148 Packet
Client::MaybeRequest (unsigned code
)
152 corked
.push_back (code
);
153 return Packet (PC_CORKED
);
156 if (int err
= CommunicateWithServer ())
157 return CommunicationError (err
);
159 std::vector
<std::string
> words
;
160 return ProcessResponse(words
, code
, true);
166 corked
.push_back (-1);
169 std::vector
<Packet
> Client::Uncork ()
171 std::vector
<Packet
> result
;
173 if (corked
.size () > 1)
175 if (int err
= CommunicateWithServer ())
176 result
.emplace_back (CommunicationError (err
));
179 std::vector
<std::string
> words
;
180 for (auto iter
= corked
.begin () + 1; iter
!= corked
.end ();)
184 result
.emplace_back (ProcessResponse (words
, code
,
185 iter
== corked
.end ()));
195 // Now the individual message handlers
197 // HELLO $vernum $agent $ident
198 Packet
Client::Connect (char const *agent
, char const *ident
,
199 size_t alen
, size_t ilen
)
202 write
.AppendWord (u8
"HELLO");
203 write
.AppendInteger (Version
);
204 write
.AppendWord (agent
, true, alen
);
205 write
.AppendWord (ident
, true, ilen
);
208 return MaybeRequest (Detail::RC_CONNECT
);
211 // HELLO $version $agent [$flags]
212 Packet
ConnectResponse (std::vector
<std::string
> &words
)
214 if (words
[0] == u8
"HELLO" && (words
.size () == 3 || words
.size () == 4))
217 unsigned long val
= strtoul (words
[1].c_str (), &eptr
, 10);
219 unsigned version
= unsigned (val
);
220 if (*eptr
|| version
!= val
|| version
< Version
)
221 return Packet (Client::PC_ERROR
, u8
"incompatible version");
225 if (words
.size () == 4)
227 val
= strtoul (words
[3].c_str (), &eptr
, 10);
228 flags
= unsigned (val
);
230 return Packet (Client::PC_CONNECT
, flags
);
234 return Packet (Client::PC_ERROR
, u8
"");
238 Packet
Client::ModuleRepo ()
241 write
.AppendWord (u8
"MODULE-REPO");
244 return MaybeRequest (Detail::RC_MODULE_REPO
);
247 // PATHNAME $dir | ERROR
248 Packet
PathnameResponse (std::vector
<std::string
> &words
)
250 if (words
[0] == u8
"PATHNAME" && words
.size () == 2)
251 return Packet (Client::PC_PATHNAME
, std::move (words
[1]));
253 return Packet (Client::PC_ERROR
, u8
"");
257 Packet
OKResponse (std::vector
<std::string
> &words
)
259 if (words
[0] == u8
"OK")
260 return Packet (Client::PC_OK
);
262 return Packet (Client::PC_ERROR
,
263 words
.size () == 2 ? std::move (words
[1]) : "");
266 // MODULE-EXPORT $modulename [$flags]
267 Packet
Client::ModuleExport (char const *module
, Flags flags
, size_t mlen
)
270 write
.AppendWord (u8
"MODULE-EXPORT");
271 write
.AppendWord (module
, true, mlen
);
272 if (flags
!= Flags::None
)
273 write
.AppendInteger (unsigned (flags
));
276 return MaybeRequest (Detail::RC_MODULE_EXPORT
);
279 // MODULE-IMPORT $modulename [$flags]
280 Packet
Client::ModuleImport (char const *module
, Flags flags
, size_t mlen
)
283 write
.AppendWord (u8
"MODULE-IMPORT");
284 write
.AppendWord (module
, true, mlen
);
285 if (flags
!= Flags::None
)
286 write
.AppendInteger (unsigned (flags
));
289 return MaybeRequest (Detail::RC_MODULE_IMPORT
);
292 // MODULE-COMPILED $modulename [$flags]
293 Packet
Client::ModuleCompiled (char const *module
, Flags flags
, size_t mlen
)
296 write
.AppendWord (u8
"MODULE-COMPILED");
297 write
.AppendWord (module
, true, mlen
);
298 if (flags
!= Flags::None
)
299 write
.AppendInteger (unsigned (flags
));
302 return MaybeRequest (Detail::RC_MODULE_COMPILED
);
305 // INCLUDE-TRANSLATE $includename [$flags]
306 Packet
Client::IncludeTranslate (char const *include
, Flags flags
, size_t ilen
)
309 write
.AppendWord (u8
"INCLUDE-TRANSLATE");
310 write
.AppendWord (include
, true, ilen
);
311 if (flags
!= Flags::None
)
312 write
.AppendInteger (unsigned (flags
));
315 return MaybeRequest (Detail::RC_INCLUDE_TRANSLATE
);
318 // BOOL $knowntextualness
320 Packet
IncludeTranslateResponse (std::vector
<std::string
> &words
)
322 if (words
[0] == u8
"BOOL" && words
.size () == 2)
324 if (words
[1] == u8
"FALSE")
325 return Packet (Client::PC_BOOL
, 0);
326 else if (words
[1] == u8
"TRUE")
327 return Packet (Client::PC_BOOL
, 1);
329 return Packet (Client::PC_ERROR
, u8
"");
332 return PathnameResponse (words
);