-LRN: don't use Z
[oweals/gnunet.git] / src / transport / plugin_transport_http_client.c
1 /*
2      This file is part of GNUnet
3      (C) 2002--2012 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file transport/plugin_transport_http_client.c
23  * @brief http transport service plugin
24  * @author Matthias Wachs
25  */
26
27 #include "plugin_transport_http.h"
28
29 static struct Plugin * p;
30
31 #if VERBOSE_CURL
32 /**
33  * Function to log curl debug messages with GNUNET_log
34  * @param curl handle
35  * @param type curl_infotype
36  * @param data data
37  * @param size size
38  * @param cls  closure
39  * @return 0
40  */
41 static int
42 client_log (CURL * curl, curl_infotype type, char *data, size_t size, void *cls)
43 {
44   if (type == CURLINFO_TEXT)
45   {
46     char text[size + 2];
47
48     memcpy (text, data, size);
49     if (text[size - 1] == '\n')
50       text[size] = '\0';
51     else
52     {
53       text[size] = '\n';
54       text[size + 1] = '\0';
55     }
56 #if BUILD_HTTPS
57     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-https",
58                      "Client: %p - %s", cls, text);
59 #else
60     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-http",
61                      "Client: %p - %s", cls, text);
62 #endif
63   }
64   return 0;
65 }
66 #endif
67
68 /**
69  * Task performing curl operations
70  * @param cls plugin as closure
71  * @param tc gnunet scheduler task context
72  */
73 static void
74 client_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
75
76
77 /**
78  * Function setting up file descriptors and scheduling task to run
79  *
80  * @param  plugin plugin as closure
81  * @param now schedule task in 1ms, regardless of what curl may say
82  * @return GNUNET_SYSERR for hard failure, GNUNET_OK for ok
83  */
84 static int
85 client_schedule (struct Plugin *plugin, int now)
86 {
87   fd_set rs;
88   fd_set ws;
89   fd_set es;
90   int max;
91   struct GNUNET_NETWORK_FDSet *grs;
92   struct GNUNET_NETWORK_FDSet *gws;
93   long to;
94   CURLMcode mret;
95   struct GNUNET_TIME_Relative timeout;
96
97   /* Cancel previous scheduled task */
98   if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
99   {
100     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
101     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
102   }
103   max = -1;
104   FD_ZERO (&rs);
105   FD_ZERO (&ws);
106   FD_ZERO (&es);
107   mret = curl_multi_fdset (plugin->client_mh, &rs, &ws, &es, &max);
108   if (mret != CURLM_OK)
109   {
110     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
111                 "curl_multi_fdset", __FILE__, __LINE__,
112                 curl_multi_strerror (mret));
113     return GNUNET_SYSERR;
114   }
115   mret = curl_multi_timeout (plugin->client_mh, &to);
116   if (to == -1)
117     timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1);
118   else
119     timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
120   if (now == GNUNET_YES)
121     timeout = GNUNET_TIME_UNIT_MILLISECONDS;
122
123   if (mret != CURLM_OK)
124   {
125     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
126                 "curl_multi_timeout", __FILE__, __LINE__,
127                 curl_multi_strerror (mret));
128     return GNUNET_SYSERR;
129   }
130
131   grs = GNUNET_NETWORK_fdset_create ();
132   gws = GNUNET_NETWORK_fdset_create ();
133   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
134   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
135
136   plugin->client_perform_task =
137       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
138                                    timeout, grs, gws,
139                                    &client_run, plugin);
140   GNUNET_NETWORK_fdset_destroy (gws);
141   GNUNET_NETWORK_fdset_destroy (grs);
142   return GNUNET_OK;
143 }
144
145
146 int
147 client_send (struct Session *s, struct HTTP_Message *msg)
148 {
149   GNUNET_assert (s != NULL);
150   GNUNET_CONTAINER_DLL_insert_tail (s->msg_head, s->msg_tail, msg);
151
152   if (GNUNET_YES != exist_session(p, s))
153   {
154     GNUNET_break (0);
155     return GNUNET_SYSERR;
156   }
157   if (s->client_put_paused == GNUNET_YES)
158   {
159     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
160                      "Client: %p was suspended, unpausing\n", s->client_put);
161     s->client_put_paused = GNUNET_NO;
162     curl_easy_pause (s->client_put, CURLPAUSE_CONT);
163   }
164   client_schedule (s->plugin, GNUNET_YES);
165
166   return GNUNET_OK;
167 }
168
169
170 /**
171  * Task performing curl operations
172  *
173  * @param cls plugin as closure
174  * @param tc gnunet scheduler task context
175  */
176 static void
177 client_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
178 {
179   struct Plugin *plugin = cls;
180   int running;
181   CURLMcode mret;
182
183   GNUNET_assert (cls != NULL);
184
185   plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
186   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
187     return;
188
189   do
190   {
191     running = 0;
192     mret = curl_multi_perform (plugin->client_mh, &running);
193
194     CURLMsg *msg;
195     int msgs_left;
196
197     while ((msg = curl_multi_info_read (plugin->client_mh, &msgs_left)))
198     {
199       CURL *easy_h = msg->easy_handle;
200       struct Session *s = NULL;
201       char *d = (char *) s;
202
203
204       //GNUNET_assert (easy_h != NULL);
205       if (easy_h == NULL)
206       {
207         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
208                          "Client: connection to ended with reason %i: `%s', %i handles running\n",
209                          msg->data.result,
210                          curl_easy_strerror (msg->data.result), running);
211         continue;
212       }
213
214       GNUNET_assert (CURLE_OK ==
215                      curl_easy_getinfo (easy_h, CURLINFO_PRIVATE, &d));
216       s = (struct Session *) d;
217
218       if (GNUNET_YES != exist_session(plugin, s))
219       {
220         GNUNET_break (0);
221         return;
222       }
223
224       GNUNET_assert (s != NULL);
225
226       if (msg->msg == CURLMSG_DONE)
227       {
228         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
229                          "Client: %p connection to '%s'  %s ended with reason %i: `%s'\n",
230                          msg->easy_handle, GNUNET_i2s (&s->target),
231                          http_plugin_address_to_string (NULL, s->addr,
232                                                         s->addrlen),
233                          msg->data.result,
234                          curl_easy_strerror (msg->data.result));
235
236         client_disconnect (s);
237         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, 
238                          plugin->name,
239                          "Notifying about ended session to peer `%s' `%s'\n", 
240                          GNUNET_i2s (&s->target), 
241                          http_plugin_address_to_string (plugin, s->addr, s->addrlen));
242         notify_session_end (plugin, &s->target, s);
243       }
244     }
245   }
246   while (mret == CURLM_CALL_MULTI_PERFORM);
247   client_schedule (plugin, GNUNET_NO);
248 }
249
250
251 int
252 client_disconnect (struct Session *s)
253 {
254   int res = GNUNET_OK;
255   CURLMcode mret;
256   struct Plugin *plugin = s->plugin;
257   struct HTTP_Message *msg;
258   struct HTTP_Message *t;
259
260   if (GNUNET_YES != exist_session(plugin, s))
261   {
262     GNUNET_break (0);
263     return GNUNET_SYSERR;
264   }
265
266   if (s->client_put != NULL)
267   {
268     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
269                      "Client: %p Deleting outbound PUT session to peer `%s'\n",
270                      s->client_put, GNUNET_i2s (&s->target));
271
272     mret = curl_multi_remove_handle (plugin->client_mh, s->client_put);
273     if (mret != CURLM_OK)
274     {
275       curl_easy_cleanup (s->client_put);
276       res = GNUNET_SYSERR;
277       GNUNET_break (0);
278     }
279     curl_easy_cleanup (s->client_put);
280     s->client_put = NULL;
281   }
282
283
284   if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK)
285   {
286     GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
287     s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK;
288   }
289
290   if (s->client_get != NULL)
291   {
292     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
293                      "Client: %p Deleting outbound GET session to peer `%s'\n",
294                      s->client_get, GNUNET_i2s (&s->target));
295
296     mret = curl_multi_remove_handle (plugin->client_mh, s->client_get);
297     if (mret != CURLM_OK)
298     {
299       curl_easy_cleanup (s->client_get);
300       res = GNUNET_SYSERR;
301       GNUNET_break (0);
302     }
303     curl_easy_cleanup (s->client_get);
304     s->client_get = NULL;
305   }
306
307   msg = s->msg_head;
308   while (msg != NULL)
309   {
310     t = msg->next;
311     if (NULL != msg->transmit_cont)
312       msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR);
313     GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg);
314     GNUNET_free (msg);
315     msg = t;
316   }
317
318   plugin->cur_connections -= 2;
319
320   GNUNET_assert (plugin->outbound_sessions > 0);
321   plugin->outbound_sessions --;
322   GNUNET_STATISTICS_set (plugin->env->stats,
323       "# HTTP outbound sessions",
324       plugin->outbound_sessions,
325       GNUNET_NO);
326
327   /* Re-schedule since handles have changed */
328   if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
329   {
330     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
331     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
332   }
333
334   client_schedule (plugin, GNUNET_YES);
335
336   return res;
337 }
338
339 static int
340 client_receive_mst_cb (void *cls, void *client,
341                        const struct GNUNET_MessageHeader *message)
342 {
343   struct Session *s = cls;
344   struct GNUNET_TIME_Relative delay;
345
346   if (GNUNET_YES != exist_session(p, s))
347   {
348     GNUNET_break (0);
349     return GNUNET_OK;
350   }
351
352   delay = http_plugin_receive (s, &s->target, message, s, s->addr, s->addrlen);
353   s->next_receive =
354       GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), delay);
355
356   if (GNUNET_TIME_absolute_get ().abs_value < s->next_receive.abs_value)
357   {
358     struct Plugin *plugin = s->plugin;
359
360     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
361                      "Client: peer `%s' address `%s' next read delayed for %llu ms\n",
362                      GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen),
363                      delay);
364   }
365   return GNUNET_OK;
366 }
367
368
369 static void
370 client_wake_up (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
371 {
372   struct Session *s = cls;
373
374   if (GNUNET_YES != exist_session(p, s))
375   {
376     GNUNET_break (0);
377     return;
378   }
379   s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK;
380   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
381     return;
382   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
383                    "Client: %p Waking up receive handle\n", s->client_get);
384   if (s->client_get != NULL)
385     curl_easy_pause (s->client_get, CURLPAUSE_CONT);
386 }
387
388
389 /**
390  * Callback method used with libcurl
391  * Method is called when libcurl needs to write data during sending
392  *
393  * @param stream pointer where to write data
394  * @param size size of an individual element
395  * @param nmemb count of elements that can be written to the buffer
396  * @param cls destination pointer, passed to the libcurl handle
397  * @return bytes read from stream
398  */
399 static size_t
400 client_receive (void *stream, size_t size, size_t nmemb, void *cls)
401 {
402   struct Session *s = cls;
403   struct GNUNET_TIME_Absolute now;
404   size_t len = size * nmemb;
405   struct Plugin *plugin = s->plugin;
406
407   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
408                    "Client: Received %u bytes from peer `%s'\n", len,
409                    GNUNET_i2s (&s->target));
410   now = GNUNET_TIME_absolute_get ();
411   if (now.abs_value < s->next_receive.abs_value)
412   {
413     struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
414     struct GNUNET_TIME_Relative delta =
415         GNUNET_TIME_absolute_get_difference (now, s->next_receive);
416     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
417                      "Client: %p No inbound bandwidth available! Next read was delayed for %llu ms\n",
418                      s->client_get, delta.rel_value);
419     if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK)
420     {
421       GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
422       s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK;
423     }
424     s->recv_wakeup_task =
425         GNUNET_SCHEDULER_add_delayed (delta, &client_wake_up, s);
426     return CURLPAUSE_ALL;
427   }
428   if (NULL == s->msg_tk)
429     s->msg_tk = GNUNET_SERVER_mst_create (&client_receive_mst_cb, s);
430   GNUNET_SERVER_mst_receive (s->msg_tk, s, stream, len, GNUNET_NO, GNUNET_NO);
431   return len;
432 }
433
434
435 /**
436  * Callback method used with libcurl
437  * Method is called when libcurl needs to read data during sending
438  *
439  * @param stream pointer where to write data
440  * @param size size of an individual element
441  * @param nmemb count of elements that can be written to the buffer
442  * @param cls source pointer, passed to the libcurl handle
443  * @return bytes written to stream, returning 0 will terminate connection!
444  */
445 static size_t
446 client_send_cb (void *stream, size_t size, size_t nmemb, void *cls)
447 {
448   struct Session *s = cls;
449   struct Plugin *plugin = s->plugin;
450   struct HTTP_Message *msg = s->msg_head;
451   size_t len;
452
453   if (GNUNET_YES != exist_session(plugin, s))
454   {
455     GNUNET_break (0);
456     return 0;
457   }
458   if (NULL == msg)
459   {
460     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
461                      "Client: %p Nothing to send! Suspending PUT handle!\n",
462                      s->client_put);
463     s->client_put_paused = GNUNET_YES;
464     return CURL_READFUNC_PAUSE;
465   }
466   /* data to send */
467   GNUNET_assert (msg->pos < msg->size);
468   /* calculate how much fits in buffer */
469   len = GNUNET_MIN (msg->size - msg->pos,
470                     size * nmemb);
471   memcpy (stream, &msg->buf[msg->pos], len);
472   msg->pos += len;
473   if (msg->pos == msg->size)
474   {
475     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
476                      "Client: %p Message with %u bytes sent, removing message from queue\n",
477                      s->client_put, msg->size, msg->pos);
478     /* Calling transmit continuation  */
479     GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg);
480     if (NULL != msg->transmit_cont)
481       msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_OK);
482     GNUNET_free (msg);
483   }
484   return len;
485 }
486
487
488 int
489 client_connect (struct Session *s)
490 {
491   struct Plugin *plugin = s->plugin;
492   int res = GNUNET_OK;
493   char *url;
494   CURLMcode mret;
495
496   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
497                    "Initiating outbound session peer `%s'\n",
498                    GNUNET_i2s (&s->target));
499   s->inbound = GNUNET_NO;
500   plugin->last_tag++;
501   /* create url */
502   GNUNET_asprintf (&url, "%s%s;%u",
503                    http_plugin_address_to_string (plugin, s->addr, s->addrlen),
504                    GNUNET_h2s_full (&plugin->env->my_identity->hashPubKey),
505                    plugin->last_tag);
506 #if 0
507   GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, "URL `%s'\n", url);
508 #endif
509   /* create get connection */
510   s->client_get = curl_easy_init ();
511 #if VERBOSE_CURL
512   curl_easy_setopt (s->client_get, CURLOPT_VERBOSE, 1L);
513   curl_easy_setopt (s->client_get, CURLOPT_DEBUGFUNCTION, &client_log);
514   curl_easy_setopt (s->client_get, CURLOPT_DEBUGDATA, s->client_get);
515 #endif
516 #if BUILD_HTTPS
517   curl_easy_setopt (s->client_get, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
518   curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYPEER, 0);
519   curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYHOST, 0);
520 #endif
521   curl_easy_setopt (s->client_get, CURLOPT_URL, url);
522   //curl_easy_setopt (s->client_get, CURLOPT_HEADERFUNCTION, &curl_get_header_cb);
523   //curl_easy_setopt (s->client_get, CURLOPT_WRITEHEADER, ps);
524   curl_easy_setopt (s->client_get, CURLOPT_READFUNCTION, client_send_cb);
525   curl_easy_setopt (s->client_get, CURLOPT_READDATA, s);
526   curl_easy_setopt (s->client_get, CURLOPT_WRITEFUNCTION, client_receive);
527   curl_easy_setopt (s->client_get, CURLOPT_WRITEDATA, s);
528   curl_easy_setopt (s->client_get, CURLOPT_TIMEOUT_MS,
529                     (long) GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value);
530   curl_easy_setopt (s->client_get, CURLOPT_PRIVATE, s);
531   curl_easy_setopt (s->client_get, CURLOPT_CONNECTTIMEOUT_MS,
532                     (long) HTTP_NOT_VALIDATED_TIMEOUT.rel_value);
533   curl_easy_setopt (s->client_get, CURLOPT_BUFFERSIZE,
534                     2 * GNUNET_SERVER_MAX_MESSAGE_SIZE);
535 #if CURL_TCP_NODELAY
536   curl_easy_setopt (ps->recv_endpoint, CURLOPT_TCP_NODELAY, 1);
537 #endif
538
539   /* create put connection */
540   s->client_put = curl_easy_init ();
541 #if VERBOSE_CURL
542   curl_easy_setopt (s->client_put, CURLOPT_VERBOSE, 1L);
543   curl_easy_setopt (s->client_put, CURLOPT_DEBUGFUNCTION, &client_log);
544   curl_easy_setopt (s->client_put, CURLOPT_DEBUGDATA, s->client_put);
545 #endif
546 #if BUILD_HTTPS
547   curl_easy_setopt (s->client_put, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
548   curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYPEER, 0);
549   curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYHOST, 0);
550 #endif
551   curl_easy_setopt (s->client_put, CURLOPT_URL, url);
552   curl_easy_setopt (s->client_put, CURLOPT_PUT, 1L);
553   //curl_easy_setopt (s->client_put, CURLOPT_HEADERFUNCTION, &curl_put_header_cb);
554   //curl_easy_setopt (s->client_put, CURLOPT_WRITEHEADER, ps);
555   curl_easy_setopt (s->client_put, CURLOPT_READFUNCTION, client_send_cb);
556   curl_easy_setopt (s->client_put, CURLOPT_READDATA, s);
557   curl_easy_setopt (s->client_put, CURLOPT_WRITEFUNCTION, client_receive);
558   curl_easy_setopt (s->client_put, CURLOPT_WRITEDATA, s);
559   curl_easy_setopt (s->client_put, CURLOPT_TIMEOUT_MS,
560                     (long) GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value);
561   curl_easy_setopt (s->client_put, CURLOPT_PRIVATE, s);
562   curl_easy_setopt (s->client_put, CURLOPT_CONNECTTIMEOUT_MS,
563                     (long) HTTP_NOT_VALIDATED_TIMEOUT.rel_value);
564   curl_easy_setopt (s->client_put, CURLOPT_BUFFERSIZE,
565                     2 * GNUNET_SERVER_MAX_MESSAGE_SIZE);
566 #if CURL_TCP_NODELAY
567   curl_easy_setopt (s->client_put, CURLOPT_TCP_NODELAY, 1);
568 #endif
569
570   GNUNET_free (url);
571
572   mret = curl_multi_add_handle (plugin->client_mh, s->client_get);
573   if (mret != CURLM_OK)
574   {
575     curl_easy_cleanup (s->client_get);
576     res = GNUNET_SYSERR;
577     GNUNET_break (0);
578   }
579
580   mret = curl_multi_add_handle (plugin->client_mh, s->client_put);
581   if (mret != CURLM_OK)
582   {
583     curl_multi_remove_handle (plugin->client_mh, s->client_get);
584     curl_easy_cleanup (s->client_get);
585     curl_easy_cleanup (s->client_put);
586     res = GNUNET_SYSERR;
587     GNUNET_break (0);
588   }
589
590   /* Perform connect */
591   plugin->cur_connections += 2;
592
593   plugin->outbound_sessions ++;
594   GNUNET_STATISTICS_set (plugin->env->stats,
595       "# HTTP outbound sessions",
596       plugin->outbound_sessions,
597       GNUNET_NO);
598
599   /* Re-schedule since handles have changed */
600   if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
601   {
602     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
603     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
604   }
605   plugin->client_perform_task = GNUNET_SCHEDULER_add_now (client_run, plugin);
606
607   return res;
608 }
609
610
611 int
612 client_start (struct Plugin *plugin)
613 {
614   int res = GNUNET_OK;
615   p = plugin;
616
617   curl_global_init (CURL_GLOBAL_ALL);
618   plugin->client_mh = curl_multi_init ();
619
620   if (NULL == plugin->client_mh)
621   {
622     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
623                      _
624                      ("Could not initialize curl multi handle, failed to start %s plugin!\n"),
625                          plugin->name);
626     res = GNUNET_SYSERR;
627   }
628   return res;
629 }
630
631
632 void
633 client_stop (struct Plugin *plugin)
634 {
635   p = NULL;
636   if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
637   {
638     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
639     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
640   }
641
642   curl_multi_cleanup (plugin->client_mh);
643   curl_global_cleanup ();
644 }
645
646
647
648 /* end of plugin_transport_http_client.c */