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 std::move (words
.size () == 2 ? words
[1]
126 : u8
"malformed error response"));
128 if (isLast
&& !read
.IsAtEnd ())
129 return Packet (Client::PC_ERROR
,
130 std::string (u8
"unexpected extra response"));
132 Assert (code
< Detail::RC_HWM
);
133 Packet
result (responseTable
[code
] (words
));
134 result
.SetRequest (code
);
135 if (result
.GetCode () == Client::PC_ERROR
&& result
.GetString ().empty ())
137 std::string msg
{u8
"malformed response '"};
139 read
.LexedLine (msg
);
141 result
.GetString () = std::move (msg
);
143 else if (result
.GetCode () == Client::PC_CONNECT
)
149 Packet
Client::MaybeRequest (unsigned code
)
153 corked
.push_back (code
);
154 return Packet (PC_CORKED
);
157 if (int err
= CommunicateWithServer ())
158 return CommunicationError (err
);
160 std::vector
<std::string
> words
;
161 return ProcessResponse(words
, code
, true);
167 corked
.push_back (-1);
170 std::vector
<Packet
> Client::Uncork ()
172 std::vector
<Packet
> result
;
174 if (corked
.size () > 1)
176 if (int err
= CommunicateWithServer ())
177 result
.emplace_back (CommunicationError (err
));
180 std::vector
<std::string
> words
;
181 for (auto iter
= corked
.begin () + 1; iter
!= corked
.end ();)
185 result
.emplace_back (ProcessResponse (words
, code
,
186 iter
== corked
.end ()));
196 // Now the individual message handlers
198 // HELLO $vernum $agent $ident
199 Packet
Client::Connect (char const *agent
, char const *ident
,
200 size_t alen
, size_t ilen
)
203 write
.AppendWord (u8
"HELLO");
204 write
.AppendInteger (Version
);
205 write
.AppendWord (agent
, true, alen
);
206 write
.AppendWord (ident
, true, ilen
);
209 return MaybeRequest (Detail::RC_CONNECT
);
212 // HELLO $version $agent [$flags]
213 Packet
ConnectResponse (std::vector
<std::string
> &words
)
215 if (words
[0] == u8
"HELLO" && (words
.size () == 3 || words
.size () == 4))
218 unsigned long val
= strtoul (words
[1].c_str (), &eptr
, 10);
220 unsigned version
= unsigned (val
);
221 if (*eptr
|| version
!= val
|| version
< Version
)
222 return Packet (Client::PC_ERROR
, u8
"incompatible version");
226 if (words
.size () == 4)
228 val
= strtoul (words
[3].c_str (), &eptr
, 10);
229 flags
= unsigned (val
);
231 return Packet (Client::PC_CONNECT
, flags
);
235 return Packet (Client::PC_ERROR
, u8
"");
239 Packet
Client::ModuleRepo ()
242 write
.AppendWord (u8
"MODULE-REPO");
245 return MaybeRequest (Detail::RC_MODULE_REPO
);
248 // PATHNAME $dir | ERROR
249 Packet
PathnameResponse (std::vector
<std::string
> &words
)
251 if (words
[0] == u8
"PATHNAME" && words
.size () == 2)
252 return Packet (Client::PC_PATHNAME
, std::move (words
[1]));
254 return Packet (Client::PC_ERROR
, u8
"");
258 Packet
OKResponse (std::vector
<std::string
> &words
)
260 if (words
[0] == u8
"OK")
261 return Packet (Client::PC_OK
);
263 return Packet (Client::PC_ERROR
,
264 words
.size () == 2 ? std::move (words
[1]) : "");
267 // MODULE-EXPORT $modulename [$flags]
268 Packet
Client::ModuleExport (char const *module
, Flags flags
, size_t mlen
)
271 write
.AppendWord (u8
"MODULE-EXPORT");
272 write
.AppendWord (module
, true, mlen
);
273 if (flags
!= Flags::None
)
274 write
.AppendInteger (unsigned (flags
));
277 return MaybeRequest (Detail::RC_MODULE_EXPORT
);
280 // MODULE-IMPORT $modulename [$flags]
281 Packet
Client::ModuleImport (char const *module
, Flags flags
, size_t mlen
)
284 write
.AppendWord (u8
"MODULE-IMPORT");
285 write
.AppendWord (module
, true, mlen
);
286 if (flags
!= Flags::None
)
287 write
.AppendInteger (unsigned (flags
));
290 return MaybeRequest (Detail::RC_MODULE_IMPORT
);
293 // MODULE-COMPILED $modulename [$flags]
294 Packet
Client::ModuleCompiled (char const *module
, Flags flags
, size_t mlen
)
297 write
.AppendWord (u8
"MODULE-COMPILED");
298 write
.AppendWord (module
, true, mlen
);
299 if (flags
!= Flags::None
)
300 write
.AppendInteger (unsigned (flags
));
303 return MaybeRequest (Detail::RC_MODULE_COMPILED
);
306 // INCLUDE-TRANSLATE $includename [$flags]
307 Packet
Client::IncludeTranslate (char const *include
, Flags flags
, size_t ilen
)
310 write
.AppendWord (u8
"INCLUDE-TRANSLATE");
311 write
.AppendWord (include
, true, ilen
);
312 if (flags
!= Flags::None
)
313 write
.AppendInteger (unsigned (flags
));
316 return MaybeRequest (Detail::RC_INCLUDE_TRANSLATE
);
319 // BOOL $knowntextualness
321 Packet
IncludeTranslateResponse (std::vector
<std::string
> &words
)
323 if (words
[0] == u8
"BOOL" && words
.size () == 2)
325 if (words
[1] == u8
"FALSE")
326 return Packet (Client::PC_BOOL
, 0);
327 else if (words
[1] == u8
"TRUE")
328 return Packet (Client::PC_BOOL
, 1);
330 return Packet (Client::PC_ERROR
, u8
"");
333 return PathnameResponse (words
);