1 #ifndef NETCACHE__SRV_SOCKETS__HPP
2 #define NETCACHE__SRV_SOCKETS__HPP
3 /*  $Id: srv_sockets.hpp 544804 2017-08-28 14:38:49Z gouriano $
4  * ===========================================================================
5  *
6  *                            PUBLIC DOMAIN NOTICE
7  *               National Center for Biotechnology Information
8  *
9  *  This software/database is a "United States Government Work" under the
10  *  terms of the United States Copyright Act.  It was written as part of
11  *  the author's official duties as a United States Government employee and
12  *  thus cannot be copyrighted.  This software/database is freely available
13  *  to the public for use. The National Library of Medicine and the U.S.
14  *  Government have not placed any restriction on its use or reproduction.
15  *
16  *  Although all reasonable efforts have been taken to ensure the accuracy
17  *  and reliability of the software and data, the NLM and the U.S.
18  *  Government do not and cannot warrant the performance or results that
19  *  may be obtained by using this software or data. The NLM and the U.S.
20  *  Government disclaim all warranties, express or implied, including
21  *  warranties of performance, merchantability or fitness for any particular
22  *  purpose.
23  *
24  *  Please cite the author in any work or product based on this material.
25  *
26  * ===========================================================================
27  *
28  * Authors:  Pavel Ivanov
29  *
30  * File Description:
31  */
32 
33 
34 namespace intr = boost::intrusive;
35 
36 
37 BEGIN_NCBI_SCOPE
38 
39 
40 /// For TaskServer's internal use only.
41 /// This structure is used as base for both CSrvSocketTask and internal
42 /// listening socket structure. Pointer to SSrvSocketInfo is remembered in
43 /// epoll and value of is_listening is used to distinguish between listening
44 /// socket structure and CSrvSocketTask.
45 struct SSrvSocketInfo
46 {
47     bool is_listening;
48 };
49 
50 
51 /*
52     from here:
53     http://www.boost.org/doc/libs/1_56_0/doc/html/intrusive/usage.html
54 
55     If there is a virtual inheritance relationship between the parent and
56     the member hook, then the distance between the parent and the hook is not
57     a compile-time fixed value so obtaining the address of the parent from
58     the member hook is not possible without reverse engineering compiler produced RTTI.
59     Apart from this, the non-standard pointer to member implementation for classes with
60     complex inheritance relationships in MSVC ABI compatible-compilers is not supported
61     by member hooks since it also depends on compiler-produced RTTI information.
62 
63     This means member hooks are potentially dangerous, because we DO use virtual inheritance here.
64     With Boost 1.53 this compiles on MSVC
65     With Boost 1.56 it does not.
66     With GCC it compiles so far... but.. who knows...
67 */
68 #define NC_SOCKLIST_USE_MEMBER_HOOK  1
69 #define NC_SOCKLIST_USE_BASE_HOOK    2
70 #define NC_SOCKLIST_USE_STD_LIST     3
71 #define NC_SOCKLIST_USE_TYPE         NC_SOCKLIST_USE_BASE_HOOK
72 
73 
74 #if NC_SOCKLIST_USE_TYPE == NC_SOCKLIST_USE_MEMBER_HOOK
75 struct SSrvSockList_tag;
76 typedef intr::list_member_hook<intr::tag<SSrvSockList_tag> >    TSrvSockListHook;
77 #elif NC_SOCKLIST_USE_TYPE == NC_SOCKLIST_USE_BASE_HOOK
78 struct SSrvSockList_tag;
79 typedef intr::list_base_hook<intr::tag<SSrvSockList_tag> >    TSrvSockListHook;
80 #endif
81 
82 
83 
84 
85 /// Task controlling a socket.
86 /// This task always becomes runnable in 3 cases:
87 /// - when socket changes from non-readable to readable (some data came into
88 ///   socket and can be read by user);
89 /// - when socket changes from non-writable to writable (socket has been just
90 ///   opened, connection established and data can be written to the other side);
91 /// - when some error appears in socket, e.g. client disconnected.
92 /// When task became runnable and ExecuteSlice() is called it's user's code
93 /// responsibility to process event completely (not necessarily immediately
94 /// but eventually). I.e. TaskServer won't set the task runnable again if user
95 /// didn't read all the data from socket.
96 /// CSrvSocketTask cannot attach to any random socket. It should be either
97 /// returned from CSrvSocketFactory and TaskServer will attach it to just
98 /// connected socket, or CSrvSocketTask::Connect() method should be called
99 /// to create socket with other server.
100 #if NC_SOCKLIST_USE_TYPE == NC_SOCKLIST_USE_BASE_HOOK
101 class CSrvSocketTask : public virtual CSrvTask,  public SSrvSocketInfo,
102                        public TSrvSockListHook
103 #else
104 class CSrvSocketTask : public virtual CSrvTask, public SSrvSocketInfo
105 #endif
106 {
107 public:
108     CSrvSocketTask(void);
109     virtual ~CSrvSocketTask(void);
110 
111     /// Checks if there's some data available for reading in internal buffers.
112     /// Method doesn't say anything about data available in socket but not yet
113     /// retrieved from kernel.
114     bool IsReadDataAvailable(void);
115     /// Checks if there's some data pending in write buffers and waiting to be
116     /// sent to kernel.
117     bool IsWriteDataPending(void);
118     /// Checks if socket has some error in it.
119     bool HasError(void);
120     /// Checks if socket can ever have more data to read even though it may not
121     /// have it at the moment. Socket won't have more data to read if EOF
122     /// received.
123     bool CanHaveMoreRead(void);
124     /// Checks if socket should be closed because of long inactivity or
125     /// because server is in "hard" shutdown phase. User code should always
126     /// check result of this method at some pivot points where it has the
127     /// ability to close.
128     bool NeedToClose(void);
129     /// Checks if socket should be closed because of internal reasons (long
130     /// inactivity or "hard" shutdown as in NeedToClose()) or because of
131     /// external reasons, like error or EOF in the socket.
132     bool NeedEarlyClose(void);
133 
134     /// Read from socket into internal buffer.
135     /// Method tries to completely fill the buffer even if there's some data
136     /// already waiting in it.
137     bool ReadToBuf(void);
138     /// Read from socket into memory.
139     /// Method combines reading from internal buffer and directly from socket
140     /// (without changing data order) and reads as much as possible to do
141     /// without blocking. Method returns total size of read data (can be 0 if
142     /// no data immediately available).
143     size_t Read(void* buf, size_t size);
144     /// Read from socket one line which ends with '\n', '\r\n' or '\0'.
145     /// Line read should fit into internal read buffer, i.e. it shouldn't be
146     /// too long, otherwise method returns FALSE and raises error flag which
147     /// means you won't be able to anything else with socket (besides closing).
148     /// Method returns TRUE if line was successfully read and FALSE if there's
149     /// no data or no line end markers in it. When method returned TRUE
150     /// variable line will point to internal read buffer. Thus it should be
151     /// used before calling any other Read*() methods -- after that memory can
152     /// become invalid.
153     bool ReadLine(CTempString* line);
154     /// Read from socket exactly the given data size. If given size is not
155     /// immediately available (without blocking) method doesn't read anything
156     /// and returns FALSE. Otherwise data is copied to buf and TRUE is
157     /// returned. Requested size must be less than internal read buffer.
158     bool ReadData(void* buf, Uint2 size);
159     /// Read from socket a number in native machine representation.
160     /// If sizeof(NumType) bytes is not immediately available in the socket
161     /// method returns FALSE and doesn't read anything. Otherwise it copies
162     /// data into num and returns TRUE.
163     template <typename NumType>
164     bool ReadNumber(NumType* num);
165 
166     /// Write text into socket.
167     /// The whole text message will be written, internal write buffer will be
168     /// expanded to accommodate the message if necessary.
169     CSrvSocketTask& WriteText(CTempString message);
170     /// Write number into socket as string, i.e. number is converted to string
171     /// using NStr::NumericToString() first and then the result is written to
172     /// socket as string. The whole number will be written, internal write
173     /// buffer will be expanded to accommodate it if necessary.
174     template <typename NumType>
175     CSrvSocketTask& WriteNumber(NumType num);
176     CSrvSocketTask& WriteBool(bool b);
177     /// Write the exact amount of data into the socket. All the data will be
178     /// written, internal write buffer will be expanded to accommodate it
179     /// if necessary.
180     void WriteData(const void* buf, size_t size);
181     /// Write into the socket as much as immediately possible (including
182     /// writing into internal write buffers and writing directly into socket)
183     /// without blocking and without expanding write buffer. Method returns
184     /// amount of data written which can be 0 if socket is not writable at the
185     /// moment.
186     size_t Write(const void* buf, size_t size);
187     /// Flush all data saved in internal write buffers to socket.
188     /// Method must be called from inside of ExecuteSlice() of this task and
189     /// no other writing methods should be called until FlushIsDone() returns
190     /// TRUE.
191     void Flush(void);
192     /// Request flushing of all data saved in internal write buffers to socket.
193     /// Method can be called from anywhere even concurrently with
194     /// ExecuteSlice() of this task. No other writing methods should be called
195     /// until FlushIsDone() returns TRUE.
196     void RequestFlush(void);
197     /// Check if data flushing requested earlier is complete.
198     bool FlushIsDone(void);
199 
200     /// Start proxying of raw data from this socket to the one in dst_task.
201     /// Method must be called from inside of ExecuteSlice() of this task. And
202     /// nothing else should be done with both sockets until IsProxyInProgress()
203     /// returns FALSE. Only proxy_size bytes is transferred between sockets.
204     /// Proxying can stop earlier only if NeedEarlyClose() on either socket
205     /// returns true. In this case ProxyHadError() will return true and
206     /// nothing else could be done with sockets except closing.
207     void StartProxyTo(CSrvSocketTask* dst_task, Uint8 proxy_size);
208     /// Check whether proxying started earlier is still in progress.
209     bool IsProxyInProgress(void);
210     /// Check whether proxying started earlier finished successfully or any of
211     /// sockets had some error in it.
212     bool ProxyHadError(void);
213 
214     /// Create new socket and connect it to given IP and port.
215     /// If operation is successful TRUE is returned, FALSE returned otherwise.
216     /// FALSE will be returned only in case of errors like network interface
217     /// is down or things like that. If server is inaccessible then Connect()
218     /// will return TRUE but later there will be error in the socket.
219     bool Connect(Uint4 host, Uint2 port);
220     /// Start processing of the socket and include it into TaskServer's
221     /// central epoll. Until this method is called TaskServer won't make this
222     /// task runnable and won't check if it's readable or writable -- any
223     /// Read/Write operation will be unsuccessful (processing 0 bytes).
224     bool StartProcessing(TSrvThreadNum thread_num = 0, bool boost = false);
225     /// Close the socket gracefully, i.e. if any socket's data is still in
226     /// kernel's buffers and didn't make it to wire it will be sent later. But
227     /// internal task's write buffers won't be flushed.
228     void CloseSocket(void);
229     /// Abort the socket, i.e. anything unsent in kernel buffers will be
230     /// dropped.
231     void AbortSocket(void);
232 
233     /// Get peer IP and port for this socket.
234     void GetPeerAddress(string& host, Uint2& port);
235     /// Get local port this socket was created on.
236     Uint2 GetLocalPort(void);
237 
238     /// Terminate the task. Method is overridden here to add some extra
239     /// processing for sockets.
240     virtual void Terminate(void);
241 
242 private:
243     CSrvSocketTask(const CSrvSocketTask&);
244     CSrvSocketTask& operator= (const CSrvSocketTask&);
245 
246     /// Internal function to execute time slice work. Overridden here to add
247     /// some extra socket processing before and after call to user's code in
248     /// ExecuteSlice().
249     virtual void InternalRunSlice(TSrvThreadNum thr_num);
250 
251     /// Close or abort the socket -- they have little difference, thus they
252     /// joined in one method.
253     void x_CloseSocket(bool do_abort);
254     /// Prints socket's error if there's any error pending on the socket.
255     void x_PrintError(void);
256 
257 // Consider this section private as it's public for internal use only
258 // to minimize implementation-specific clutter in headers.
259 public:
260     /// Source task for proxying.
261     /// This variable is set to non-NULL value only in destination tasks for
262     /// proxying.
263     CSrvSocketTask* m_ProxySrc;
264     /// Destination task for proxying.
265     /// This variable is set to non-NULL value only in source tasks for
266     /// proxying.
267     CSrvSocketTask* m_ProxyDst;
268     /// Amount left to proxy if proxying operation is in progress.
269     /// Value is set only in source tasks for proxying.
270     Uint8 m_ProxySize;
271 #if NC_SOCKLIST_USE_TYPE == NC_SOCKLIST_USE_MEMBER_HOOK
272     /// Hook to include the task to lists of open sockets.
273     TSrvSockListHook m_SockListHook;
274 #endif
275     /// Read buffer.
276     char* m_RdBuf;
277     /// Write buffer.
278     char* m_WrBuf;
279     /// Jiffy number when Connect() method was called. Variable is used to
280     /// track connection timeouts.
281     Uint8 m_ConnStartJfy;
282     /// File descriptor for the socket.
283     int m_Fd;
284     /// Size of data available for reading in the read buffer.
285     Uint2 m_RdSize;
286     /// Position of current reading in the read buffer, i.e. all data in
287     /// [0, m_RdPos) was already read; data in [m_RdPos, m_RdSize) are queued
288     /// for reading.
289     Uint2 m_RdPos;
290     /// Size of memory allocated for write buffer. This size can grow in
291     /// methods requiring exact writing like WriteData(), WriteText() etc.
292     Uint2 m_WrMemSize;
293     /// Size of data in the write buffer waiting for writing.
294     Uint2 m_WrSize;
295     /// Position of current writing pointer in the write buffer.
296     Uint2 m_WrPos;
297     /// Flag showing if '\r' symbol was seen at the end of last line but '\n'
298     /// wasn't seen yet.
299     bool m_CRMet;
300     /// Flag showing that last proxying operation finished with error.
301     bool m_ProxyHadError;
302     /// Flag showing that socket is readable.
303     bool m_SockHasRead;
304     /// Flag showing that socket is writable.
305     bool m_SockCanWrite;
306     /// Flag showing that socket can have more reads, i.e. there was no EOF yet.
307     bool m_SockCanReadMore;
308     /// Flag showing that socket needs to be closed because of long
309     /// inactivity.
310     bool m_NeedToClose;
311     /// Flag showing that task needs to flush all write buffers.
312     bool m_NeedToFlush;
313     /// Flag showing that write buffers were flushed.
314     bool m_FlushIsDone;
315     /// Number of last read event seen by Read() when it read from socket. If
316     /// this number is equal to m_RegReadEvts then no new "readable" events
317     /// came from epoll and thus result of last read defines if there is more
318     /// data in the socket to read (if last read returned less data than
319     /// provided buffer size then there's no more data in the socket).
320     Uint1 m_SeenReadEvts;
321     /// Number of last write event seen by Write() when it wrote to socket. If
322     /// this number is equal to m_RegWriteEvts then no new "writable" events
323     /// came from epoll and thus result of last write defines if more data
324     /// can be written in the socket (if last write have written less data than
325     /// provided buffer size then nothing else can be written in the socket).
326     Uint1 m_SeenWriteEvts;
327     /// Total number of bytes read from socket.
328     Uint8 m_ReadBytes;
329     /// Total number of bytes written to socket.
330     Uint8 m_WrittenBytes;
331     /// Counter of "readable" events received from epoll.
332     Uint1 m_RegReadEvts;
333     /// Counter of "writable" events received from epoll.
334     Uint1 m_RegWriteEvts;
335     /// Flag showing if epoll returned RDHUP on this socket.
336     bool m_RegReadHup;
337     /// Flag showing if there's error pending on the socket.
338     bool m_RegError;
339     /// Flag showing if pending error in socket was printed in logs.
340     bool m_ErrorPrinted;
341     /// Remembered peer port. Value valid only for sockets created from
342     /// listening sockets.
343     Uint2 m_PeerPort;
344     /// Remembered peer IP address. Value valid only for sockets created from
345     /// listening sockets.
346     Uint4 m_PeerAddr;
347     ///
348     string m_ConnReqId;
349 };
350 
351 
352 /// Factory that creates CSrvSocketTask-derived object for each connection
353 /// coming to listening port which this factory is assigned to.
354 class CSrvSocketFactory
355 {
356 public:
357     CSrvSocketFactory(void);
358     virtual ~CSrvSocketFactory(void);
359 
360     virtual CSrvSocketTask* CreateSocketTask(void) = 0;
361 };
362 
363 
364 END_NCBI_SCOPE
365 
366 #endif /* NETCACHE__SRV_SOCKETS__HPP */
367