bf4aa4ea4ef58e00d3177fdf295783d29709cc57
[citadel.git] / citadel / event_client.h
1 /*
2  *
3  * Copyright (c) 1998-2012 by the citadel.org team
4  *
5  *  This program is open source software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License, version 3.
7  *
8  *  This program is distributed in the hope that it will be useful,
9  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
10  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  *  GNU General Public License for more details.
12  */
13
14 #ifndef __EVENT_CLIENT_H__
15 #define __EVENT_CLIENT_H__
16 #define EV_COMPAT3 0
17 #include "sysconfig.h"
18 #include <ev.h>
19 #include <sys/types.h>
20 #include <sys/socket.h>
21 #include <netdb.h>
22 #include <arpa/nameser.h>
23 #include <ares.h>
24 #include <curl/curl.h>
25
26 #ifndef __ASYNCIO__
27 #define __ASYNCIO__
28 typedef struct AsyncIO AsyncIO;
29 #endif
30 #ifndef __CIT_CONTEXT__
31 #define __CIT_CONTEXT__
32 typedef struct CitContext CitContext;
33 #endif
34
35 typedef enum _eNextState {
36         eSendDNSQuery,
37         eReadDNSReply,
38
39         eDBQuery,
40
41         eConnect,
42         eSendReply,
43         eSendMore,
44         eSendFile,
45
46         eReadMessage,
47         eReadMore,
48         eReadPayload,
49         eReadFile,
50
51         eTerminateConnection,
52         eAbort
53 }eNextState;
54
55 typedef eNextState (*IO_CallBack)(AsyncIO *IO);
56 typedef eReadState (*IO_LineReaderCallback)(AsyncIO *IO);
57 typedef void (*ParseDNSAnswerCb)(AsyncIO*, unsigned char*, int);
58 typedef void (*FreeDNSReply)(void *DNSData);
59
60
61 typedef struct __ReadAsyncMsg {
62         StrBuf *MsgBuf;
63         size_t maxlen;          /* maximum message length */
64
65         const char *terminator; /* token signalling EOT */
66         long tlen;
67         int dodot;
68
69         int flushing;
70 /* if we read maxlen, read until nothing more arives and ignore this. */
71
72         int crlf;               /* CRLF newlines instead of LF */
73 } ReadAsyncMsg;
74
75
76 typedef struct _DNSQueryParts {
77         ParseDNSAnswerCb DNS_CB;
78         IO_CallBack PostDNS;
79
80         const char *QueryTYPE;
81         const char *QStr;
82         int DNSStatus;
83         void *VParsedDNSReply;
84         FreeDNSReply DNSReplyFree;
85         void *Data;
86 } DNSQueryParts;
87
88 typedef struct _evcurl_request_data
89 {
90         CURL                    *chnd;
91         struct curl_slist       *headers;
92         char                     errdesc[CURL_ERROR_SIZE];
93
94         int                      attached;
95
96         char                    *PlainPostData;
97         long                     PlainPostDataLen;
98         StrBuf                  *PostData;
99
100         StrBuf                  *ReplyData;
101         long                     httpcode;
102 } evcurl_request_data;
103
104 /* DNS Related */
105 typedef struct __evcares_data {
106         ev_tstamp Start;
107         ev_io recv_event,
108                 send_event;
109         ev_timer timeout;           /* timeout while requesting ips */
110         short int SourcePort;
111
112         struct ares_options Options;
113         ares_channel Channel;
114         DNSQueryParts *Query;
115
116         IO_CallBack Fail;      /* the dns lookup didn't work out. */
117 } evcares_data;
118
119 struct AsyncIO {
120         long ID;
121         ev_tstamp Now;
122         ev_tstamp StartIO;
123         ev_tstamp StartDB;
124         eNextState NextState;
125
126         /* connection related */
127         ParsedURL *ConnectMe;
128
129         /* read/send related... */
130         StrBuf *IOBuf;
131         IOBuffer SendBuf,
132                 RecvBuf;
133
134         FDIOBuffer IOB;
135         /* when sending from / reading into files, this is used. */
136
137         /* our events... */
138         ev_cleanup abort_by_shutdown, /* server wants to go down... */
139                 db_abort_by_shutdown; /* server wants to go down... */
140         ev_timer conn_fail,           /* connection establishing timed out */
141                 rw_timeout;           /* timeout while sending data */
142         ev_idle unwind_stack,         /* get c-ares out of the stack */
143                 db_unwind_stack,      /* wait for next db operation... */
144                 conn_fail_immediate;  /* unwind stack, but fail immediately. */
145         ev_io recv_event,             /* receive data from the client */
146                 send_event,           /* send more data to the client */
147                 conn_event;           /* Connection successfully established */
148
149         StrBuf *ErrMsg; /* if we fail to connect, or lookup, error goes here. */
150
151         /* Citadel application callbacks... */
152         IO_CallBack ReadDone, /* Theres new data to read... */
153                 SendDone,     /* we may send more data */
154                 Terminate,    /* shutting down... */
155                 DBTerminate,  /* shutting down... */
156                 Timeout,      /* Timeout handler;may also be conn. timeout */
157                 ConnFail,     /* What to do when one connection failed? */
158                 ShutdownAbort,/* we're going down. make your piece. */
159                 NextDBOperation; /* Perform Database IO */
160
161         /* if we have linereaders, maybe we want to read more lines before
162          * the real application logic is called? */
163         IO_LineReaderCallback LineReader;
164
165         evcares_data DNS;
166
167         evcurl_request_data HttpReq;
168
169         /* Saving / loading a message async from / to disk */
170         ReadAsyncMsg *ReadMsg;
171         struct CtdlMessage *AsyncMsg;
172         struct recptypes *AsyncRcp;
173
174         /* Context specific data; Hint: put AsyncIO in there */
175         void *Data;        /* application specific data */
176         CitContext *CitContext;  /* Citadel Session context... */
177 };
178
179 typedef struct _IOAddHandler {
180         AsyncIO *IO;
181         IO_CallBack EvAttch;
182 } IOAddHandler;
183
184
185
186 extern int DebugEventLoop;
187 extern int DebugCAres;
188
189 #define EDBGLOG(LEVEL) if ((LEVEL != LOG_DEBUG) || (DebugEventLoop != 0))
190
191 #define CCID ((CitContext*)IO->CitContext)->cs_pid
192
193 #define EVQ_syslog(LEVEL, FORMAT, ...)                                  \
194         EDBGLOG (LEVEL) syslog(LEVEL, "IOQ " FORMAT, __VA_ARGS__)
195
196 #define EVQM_syslog(LEVEL, FORMAT)                      \
197         EDBGLOG (LEVEL) syslog(LEVEL, "IO " FORMAT)
198
199 #define EV_syslog(LEVEL, FORMAT, ...)                                   \
200         EDBGLOG (LEVEL) syslog(LEVEL, "IO[%ld]CC[%d] " FORMAT, IO->ID, CCID, __VA_ARGS__)
201
202 #define EVM_syslog(LEVEL, FORMAT)                                       \
203         EDBGLOG (LEVEL) syslog(LEVEL, "IO[%ld]CC[%d] " FORMAT, IO->ID, CCID)
204
205 #define EVNC_syslog(LEVEL, FORMAT, ...)                                 \
206         EDBGLOG (LEVEL) syslog(LEVEL, "IO[%ld] " FORMAT, IO->ID, __VA_ARGS__)
207
208 #define EVNCM_syslog(LEVEL, FORMAT) EDBGLOG (LEVEL) syslog(LEVEL, "IO[%ld]" FORMAT, IO->ID)
209
210
211 #define CDBGLOG() if (DebugCAres != 0)
212 #define CEDBGLOG(LEVEL) if ((LEVEL != LOG_DEBUG) || (DebugCAres != 0))
213 #define EV_DNS_LOG_START(a)                                                     \
214         CDBGLOG () {syslog(LOG_DEBUG, "IO[%ld]CC[%d] + Starting " #a " %s %p FD %d", IO->ID, CCID, __FUNCTION__, &IO->a, IO->a.fd); \
215                     EV_backtrace(IO);}
216
217 #define EV_DNS_LOG_STOP(a)                                                      \
218         CDBGLOG () { syslog(LOG_DEBUG, "IO[%ld]CC[%d] - Stopping " #a " %s %p FD %d", IO->ID, CCID, __FUNCTION__, &IO->a, IO->a.fd); \
219                      EV_backtrace(IO);}
220
221 #define EV_DNS_LOG_INIT(a)                                                      \
222         CDBGLOG () { syslog(LOG_DEBUG, "IO[%ld]CC[%d] * Init " #a " %s %p FD %d", IO->ID, CCID, __FUNCTION__, &IO->a, IO->a.fd); \
223                      EV_backtrace(IO);}
224
225 #define EV_DNS_LOGT_START(a)                                                    \
226         CDBGLOG () { syslog(LOG_DEBUG, "IO[%ld]CC[%d] + Starting " #a " %s %p", IO->ID, CCID, __FUNCTION__, &IO->a); \
227                      EV_backtrace(IO);}
228
229 #define EV_DNS_LOGT_STOP(a)                                                     \
230         CDBGLOG () { syslog(LOG_DEBUG, "IO[%ld]CC[%d] - Stopping " #a " %s %p", IO->ID, CCID, __FUNCTION__, &IO->a); \
231                      EV_backtrace(IO); }
232
233 #define EV_DNS_LOGT_INIT(a)                                                     \
234         CDBGLOG () { syslog(LOG_DEBUG, "IO[%ld]CC[%d] * Init " #a " %p", IO->ID, CCID, &IO->a); \
235                      EV_backtrace(IO);}
236
237 #define EV_DNS_syslog(LEVEL, FORMAT, ...)                               \
238         CEDBGLOG (LEVEL) syslog(LEVEL, "IO[%ld]CC[%d] " FORMAT, IO->ID, CCID, __VA_ARGS__)
239
240 #define EVM_DNS_syslog(LEVEL, FORMAT)                                   \
241         CEDBGLOG (LEVEL) syslog(LEVEL, "IO[%ld]CC[%d] " FORMAT, IO->ID, CCID)
242
243 void FreeAsyncIOContents(AsyncIO *IO);
244
245 eNextState NextDBOperation(AsyncIO *IO, IO_CallBack CB);
246 eNextState QueueDBOperation(AsyncIO *IO, IO_CallBack CB);
247 void StopDBWatchers(AsyncIO *IO);
248 eNextState QueueEventContext(AsyncIO *IO, IO_CallBack CB);
249 eNextState QueueCurlContext(AsyncIO *IO);
250
251 eNextState EvConnectSock(AsyncIO *IO,
252                          double conn_timeout,
253                          double first_rw_timeout,
254                          int ReadFirst);
255 void IO_postdns_callback(struct ev_loop *loop, ev_idle *watcher, int revents);
256
257 int QueueQuery(ns_type Type,
258                const char *name,
259                AsyncIO *IO,
260                DNSQueryParts *QueryParts,
261                IO_CallBack PostDNS);
262
263 void QueueGetHostByName(AsyncIO *IO,
264                         const char *Hostname,
265                         DNSQueryParts *QueryParts,
266                         IO_CallBack PostDNS);
267
268 void QueryCbDone(AsyncIO *IO);
269
270 void StopClient(AsyncIO *IO);
271
272 void StopClientWatchers(AsyncIO *IO, int CloseFD);
273
274 void SetNextTimeout(AsyncIO *IO, double timeout);
275
276 #include <curl/curl.h>
277
278 #define OPT(s, v) \
279         do { \
280                 sta = curl_easy_setopt(chnd, (CURLOPT_##s), (v));       \
281                 if (sta)  {                                             \
282                         EVQ_syslog(LOG_ERR,                             \
283                                "error setting option " #s               \
284                                " on curl handle: %s",                   \
285                                curl_easy_strerror(sta));                \
286         } } while (0)
287
288 void InitIOStruct(AsyncIO *IO,
289                   void *Data,
290                   eNextState NextState,
291                   IO_LineReaderCallback LineReader,
292                   IO_CallBack DNS_Fail,
293                   IO_CallBack SendDone,
294                   IO_CallBack ReadDone,
295                   IO_CallBack Terminate,
296                   IO_CallBack DBTerminate,
297                   IO_CallBack ConnFail,
298                   IO_CallBack Timeout,
299                   IO_CallBack ShutdownAbort);
300
301 int InitcURLIOStruct(AsyncIO *IO,
302                      void *Data,
303                      const char* Desc,
304                      IO_CallBack SendDone,
305                      IO_CallBack Terminate,
306                      IO_CallBack DBTerminate,
307                      IO_CallBack ShutdownAbort);
308 void KillAsyncIOContext(AsyncIO *IO);
309 void StopCurlWatchers(AsyncIO *IO);
310
311
312 eNextState ReAttachIO(AsyncIO *IO,
313                       void *pData,
314                       int ReadFirst);
315
316 void EV_backtrace(AsyncIO *IO);
317 ev_tstamp ctdl_ev_now (void);
318
319 #endif /* __EVENT_CLIENT_H__ */