1 | // CODYlib -*- mode:c++ -*- |
2 | // Copyright (C) 2020 Nathan Sidwell, nathan@acm.org |
3 | // License: Apache v2.0 |
4 | |
5 | #ifndef CODY_HH |
6 | #define CODY_HH 1 |
7 | |
8 | // If the user specifies this as non-zero, it must be what we expect, |
9 | // generally only good for requesting no networking |
10 | #if !defined (CODY_NETWORKING) |
11 | // Have a known-good list of networking systems |
12 | #if defined (__unix__) || defined (__MACH__) |
13 | #define CODY_NETWORKING 1 |
14 | #else |
15 | #define CODY_NETWORKING 0 |
16 | #endif |
17 | #if 0 // For testing |
18 | #undef CODY_NETWORKING |
19 | #define CODY_NETWORKING 0 |
20 | #endif |
21 | #endif |
22 | |
23 | // C++ |
24 | #include <memory> |
25 | #include <string> |
26 | #include <vector> |
27 | // C |
28 | #include <cstddef> |
29 | // OS |
30 | #include <errno.h> |
31 | #include <sys/types.h> |
32 | #if CODY_NETWORKING |
33 | #include <sys/socket.h> |
34 | #endif |
35 | |
36 | namespace Cody { |
37 | |
38 | // Set version to 1, as this is completely incompatible with 0. |
39 | // Fortunately both versions 0 and 1 will recognize each other's HELLO |
40 | // messages sufficiently to error out |
41 | constexpr unsigned Version = 1; |
42 | |
43 | // FIXME: I guess we need a file-handle abstraction here |
44 | // Is windows DWORDPTR still?, or should it be FILE *? (ew). |
45 | |
46 | namespace Detail { |
47 | |
48 | // C++11 doesn't have utf8 character literals :( |
49 | |
50 | template<unsigned I> |
51 | constexpr char S2C (char const (&s)[I]) |
52 | { |
53 | static_assert (I == 2, "only single octet strings may be converted" ); |
54 | return s[0]; |
55 | } |
56 | |
57 | /// Internal buffering class. Used to concatenate outgoing messages |
58 | /// and Lex incoming ones. |
59 | class MessageBuffer |
60 | { |
61 | std::vector<char> buffer; ///< buffer holding the message |
62 | size_t lastBol = 0; ///< location of the most recent Beginning Of |
63 | ///< Line, or position we've readed when writing |
64 | |
65 | public: |
66 | MessageBuffer () = default; |
67 | ~MessageBuffer () = default; |
68 | MessageBuffer (MessageBuffer &&) = default; |
69 | MessageBuffer &operator= (MessageBuffer &&) = default; |
70 | |
71 | public: |
72 | /// |
73 | /// Finalize a buffer to be written. No more lines can be added to |
74 | /// the buffer. Use before a sequence of Write calls. |
75 | void PrepareToWrite () |
76 | { |
77 | buffer.push_back (x: u8"\n" [0]); |
78 | lastBol = 0; |
79 | } |
80 | /// |
81 | /// Prepare a buffer for reading. Use before a sequence of Read calls. |
82 | void PrepareToRead () |
83 | { |
84 | buffer.clear (); |
85 | lastBol = 0; |
86 | } |
87 | |
88 | public: |
89 | /// Begin a message line. Use before a sequence of Append and |
90 | /// related calls. |
91 | void BeginLine (); |
92 | /// End a message line. Use after a sequence of Append and related calls. |
93 | void EndLine () {} |
94 | |
95 | public: |
96 | /// Append a string to the current line. No whitespace is prepended |
97 | /// or appended. |
98 | /// |
99 | /// @param str the string to be written |
100 | /// @param maybe_quote indicate if there's a possibility the string |
101 | /// contains characters that need quoting. Defaults to false. |
102 | /// It is always safe to set |
103 | /// this true, but that causes an additional scan of the string. |
104 | /// @param len The length of the string. If not specified, strlen |
105 | /// is used to find the length. |
106 | void Append (char const *str, bool maybe_quote = false, |
107 | size_t len = ~size_t (0)); |
108 | |
109 | /// |
110 | /// Add whitespace word separator. Multiple adjacent whitespace is fine. |
111 | void Space () |
112 | { |
113 | Append (c: Detail::S2C(s: u8" " )); |
114 | } |
115 | |
116 | public: |
117 | /// Add a word as with Append, but prefixing whitespace to make a |
118 | /// separate word |
119 | void AppendWord (char const *str, bool maybe_quote = false, |
120 | size_t len = ~size_t (0)) |
121 | { |
122 | if (buffer.size () != lastBol) |
123 | Space (); |
124 | Append (str, maybe_quote, len); |
125 | } |
126 | /// Add a word as with AppendWord |
127 | /// @param str the string to append |
128 | /// @param maybe_quote string might need quoting, as for Append |
129 | void AppendWord (std::string const &str, bool maybe_quote = false) |
130 | { |
131 | AppendWord (str: str.data (), maybe_quote, len: str.size ()); |
132 | } |
133 | /// |
134 | /// Add an integral value, prepending a space. |
135 | void AppendInteger (unsigned u); |
136 | |
137 | private: |
138 | /// Append a literal character. |
139 | /// @param c character to append |
140 | void Append (char c); |
141 | |
142 | public: |
143 | /// Lex the next input line into a vector of words. |
144 | /// @param words filled with a vector of lexed strings |
145 | /// @result 0 if no errors, an errno value on lexxing error such as |
146 | /// there being no next line (ENOENT), or malformed quoting (EINVAL) |
147 | int Lex (std::vector<std::string> &words); |
148 | |
149 | public: |
150 | /// Append the most-recently lexxed line to a string. May be useful |
151 | /// in error messages. The unparsed line is appended -- before any |
152 | /// unquoting. |
153 | /// If we had c++17 string_view, we'd simply return a view of the |
154 | /// line, and leave it to the caller to do any concatenation. |
155 | /// @param l string to-which the lexxed line is appended. |
156 | void LexedLine (std::string &l); |
157 | |
158 | public: |
159 | /// Detect if we have reached the end of the input buffer. |
160 | /// I.e. there are no more lines to Lex |
161 | /// @result True if at end |
162 | bool IsAtEnd () const |
163 | { |
164 | return lastBol == buffer.size (); |
165 | } |
166 | |
167 | public: |
168 | /// Read from end point into a read buffer, as with read(2). This will |
169 | /// not block , unless FD is blocking, and there is nothing |
170 | /// immediately available. |
171 | /// @param fd file descriptor to read from. This may be a regular |
172 | /// file, pipe or socket. |
173 | /// @result on error returns errno. If end of file occurs, returns |
174 | /// -1. At end of message returns 0. If there is more needed |
175 | /// returns EAGAIN (or possibly EINTR). If the message is |
176 | /// malformed, returns EINVAL. |
177 | int Read (int fd) noexcept; |
178 | |
179 | public: |
180 | /// Write to an end point from a write buffer, as with write(2). As |
181 | /// with Read, this will not usually block. |
182 | /// @param fd file descriptor to write to. This may be a regular |
183 | /// file, pipe or socket. |
184 | /// @result on error returns errno. |
185 | /// At end of message returns 0. If there is more to write |
186 | /// returns EAGAIN (or possibly EINTR). |
187 | int Write (int fd) noexcept; |
188 | }; |
189 | |
190 | /// |
191 | /// Request codes. Perhaps this should be exposed? These are likely |
192 | /// useful to servers that queue requests. |
193 | enum RequestCode |
194 | { |
195 | RC_CONNECT, |
196 | RC_MODULE_REPO, |
197 | RC_MODULE_EXPORT, |
198 | RC_MODULE_IMPORT, |
199 | RC_MODULE_COMPILED, |
200 | RC_INCLUDE_TRANSLATE, |
201 | RC_HWM |
202 | }; |
203 | |
204 | /// Internal file descriptor tuple. It's used as an anonymous union member. |
205 | struct FD |
206 | { |
207 | int from; ///< Read from this FD |
208 | int to; ///< Write to this FD |
209 | }; |
210 | |
211 | } |
212 | |
213 | // Flags for various requests |
214 | enum class Flags : unsigned |
215 | { |
216 | None, |
217 | NameOnly = 1<<0, // Only querying for CMI names, not contents |
218 | }; |
219 | |
220 | inline Flags operator& (Flags a, Flags b) |
221 | { |
222 | return Flags (unsigned (a) & unsigned (b)); |
223 | } |
224 | inline Flags operator| (Flags a, Flags b) |
225 | { |
226 | return Flags (unsigned (a) | unsigned (b)); |
227 | } |
228 | |
229 | /// |
230 | /// Response data for a request. Returned by Client's request calls, |
231 | /// which return a single Packet. When the connection is Corked, the |
232 | /// Uncork call will return a vector of Packets. |
233 | class Packet |
234 | { |
235 | public: |
236 | /// |
237 | /// Packet is a variant structure. These are the possible content types. |
238 | enum Category { INTEGER, STRING, VECTOR}; |
239 | |
240 | private: |
241 | // std:variant is a C++17 thing, so we're doing this ourselves. |
242 | union |
243 | { |
244 | size_t integer; ///< Integral value |
245 | std::string string; ///< String value |
246 | std::vector<std::string> vector; ///< Vector of string value |
247 | }; |
248 | Category cat : 2; ///< Discriminator |
249 | |
250 | private: |
251 | unsigned short code = 0; ///< Packet type |
252 | unsigned short request = 0; |
253 | |
254 | public: |
255 | Packet (unsigned c, size_t i = 0) |
256 | : integer (i), cat (INTEGER), code (c) |
257 | { |
258 | } |
259 | Packet (unsigned c, std::string &&s) |
260 | : string (std::move (s)), cat (STRING), code (c) |
261 | { |
262 | } |
263 | Packet (unsigned c, std::string const &s) |
264 | : string (s), cat (STRING), code (c) |
265 | { |
266 | } |
267 | Packet (unsigned c, std::vector<std::string> &&v) |
268 | : vector (std::move (v)), cat (VECTOR), code (c) |
269 | { |
270 | } |
271 | // No non-move constructor from a vector. You should not be doing |
272 | // that. |
273 | |
274 | // Only move constructor and move assignment |
275 | Packet (Packet &&t) |
276 | { |
277 | Create (t: std::move (t)); |
278 | } |
279 | Packet &operator= (Packet &&t) |
280 | { |
281 | Destroy (); |
282 | Create (t: std::move (t)); |
283 | |
284 | return *this; |
285 | } |
286 | ~Packet () |
287 | { |
288 | Destroy (); |
289 | } |
290 | |
291 | private: |
292 | /// |
293 | /// Variant move creation from another packet |
294 | void Create (Packet &&t); |
295 | /// |
296 | /// Variant destruction |
297 | void Destroy (); |
298 | |
299 | public: |
300 | /// |
301 | /// Return the packet type |
302 | unsigned GetCode () const |
303 | { |
304 | return code; |
305 | } |
306 | /// |
307 | /// Return the packet type |
308 | unsigned GetRequest () const |
309 | { |
310 | return request; |
311 | } |
312 | void SetRequest (unsigned r) |
313 | { |
314 | request = r; |
315 | } |
316 | /// |
317 | /// Return the category of the packet's payload |
318 | Category GetCategory () const |
319 | { |
320 | return cat; |
321 | } |
322 | |
323 | public: |
324 | /// |
325 | /// Return an integral payload. Undefined if the category is not INTEGER |
326 | size_t GetInteger () const |
327 | { |
328 | return integer; |
329 | } |
330 | /// |
331 | /// Return (a reference to) a string payload. Undefined if the |
332 | /// category is not STRING |
333 | std::string const &GetString () const |
334 | { |
335 | return string; |
336 | } |
337 | std::string &GetString () |
338 | { |
339 | return string; |
340 | } |
341 | /// |
342 | /// Return (a reference to) a constant vector of strings payload. |
343 | /// Undefined if the category is not VECTOR |
344 | std::vector<std::string> const &GetVector () const |
345 | { |
346 | return vector; |
347 | } |
348 | /// |
349 | /// Return (a reference to) a non-conatant vector of strings payload. |
350 | /// Undefined if the category is not VECTOR |
351 | std::vector<std::string> &GetVector () |
352 | { |
353 | return vector; |
354 | } |
355 | }; |
356 | |
357 | class Server; |
358 | |
359 | /// |
360 | /// Client-side (compiler) object. |
361 | class Client |
362 | { |
363 | public: |
364 | /// Response packet codes |
365 | enum PacketCode |
366 | { |
367 | PC_CORKED, ///< Messages are corked |
368 | PC_CONNECT, ///< Packet is integer version |
369 | PC_ERROR, ///< Packet is error string |
370 | PC_OK, |
371 | PC_BOOL, |
372 | PC_PATHNAME |
373 | }; |
374 | |
375 | private: |
376 | Detail::MessageBuffer write; ///< Outgoing write buffer |
377 | Detail::MessageBuffer read; ///< Incoming read buffer |
378 | std::string corked; ///< Queued request tags |
379 | union |
380 | { |
381 | Detail::FD fd; ///< FDs connecting to server |
382 | Server *server; ///< Directly connected server |
383 | }; |
384 | bool is_direct = false; ///< Discriminator |
385 | bool is_connected = false; /// Connection handshake succesful |
386 | |
387 | private: |
388 | Client (); |
389 | public: |
390 | /// Direct connection constructor. |
391 | /// @param s Server to directly connect |
392 | Client (Server *s) |
393 | : Client () |
394 | { |
395 | is_direct = true; |
396 | server = s; |
397 | } |
398 | /// Communication connection constructor |
399 | /// @param from file descriptor to read from |
400 | /// @param to file descriptor to write to, defaults to from |
401 | Client (int from, int to = -1) |
402 | : Client () |
403 | { |
404 | fd.from = from; |
405 | fd.to = to < 0 ? from : to; |
406 | } |
407 | ~Client (); |
408 | // We have to provide our own move variants, because of the variant member. |
409 | Client (Client &&); |
410 | Client &operator= (Client &&); |
411 | |
412 | public: |
413 | /// |
414 | /// Direct connection predicate |
415 | bool IsDirect () const |
416 | { |
417 | return is_direct; |
418 | } |
419 | /// |
420 | /// Successful handshake predicate |
421 | bool IsConnected () const |
422 | { |
423 | return is_connected; |
424 | } |
425 | |
426 | public: |
427 | /// |
428 | /// Get the read FD |
429 | /// @result the FD to read from, -1 if a direct connection |
430 | int GetFDRead () const |
431 | { |
432 | return is_direct ? -1 : fd.from; |
433 | } |
434 | /// |
435 | /// Get the write FD |
436 | /// @result the FD to write to, -1 if a direct connection |
437 | int GetFDWrite () const |
438 | { |
439 | return is_direct ? -1 : fd.to; |
440 | } |
441 | /// |
442 | /// Get the directly-connected server |
443 | /// @result the server, or nullptr if a communication connection |
444 | Server *GetServer () const |
445 | { |
446 | return is_direct ? server : nullptr; |
447 | } |
448 | |
449 | public: |
450 | /// |
451 | /// Perform connection handshake. All othe requests will result in |
452 | /// errors, until handshake is succesful. |
453 | /// @param agent compiler identification |
454 | /// @param ident compilation identifiation (maybe nullptr) |
455 | /// @param alen length of agent string, if known |
456 | /// @param ilen length of ident string, if known |
457 | /// @result packet indicating success (or deferrment) of the |
458 | /// connection, payload is optional flags |
459 | Packet Connect (char const *agent, char const *ident, |
460 | size_t alen = ~size_t (0), size_t ilen = ~size_t (0)); |
461 | /// std::string wrapper for connection |
462 | /// @param agent compiler identification |
463 | /// @param ident compilation identification |
464 | Packet Connect (std::string const &agent, std::string const &ident) |
465 | { |
466 | return Connect (agent: agent.c_str (), ident: ident.c_str (), |
467 | alen: agent.size (), ilen: ident.size ()); |
468 | } |
469 | |
470 | public: |
471 | /// Request compiler module repository |
472 | /// @result packet indicating repo |
473 | Packet ModuleRepo (); |
474 | |
475 | public: |
476 | /// Inform of compilation of a named module interface or partition, |
477 | /// or a header unit |
478 | /// @param str module or header-unit |
479 | /// @param len name length, if known |
480 | /// @result CMI name (or deferrment/error) |
481 | Packet ModuleExport (char const *str, Flags flags, size_t len = ~size_t (0)); |
482 | |
483 | Packet ModuleExport (char const *str) |
484 | { |
485 | return ModuleExport (str, flags: Flags::None, len: ~size_t (0)); |
486 | } |
487 | Packet ModuleExport (std::string const &s, Flags flags = Flags::None) |
488 | { |
489 | return ModuleExport (str: s.c_str (), flags, len: s.size ()); |
490 | } |
491 | |
492 | public: |
493 | /// Importation of a module, partition or header-unit |
494 | /// @param str module or header-unit |
495 | /// @param len name length, if known |
496 | /// @result CMI name (or deferrment/error) |
497 | Packet ModuleImport (char const *str, Flags flags, size_t len = ~size_t (0)); |
498 | |
499 | Packet ModuleImport (char const *str) |
500 | { |
501 | return ModuleImport (str, flags: Flags::None, len: ~size_t (0)); |
502 | } |
503 | Packet ModuleImport (std::string const &s, Flags flags = Flags::None) |
504 | { |
505 | return ModuleImport (str: s.c_str (), flags, len: s.size ()); |
506 | } |
507 | |
508 | public: |
509 | /// Successful compilation of a module interface, partition or |
510 | /// header-unit. Must have been preceeded by a ModuleExport |
511 | /// request. |
512 | /// @param str module or header-unit |
513 | /// @param len name length, if known |
514 | /// @result OK (or deferment/error) |
515 | Packet ModuleCompiled (char const *str, Flags flags, size_t len = ~size_t (0)); |
516 | |
517 | Packet ModuleCompiled (char const *str) |
518 | { |
519 | return ModuleCompiled (str, flags: Flags::None, len: ~size_t (0)); |
520 | } |
521 | Packet ModuleCompiled (std::string const &s, Flags flags = Flags::None) |
522 | { |
523 | return ModuleCompiled (str: s.c_str (), flags, len: s.size ()); |
524 | } |
525 | |
526 | /// Include translation query. |
527 | /// @param str header unit name |
528 | /// @param len name length, if known |
529 | /// @result Packet indicating include translation boolean, or CMI |
530 | /// name (or deferment/error) |
531 | Packet IncludeTranslate (char const *str, Flags flags, |
532 | size_t len = ~size_t (0)); |
533 | |
534 | Packet IncludeTranslate (char const *str) |
535 | { |
536 | return IncludeTranslate (str, flags: Flags::None, len: ~size_t (0)); |
537 | } |
538 | Packet IncludeTranslate (std::string const &s, Flags flags = Flags::None) |
539 | { |
540 | return IncludeTranslate (str: s.c_str (), flags, len: s.size ()); |
541 | } |
542 | |
543 | public: |
544 | /// Cork the connection. All requests are queued up. Each request |
545 | /// call will return a PC_CORKED packet. |
546 | void Cork (); |
547 | |
548 | /// Uncork the connection. All queued requests are sent to the |
549 | /// server, and a block of responses waited for. |
550 | /// @result A vector of packets, containing the in-order responses to the |
551 | /// queued requests. |
552 | std::vector<Packet> Uncork (); |
553 | /// |
554 | /// Indicate corkedness of connection |
555 | bool IsCorked () const |
556 | { |
557 | return !corked.empty (); |
558 | } |
559 | |
560 | private: |
561 | Packet ProcessResponse (std::vector<std::string> &, unsigned code, |
562 | bool isLast); |
563 | Packet MaybeRequest (unsigned code); |
564 | int CommunicateWithServer (); |
565 | }; |
566 | |
567 | /// This server-side class is used to resolve requests from one or |
568 | /// more clients. You are expected to derive from it and override the |
569 | /// virtual functions it provides. The connection resolver may return |
570 | /// a different resolved object to service the remainder of the |
571 | /// connection -- for instance depending on the compiler that is |
572 | /// making the requests. |
573 | class Resolver |
574 | { |
575 | public: |
576 | Resolver () = default; |
577 | virtual ~Resolver (); |
578 | |
579 | protected: |
580 | /// Mapping from a module or header-unit name to a CMI file name. |
581 | /// @param module module name |
582 | /// @result CMI name |
583 | virtual std::string GetCMIName (std::string const &module); |
584 | |
585 | /// Return the CMI file suffix to use |
586 | /// @result CMI suffix, a statically allocated string |
587 | virtual char const *GetCMISuffix (); |
588 | |
589 | public: |
590 | /// When the requests of a directly-connected server are processed, |
591 | /// we may want to wait for the requests to complete (for instance a |
592 | /// set of subjobs). |
593 | /// @param s directly connected server. |
594 | virtual void WaitUntilReady (Server *s); |
595 | |
596 | public: |
597 | /// Provide an error response. |
598 | /// @param s the server to provide the response to. |
599 | /// @param msg the error message |
600 | virtual void ErrorResponse (Server *s, std::string &&msg); |
601 | |
602 | public: |
603 | /// Connection handshake. Provide response to server and return new |
604 | /// (or current) resolver, or nullptr. |
605 | /// @param s server to provide response to |
606 | /// @param version the client's version number |
607 | /// @param agent the client agent (compiler identification) |
608 | /// @param ident the compilation identification (may be empty) |
609 | /// @result nullptr in the case of an error. An error response will |
610 | /// be sent. If handing off to another resolver, return that, |
611 | /// otherwise this |
612 | virtual Resolver *ConnectRequest (Server *s, unsigned version, |
613 | std::string &agent, std::string &ident); |
614 | |
615 | public: |
616 | // return 0 on ok, ERRNO on failure, -1 on unspecific error |
617 | virtual int ModuleRepoRequest (Server *s); |
618 | |
619 | virtual int ModuleExportRequest (Server *s, Flags flags, |
620 | std::string &module); |
621 | virtual int ModuleImportRequest (Server *s, Flags flags, |
622 | std::string &module); |
623 | virtual int ModuleCompiledRequest (Server *s, Flags flags, |
624 | std::string &module); |
625 | virtual int IncludeTranslateRequest (Server *s, Flags flags, |
626 | std::string &include); |
627 | }; |
628 | |
629 | |
630 | /// This server-side (build system) class handles a single connection |
631 | /// to a client. It has 3 states, READING:accumulating a message |
632 | /// block froma client, WRITING:writing a message block to a client |
633 | /// and PROCESSING:resolving requests. If the server does not spawn |
634 | /// jobs to build needed artifacts, the PROCESSING state will be brief. |
635 | class Server |
636 | { |
637 | public: |
638 | enum Direction |
639 | { |
640 | READING, // Server is waiting for completion of a (set of) |
641 | // requests from client. The next state will be PROCESSING. |
642 | WRITING, // Server is writing a (set of) responses to client. |
643 | // The next state will be READING. |
644 | PROCESSING // Server is processing client request(s). The next |
645 | // state will be WRITING. |
646 | }; |
647 | |
648 | private: |
649 | Detail::MessageBuffer write; |
650 | Detail::MessageBuffer read; |
651 | Resolver *resolver; |
652 | Detail::FD fd; |
653 | bool is_connected = false; |
654 | Direction direction : 2; |
655 | |
656 | public: |
657 | Server (Resolver *r); |
658 | Server (Resolver *r, int from, int to = -1) |
659 | : Server (r) |
660 | { |
661 | fd.from = from; |
662 | fd.to = to >= 0 ? to : from; |
663 | } |
664 | ~Server (); |
665 | Server (Server &&); |
666 | Server &operator= (Server &&); |
667 | |
668 | public: |
669 | bool IsConnected () const |
670 | { |
671 | return is_connected; |
672 | } |
673 | |
674 | public: |
675 | void SetDirection (Direction d) |
676 | { |
677 | direction = d; |
678 | } |
679 | |
680 | public: |
681 | Direction GetDirection () const |
682 | { |
683 | return direction; |
684 | } |
685 | int GetFDRead () const |
686 | { |
687 | return fd.from; |
688 | } |
689 | int GetFDWrite () const |
690 | { |
691 | return fd.to; |
692 | } |
693 | Resolver *GetResolver () const |
694 | { |
695 | return resolver; |
696 | } |
697 | |
698 | public: |
699 | /// Process requests from a directly-connected client. This is a |
700 | /// small wrapper around ProcessRequests, with some buffer swapping |
701 | /// for communication. It is expected that such processessing is |
702 | /// immediate. |
703 | /// @param from message block from client |
704 | /// @param to message block to client |
705 | void DirectProcess (Detail::MessageBuffer &from, Detail::MessageBuffer &to); |
706 | |
707 | public: |
708 | /// Process the messages queued in the read buffer. We enter the |
709 | /// PROCESSING state, and each message line causes various resolver |
710 | /// methods to be called. Once processed, the server may need to |
711 | /// wait for all the requests to be ready, or it may be able to |
712 | /// immediately write responses back. |
713 | void ProcessRequests (); |
714 | |
715 | public: |
716 | /// Accumulate an error response. |
717 | /// @param error the error message to encode |
718 | /// @param elen length of error, if known |
719 | void ErrorResponse (char const *error, size_t elen = ~size_t (0)); |
720 | void ErrorResponse (std::string const &error) |
721 | { |
722 | ErrorResponse (error: error.data (), elen: error.size ()); |
723 | } |
724 | |
725 | /// Accumulate an OK response |
726 | void OKResponse (); |
727 | |
728 | /// Accumulate a boolean response |
729 | void BoolResponse (bool); |
730 | |
731 | /// Accumulate a pathname response |
732 | /// @param path (may be nullptr, or empty) |
733 | /// @param rlen length, if known |
734 | void PathnameResponse (char const *path, size_t plen = ~size_t (0)); |
735 | void PathnameResponse (std::string const &path) |
736 | { |
737 | PathnameResponse (path: path.data (), plen: path.size ()); |
738 | } |
739 | |
740 | public: |
741 | /// Accumulate a (successful) connection response |
742 | /// @param agent the server-side agent |
743 | /// @param alen agent length, if known |
744 | void ConnectResponse (char const *agent, size_t alen = ~size_t (0)); |
745 | void ConnectResponse (std::string const &agent) |
746 | { |
747 | ConnectResponse (agent: agent.data (), alen: agent.size ()); |
748 | } |
749 | |
750 | public: |
751 | /// Write message block to client. Semantics as for |
752 | /// MessageBuffer::Write. |
753 | /// @result errno or completion (0). |
754 | int Write () |
755 | { |
756 | return write.Write (fd: fd.to); |
757 | } |
758 | /// Initialize for writing a message block. All responses to the |
759 | /// incomping message block must be complete Enters WRITING state. |
760 | void PrepareToWrite () |
761 | { |
762 | write.PrepareToWrite (); |
763 | direction = WRITING; |
764 | } |
765 | |
766 | public: |
767 | /// Read message block from client. Semantics as for |
768 | /// MessageBuffer::Read. |
769 | /// @result errno, eof (-1) or completion (0) |
770 | int Read () |
771 | { |
772 | return read.Read (fd: fd.from); |
773 | } |
774 | /// Initialize for reading a message block. Enters READING state. |
775 | void PrepareToRead () |
776 | { |
777 | read.PrepareToRead (); |
778 | direction = READING; |
779 | } |
780 | }; |
781 | |
782 | // Helper network stuff |
783 | |
784 | #if CODY_NETWORKING |
785 | // Socket with specific address |
786 | int OpenSocket (char const **, sockaddr const *sock, socklen_t len); |
787 | int ListenSocket (char const **, sockaddr const *sock, socklen_t len, |
788 | unsigned backlog); |
789 | |
790 | // Local domain socket (eg AF_UNIX) |
791 | int OpenLocal (char const **, char const *name); |
792 | int ListenLocal (char const **, char const *name, unsigned backlog = 0); |
793 | |
794 | // ipv6 socket |
795 | int OpenInet6 (char const **e, char const *name, int port); |
796 | int ListenInet6 (char const **, char const *name, int port, |
797 | unsigned backlog = 0); |
798 | #endif |
799 | |
800 | // FIXME: Mapping file utilities? |
801 | |
802 | } |
803 | |
804 | #endif // CODY_HH |
805 | |