cc3c1db950da41d3f01833a11cff7280df4e5420
[oweals/gnunet.git] / src / curl / curl_reschedule.c
1 /*
2   This file is part of GNUnet
3   Copyright (C) 2015, 2016 GNUnet e.V.
4
5   GNUnet is free software: you can redistribute it and/or modify it
6   under the terms of the GNU Affero General Public License as published
7   by the Free Software Foundation, either version 3 of the License,
8   or (at your option) any later version.
9
10   GNUnet is distributed in the hope that it will be useful, but
11   WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   Affero General Public License for more details.
14  
15   You should have received a copy of the GNU Affero General Public License
16   along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 /**
19  * @file curl/curl_reschedule.c
20  * @brief API for event loop integration with GNUnet SCHEDULER.
21  * @author Christian Grothoff
22  */
23 #include "platform.h"
24 #include "gnunet_curl_lib.h"
25 #include "gnunet_util_lib.h"
26
27 extern void *
28 download_get_result (struct GNUNET_CURL_DownloadBuffer *db,
29                      CURL *eh,
30                      long *response_code);
31
32 /**
33  * Closure for #GNUNET_CURL_gnunet_scheduler_reschedule().
34  */
35 struct GNUNET_CURL_RescheduleContext
36 {
37   /**
38    * Just the task.
39    */
40   struct GNUNET_SCHEDULER_Task *task;
41
42   /**
43    * Context we manage.
44    */
45   struct GNUNET_CURL_Context *ctx;
46
47   /**
48    * Parser of the raw response.
49    */
50   GNUNET_CURL_RawParser parser;
51
52   /**
53    * Deallocate the response object.
54    */
55   GNUNET_CURL_ResponseCleaner cleaner;
56 };
57
58
59 /**
60  * Initialize reschedule context; with custom response parser
61  *
62  * @param ctx context to manage
63  * @return closure for #GNUNET_CURL_gnunet_scheduler_reschedule().
64  */
65 struct GNUNET_CURL_RescheduleContext *
66 GNUNET_CURL_gnunet_rc_create_with_parser (struct GNUNET_CURL_Context *ctx,
67                                           GNUNET_CURL_RawParser rp,
68                                           GNUNET_CURL_ResponseCleaner rc)
69 {
70   struct GNUNET_CURL_RescheduleContext *rctx;
71
72   rctx = GNUNET_new (struct GNUNET_CURL_RescheduleContext);
73   rctx->ctx = ctx;
74   rctx->parser = rp;
75   rctx->cleaner = rc;
76
77   return rctx;
78 }
79
80
81 /**
82  * Just a wrapper to avoid casting of function pointers.
83  *
84  * @param response the (JSON) response to clean.
85  */
86 static void
87 clean_result (void *response)
88 {
89   json_decref (response);
90 }
91
92 /**
93  * Initialize reschedule context.
94  *
95  * @param ctx context to manage
96  * @return closure for #GNUNET_CURL_gnunet_scheduler_reschedule().
97  */
98 struct GNUNET_CURL_RescheduleContext *
99 GNUNET_CURL_gnunet_rc_create (struct GNUNET_CURL_Context *ctx)
100 {
101   struct GNUNET_CURL_RescheduleContext *rc;
102
103   rc = GNUNET_new (struct GNUNET_CURL_RescheduleContext);
104   rc->ctx = ctx;
105   rc->parser = &download_get_result;
106   rc->cleaner = &clean_result;
107   return rc;
108 }
109
110
111 /**
112  * Destroy reschedule context.
113  *
114  * @param rc context to destroy
115  */
116 void
117 GNUNET_CURL_gnunet_rc_destroy (struct GNUNET_CURL_RescheduleContext *rc)
118 {
119   if (NULL != rc->task)
120     GNUNET_SCHEDULER_cancel (rc->task);
121   GNUNET_free (rc);
122 }
123
124
125 /**
126  * Task that runs the context's event loop with the GNUnet scheduler.
127  *
128  * @param cls a `struct GNUNET_CURL_RescheduleContext *`
129  */
130 static void
131 context_task (void *cls)
132 {
133   struct GNUNET_CURL_RescheduleContext *rc = cls;
134   long timeout;
135   int max_fd;
136   fd_set read_fd_set;
137   fd_set write_fd_set;
138   fd_set except_fd_set;
139   struct GNUNET_NETWORK_FDSet *rs;
140   struct GNUNET_NETWORK_FDSet *ws;
141   struct GNUNET_TIME_Relative delay;
142
143   rc->task = NULL;
144
145   GNUNET_CURL_perform2 (rc->ctx,
146                         rc->parser,
147                         rc->cleaner);
148   max_fd = -1;
149   timeout = -1;
150   FD_ZERO (&read_fd_set);
151   FD_ZERO (&write_fd_set);
152   FD_ZERO (&except_fd_set);
153   GNUNET_CURL_get_select_info (rc->ctx,
154                                &read_fd_set,
155                                &write_fd_set,
156                                &except_fd_set,
157                                &max_fd,
158                                &timeout);
159   if (timeout >= 0)
160     delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
161                                            timeout);
162   else
163     delay = GNUNET_TIME_UNIT_FOREVER_REL;
164   rs = GNUNET_NETWORK_fdset_create ();
165   GNUNET_NETWORK_fdset_copy_native (rs,
166                                     &read_fd_set,
167                                     max_fd + 1);
168   ws = GNUNET_NETWORK_fdset_create ();
169   GNUNET_NETWORK_fdset_copy_native (ws,
170                                     &write_fd_set,
171                                     max_fd + 1);
172   if (NULL == rc->task)
173     rc->task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
174                                             delay,
175                                             rs,
176                                             ws,
177                                             &context_task,
178                                             rc);
179   GNUNET_NETWORK_fdset_destroy (rs);
180   GNUNET_NETWORK_fdset_destroy (ws);
181 }
182
183
184 /**
185  * Implementation of the #GNUNET_CURL_RescheduleCallback for GNUnet's
186  * scheduler.  Will run the CURL context using GNUnet's scheduler.
187  * Note that you MUST immediately destroy the reschedule context after
188  * calling #GNUNET_CURL_fini().
189  *
190  * @param cls must point to a `struct GNUNET_CURL_RescheduleContext *`
191  *           (pointer to a pointer!)
192  */
193 void
194 GNUNET_CURL_gnunet_scheduler_reschedule (void *cls)
195 {
196   struct GNUNET_CURL_RescheduleContext *rc = *(void**) cls;
197
198   if (NULL != rc->task)
199     GNUNET_SCHEDULER_cancel (rc->task);
200   rc->task = GNUNET_SCHEDULER_add_now (&context_task,
201                                        rc);
202 }
203
204 /* end of curl_reschedule.c */