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