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 4769 : inline Flags operator& (Flags a, Flags b)
237 : {
238 4769 : 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 51468 : ~Packet ()
309 : {
310 51468 : Destroy ();
311 38683 : }
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 34026 : unsigned GetCode () const
325 : {
326 34026 : 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 23758 : size_t GetInteger () const
349 : {
350 23758 : return integer;
351 : }
352 : ///
353 : /// Return (a reference to) a string payload. Undefined if the
354 : /// category is not STRING
355 5507 : std::string const &GetString () const
356 : {
357 5510 : return string;
358 : }
359 : std::string &GetString ()
360 : {
361 4709 : 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 4700 : Client (Server *s)
415 4700 : : Client ()
416 : {
417 4700 : is_direct = true;
418 4700 : 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 9237 : bool IsDirect () const
438 : {
439 9237 : 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 4519 : Server *GetServer () const
467 : {
468 4519 : 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 4709 : Packet Connect (std::string const &agent, std::string const &ident)
487 : {
488 4709 : return Connect (agent.c_str (), ident.c_str (),
489 4709 : 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 2615 : Packet ModuleCompiled (char const *str)
540 : {
541 2615 : 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 4700 : 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 4519 : Resolver *GetResolver () const
716 : {
717 4519 : 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 10195 : void PathnameResponse (std::string const &path)
758 : {
759 10195 : PathnameResponse (path.data (), path.size ());
760 5495 : }
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
|