tolerate additional IPv4 address now available for gnunet.org
[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      SPDX-License-Identifier: AGPL3.0-or-later
19 */
20 /**
21  * @file curl/curl_reschedule.c
22  * @brief API for event loop integration with GNUnet SCHEDULER.
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "gnunet_curl_lib.h"
27 #include "gnunet_util_lib.h"
28
29 extern void *
30 download_get_result (struct GNUNET_CURL_DownloadBuffer *db,
31                      CURL *eh,
32                      long *response_code);
33
34 /**
35  * Closure for #GNUNET_CURL_gnunet_scheduler_reschedule().
36  */
37 struct GNUNET_CURL_RescheduleContext
38 {
39   /**
40    * Just the task.
41    */
42   struct GNUNET_SCHEDULER_Task *task;
43
44   /**
45    * Context we manage.
46    */
47   struct GNUNET_CURL_Context *ctx;
48
49   /**
50    * Parser of the raw response.
51    */
52   GNUNET_CURL_RawParser parser;
53
54   /**
55    * Deallocate the response object.
56    */
57   GNUNET_CURL_ResponseCleaner cleaner;
58 };
59
60
61 /**
62  * Initialize reschedule context; with custom response parser
63  *
64  * @param ctx context to manage
65  * @return closure for #GNUNET_CURL_gnunet_scheduler_reschedule().
66  */
67 struct GNUNET_CURL_RescheduleContext *
68 GNUNET_CURL_gnunet_rc_create_with_parser (struct GNUNET_CURL_Context *ctx,
69                                           GNUNET_CURL_RawParser rp,
70                                           GNUNET_CURL_ResponseCleaner rc)
71 {
72   struct GNUNET_CURL_RescheduleContext *rctx;
73
74   rctx = GNUNET_new (struct GNUNET_CURL_RescheduleContext);
75   rctx->ctx = ctx;
76   rctx->parser = rp;
77   rctx->cleaner = rc;
78
79   return rctx;
80 }
81
82
83 /**
84  * Just a wrapper to avoid casting of function pointers.
85  *
86  * @param response the (JSON) response to clean.
87  */
88 static void
89 clean_result (void *response)
90 {
91   json_decref (response);
92 }
93
94 /**
95  * Initialize reschedule context.
96  *
97  * @param ctx context to manage
98  * @return closure for #GNUNET_CURL_gnunet_scheduler_reschedule().
99  */
100 struct GNUNET_CURL_RescheduleContext *
101 GNUNET_CURL_gnunet_rc_create (struct GNUNET_CURL_Context *ctx)
102 {
103   struct GNUNET_CURL_RescheduleContext *rc;
104
105   rc = GNUNET_new (struct GNUNET_CURL_RescheduleContext);
106   rc->ctx = ctx;
107   rc->parser = &download_get_result;
108   rc->cleaner = &clean_result;
109   return rc;
110 }
111
112
113 /**
114  * Destroy reschedule context.
115  *
116  * @param rc context to destroy
117  */
118 void
119 GNUNET_CURL_gnunet_rc_destroy (struct GNUNET_CURL_RescheduleContext *rc)
120 {
121   if (NULL != rc->task)
122     GNUNET_SCHEDULER_cancel (rc->task);
123   GNUNET_free (rc);
124 }
125
126
127 /**
128  * Task that runs the context's event loop with the GNUnet scheduler.
129  *
130  * @param cls a `struct GNUNET_CURL_RescheduleContext *`
131  */
132 static void
133 context_task (void *cls)
134 {
135   struct GNUNET_CURL_RescheduleContext *rc = cls;
136   long timeout;
137   int max_fd;
138   fd_set read_fd_set;
139   fd_set write_fd_set;
140   fd_set except_fd_set;
141   struct GNUNET_NETWORK_FDSet *rs;
142   struct GNUNET_NETWORK_FDSet *ws;
143   struct GNUNET_TIME_Relative delay;
144
145   rc->task = NULL;
146
147   GNUNET_CURL_perform2 (rc->ctx,
148                         rc->parser,
149                         rc->cleaner);
150   max_fd = -1;
151   timeout = -1;
152   FD_ZERO (&read_fd_set);
153   FD_ZERO (&write_fd_set);
154   FD_ZERO (&except_fd_set);
155   GNUNET_CURL_get_select_info (rc->ctx,
156                                &read_fd_set,
157                                &write_fd_set,
158                                &except_fd_set,
159                                &max_fd,
160                                &timeout);
161   if (timeout >= 0)
162     delay = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
163                                            timeout);
164   else
165     delay = GNUNET_TIME_UNIT_FOREVER_REL;
166   rs = GNUNET_NETWORK_fdset_create ();
167   GNUNET_NETWORK_fdset_copy_native (rs,
168                                     &read_fd_set,
169                                     max_fd + 1);
170   ws = GNUNET_NETWORK_fdset_create ();
171   GNUNET_NETWORK_fdset_copy_native (ws,
172                                     &write_fd_set,
173                                     max_fd + 1);
174   if (NULL == rc->task)
175     rc->task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
176                                             delay,
177                                             rs,
178                                             ws,
179                                             &context_task,
180                                             rc);
181   GNUNET_NETWORK_fdset_destroy (rs);
182   GNUNET_NETWORK_fdset_destroy (ws);
183 }
184
185
186 /**
187  * Implementation of the #GNUNET_CURL_RescheduleCallback for GNUnet's
188  * scheduler.  Will run the CURL context using GNUnet's scheduler.
189  * Note that you MUST immediately destroy the reschedule context after
190  * calling #GNUNET_CURL_fini().
191  *
192  * @param cls must point to a `struct GNUNET_CURL_RescheduleContext *`
193  *           (pointer to a pointer!)
194  */
195 void
196 GNUNET_CURL_gnunet_scheduler_reschedule (void *cls)
197 {
198   struct GNUNET_CURL_RescheduleContext *rc = *(void**) cls;
199
200   if (NULL != rc->task)
201     GNUNET_SCHEDULER_cancel (rc->task);
202   rc->task = GNUNET_SCHEDULER_add_now (&context_task,
203                                        rc);
204 }
205
206 /* end of curl_reschedule.c */