-fixig #2377
[oweals/gnunet.git] / src / transport / plugin_transport_http_client.c
1 /*
2      This file is part of GNUnet
3      (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 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: %X - %s", cls, text);
59 #else
60     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-http",
61                      "Client: %X - %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  * Function setting up file descriptors and scheduling task to run
78  *
79  * @param  plugin plugin as closure
80  * @param now schedule task in 1ms, regardless of what curl may say
81  * @return GNUNET_SYSERR for hard failure, GNUNET_OK for ok
82  */
83 static int
84 client_schedule (struct Plugin *plugin, int now)
85 {
86   fd_set rs;
87   fd_set ws;
88   fd_set es;
89   int max;
90   struct GNUNET_NETWORK_FDSet *grs;
91   struct GNUNET_NETWORK_FDSet *gws;
92   long to;
93   CURLMcode mret;
94   struct GNUNET_TIME_Relative timeout;
95
96   /* Cancel previous scheduled task */
97   if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
98   {
99     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
100     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
101   }
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 (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
158   if (s->client_put_paused == GNUNET_YES)
159   {
160     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
161                      "Client: %X was suspended, unpausing\n", s->client_put);
162     s->client_put_paused = GNUNET_NO;
163     curl_easy_pause (s->client_put, CURLPAUSE_CONT);
164   }
165
166   client_schedule (s->plugin, GNUNET_YES);
167
168   return GNUNET_OK;
169 }
170
171
172
173 /**
174  * Task performing curl operations
175  * @param cls plugin as closure
176  * @param tc gnunet scheduler task context
177  */
178 static void
179 client_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
180 {
181   struct Plugin *plugin = cls;
182   int running;
183   CURLMcode mret;
184
185   GNUNET_assert (cls != NULL);
186
187   plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
188   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
189     return;
190
191   do
192   {
193     running = 0;
194     mret = curl_multi_perform (plugin->client_mh, &running);
195
196     CURLMsg *msg;
197     int msgs_left;
198
199     while ((msg = curl_multi_info_read (plugin->client_mh, &msgs_left)))
200     {
201       CURL *easy_h = msg->easy_handle;
202       struct Session *s = NULL;
203       char *d = (char *) s;
204
205
206       //GNUNET_assert (easy_h != NULL);
207       if (easy_h == NULL)
208       {
209         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
210                          "Client: connection to ended with reason %i: `%s', %i handles running\n",
211                          msg->data.result,
212                          curl_easy_strerror (msg->data.result), running);
213         continue;
214       }
215
216       GNUNET_assert (CURLE_OK ==
217                      curl_easy_getinfo (easy_h, CURLINFO_PRIVATE, &d));
218       s = (struct Session *) d;
219
220       if (GNUNET_YES != exist_session(plugin, s))
221       {
222         GNUNET_break (0);
223         return;
224       }
225
226       GNUNET_assert (s != NULL);
227
228       if (msg->msg == CURLMSG_DONE)
229       {
230         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
231                          "Client: %X connection to '%s'  %s ended with reason %i: `%s'\n",
232                          msg->easy_handle, GNUNET_i2s (&s->target),
233                          http_plugin_address_to_string (NULL, s->addr,
234                                                         s->addrlen),
235                          msg->data.result,
236                          curl_easy_strerror (msg->data.result));
237
238         client_disconnect (s);
239         //GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,"Notifying about ended session to peer `%s' `%s'\n", GNUNET_i2s (&s->target), http_plugin_address_to_string (plugin, s->addr, s->addrlen));
240         notify_session_end (plugin, &s->target, s);
241       }
242     }
243   }
244   while (mret == CURLM_CALL_MULTI_PERFORM);
245   client_schedule (plugin, GNUNET_NO);
246 }
247
248 int
249 client_disconnect (struct Session *s)
250 {
251   int res = GNUNET_OK;
252   CURLMcode mret;
253   struct Plugin *plugin = s->plugin;
254   struct HTTP_Message *msg;
255   struct HTTP_Message *t;
256
257   if (GNUNET_YES != exist_session(plugin, s))
258   {
259     GNUNET_break (0);
260     return GNUNET_SYSERR;
261   }
262
263   if (s->client_put != NULL)
264   {
265     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
266                      "Client: %X Deleting outbound PUT session to peer `%s'\n",
267                      s->client_put, GNUNET_i2s (&s->target));
268
269     mret = curl_multi_remove_handle (plugin->client_mh, s->client_put);
270     if (mret != CURLM_OK)
271     {
272       curl_easy_cleanup (s->client_put);
273       res = GNUNET_SYSERR;
274       GNUNET_break (0);
275     }
276     curl_easy_cleanup (s->client_put);
277     s->client_put = NULL;
278   }
279
280
281   if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK)
282   {
283     GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
284     s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK;
285   }
286
287   if (s->client_get != NULL)
288   {
289     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
290                      "Client: %X Deleting outbound GET session to peer `%s'\n",
291                      s->client_get, GNUNET_i2s (&s->target));
292
293     mret = curl_multi_remove_handle (plugin->client_mh, s->client_get);
294     if (mret != CURLM_OK)
295     {
296       curl_easy_cleanup (s->client_get);
297       res = GNUNET_SYSERR;
298       GNUNET_break (0);
299     }
300     curl_easy_cleanup (s->client_get);
301     s->client_get = NULL;
302   }
303
304   msg = s->msg_head;
305   while (msg != NULL)
306   {
307     t = msg->next;
308     if (NULL != msg->transmit_cont)
309       msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR);
310     GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg);
311     GNUNET_free (msg);
312     msg = t;
313   }
314
315   plugin->cur_connections -= 2;
316
317   GNUNET_assert (plugin->outbound_sessions > 0);
318   plugin->outbound_sessions --;
319   GNUNET_STATISTICS_set (plugin->env->stats,
320       "# HTTP outbound sessions",
321       plugin->outbound_sessions,
322       GNUNET_NO);
323
324   /* Re-schedule since handles have changed */
325   if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
326   {
327     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
328     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
329   }
330
331   client_schedule (plugin, GNUNET_YES);
332
333   return res;
334 }
335
336 static int
337 client_receive_mst_cb (void *cls, void *client,
338                        const struct GNUNET_MessageHeader *message)
339 {
340   struct Session *s = cls;
341   struct GNUNET_TIME_Relative delay;
342
343   if (GNUNET_YES != exist_session(p, s))
344   {
345     GNUNET_break (0);
346     return GNUNET_OK;
347   }
348
349   delay = http_plugin_receive (s, &s->target, message, s, s->addr, s->addrlen);
350   s->next_receive =
351       GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), delay);
352
353   if (GNUNET_TIME_absolute_get ().abs_value < s->next_receive.abs_value)
354   {
355     struct Plugin *plugin = s->plugin;
356
357     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
358                      "Client: peer `%s' address `%s' next read delayed for %llu ms\n",
359                      GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen),
360                      delay);
361   }
362   return GNUNET_OK;
363 }
364
365 static void
366 client_wake_up (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
367 {
368   struct Session *s = cls;
369
370   if (GNUNET_YES != exist_session(p, s))
371   {
372     GNUNET_break (0);
373     return;
374   }
375
376   s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK;
377
378   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
379     return;
380
381   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
382                    "Client: %X Waking up receive handle\n", s->client_get);
383
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 * @param stream pointer where to write data
393 * @param size size of an individual element
394 * @param nmemb count of elements that can be written to the buffer
395 * @param cls destination pointer, passed to the libcurl handle
396 * @return bytes read from stream
397 */
398 static size_t
399 client_receive (void *stream, size_t size, size_t nmemb, void *cls)
400 {
401   struct Session *s = cls;
402   struct GNUNET_TIME_Absolute now;
403   size_t len = size * nmemb;
404   struct Plugin *plugin = s->plugin;
405
406   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
407                    "Client: Received %Zu bytes from peer `%s'\n", len,
408                    GNUNET_i2s (&s->target));
409   now = GNUNET_TIME_absolute_get ();
410   if (now.abs_value < s->next_receive.abs_value)
411   {
412     struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
413     struct GNUNET_TIME_Relative delta =
414         GNUNET_TIME_absolute_get_difference (now, s->next_receive);
415     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
416                      "Client: %X No inbound bandwidth available! Next read was delayed for %llu ms\n",
417                      s->client_get, delta.rel_value);
418     if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK)
419     {
420       GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
421       s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK;
422     }
423     s->recv_wakeup_task =
424         GNUNET_SCHEDULER_add_delayed (delta, &client_wake_up, s);
425     return CURLPAUSE_ALL;
426   }
427
428
429   if (s->msg_tk == NULL)
430     s->msg_tk = GNUNET_SERVER_mst_create (&client_receive_mst_cb, s);
431
432   GNUNET_SERVER_mst_receive (s->msg_tk, s, stream, len, GNUNET_NO, GNUNET_NO);
433
434   return len;
435 }
436
437 /**
438  * Callback method used with libcurl
439  * Method is called when libcurl needs to read data during sending
440  * @param stream pointer where to write data
441  * @param size size of an individual element
442  * @param nmemb count of elements that can be written to the buffer
443  * @param cls source pointer, passed to the libcurl handle
444  * @return bytes written to stream, returning 0 will terminate connection!
445  */
446 static size_t
447 client_send_cb (void *stream, size_t size, size_t nmemb, void *cls)
448 {
449   struct Session *s = cls;
450   struct Plugin *plugin = s->plugin;
451   size_t bytes_sent = 0;
452   size_t len;
453
454   if (GNUNET_YES != exist_session(plugin, s))
455   {
456     GNUNET_break (0);
457     return GNUNET_SYSERR;
458   }
459
460   struct HTTP_Message *msg = s->msg_head;
461
462   if (msg == NULL)
463   {
464     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
465                      "Client: %X Nothing to send! Suspending PUT handle!\n",
466                      s->client_put);
467     s->client_put_paused = GNUNET_YES;
468     return CURL_READFUNC_PAUSE;
469   }
470
471   GNUNET_assert (msg != NULL);
472   /* data to send */
473   if (msg->pos < msg->size)
474   {
475     /* data fit in buffer */
476     if ((msg->size - msg->pos) <= (size * nmemb))
477     {
478       len = (msg->size - msg->pos);
479       memcpy (stream, &msg->buf[msg->pos], len);
480       msg->pos += len;
481       bytes_sent = len;
482     }
483     else
484     {
485       len = size * nmemb;
486       memcpy (stream, &msg->buf[msg->pos], len);
487       msg->pos += len;
488       bytes_sent = len;
489     }
490   }
491   /* no data to send */
492   else
493   {
494     GNUNET_assert (0);
495     bytes_sent = 0;
496   }
497
498   if (msg->pos == msg->size)
499   {
500     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
501                      "Client: %X Message with %u bytes sent, removing message from queue\n",
502                      s->client_put, msg->size, msg->pos);
503     /* Calling transmit continuation  */
504     if (NULL != msg->transmit_cont)
505       msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_OK);
506     GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg);
507     GNUNET_free (msg);
508   }
509   return bytes_sent;
510 }
511
512 int
513 client_connect (struct Session *s)
514 {
515   struct Plugin *plugin = s->plugin;
516   int res = GNUNET_OK;
517   char *url;
518   CURLMcode mret;
519
520   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
521                    "Initiating outbound session peer `%s'\n",
522                    GNUNET_i2s (&s->target));
523   s->inbound = GNUNET_NO;
524   plugin->last_tag++;
525   /* create url */
526   GNUNET_asprintf (&url, "%s%s;%u",
527                    http_plugin_address_to_string (plugin, s->addr, s->addrlen),
528                    GNUNET_h2s_full (&plugin->env->my_identity->hashPubKey),
529                    plugin->last_tag);
530 #if 0
531   GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, "URL `%s'\n", url);
532 #endif
533   /* create get connection */
534   s->client_get = curl_easy_init ();
535 #if VERBOSE_CURL
536   curl_easy_setopt (s->client_get, CURLOPT_VERBOSE, 1L);
537   curl_easy_setopt (s->client_get, CURLOPT_DEBUGFUNCTION, &client_log);
538   curl_easy_setopt (s->client_get, CURLOPT_DEBUGDATA, s->client_get);
539 #endif
540 #if BUILD_HTTPS
541   curl_easy_setopt (s->client_get, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
542   curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYPEER, 0);
543   curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYHOST, 0);
544 #endif
545   curl_easy_setopt (s->client_get, CURLOPT_URL, url);
546   //curl_easy_setopt (s->client_get, CURLOPT_HEADERFUNCTION, &curl_get_header_cb);
547   //curl_easy_setopt (s->client_get, CURLOPT_WRITEHEADER, ps);
548   curl_easy_setopt (s->client_get, CURLOPT_READFUNCTION, client_send_cb);
549   curl_easy_setopt (s->client_get, CURLOPT_READDATA, s);
550   curl_easy_setopt (s->client_get, CURLOPT_WRITEFUNCTION, client_receive);
551   curl_easy_setopt (s->client_get, CURLOPT_WRITEDATA, s);
552   curl_easy_setopt (s->client_get, CURLOPT_TIMEOUT_MS,
553                     (long) GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value);
554   curl_easy_setopt (s->client_get, CURLOPT_PRIVATE, s);
555   curl_easy_setopt (s->client_get, CURLOPT_CONNECTTIMEOUT_MS,
556                     (long) HTTP_NOT_VALIDATED_TIMEOUT.rel_value);
557   curl_easy_setopt (s->client_get, CURLOPT_BUFFERSIZE,
558                     2 * GNUNET_SERVER_MAX_MESSAGE_SIZE);
559 #if CURL_TCP_NODELAY
560   curl_easy_setopt (ps->recv_endpoint, CURLOPT_TCP_NODELAY, 1);
561 #endif
562
563   /* create put connection */
564   s->client_put = curl_easy_init ();
565 #if VERBOSE_CURL
566   curl_easy_setopt (s->client_put, CURLOPT_VERBOSE, 1L);
567   curl_easy_setopt (s->client_put, CURLOPT_DEBUGFUNCTION, &client_log);
568   curl_easy_setopt (s->client_put, CURLOPT_DEBUGDATA, s->client_put);
569 #endif
570 #if BUILD_HTTPS
571   curl_easy_setopt (s->client_put, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
572   curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYPEER, 0);
573   curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYHOST, 0);
574 #endif
575   curl_easy_setopt (s->client_put, CURLOPT_URL, url);
576   curl_easy_setopt (s->client_put, CURLOPT_PUT, 1L);
577   //curl_easy_setopt (s->client_put, CURLOPT_HEADERFUNCTION, &curl_put_header_cb);
578   //curl_easy_setopt (s->client_put, CURLOPT_WRITEHEADER, ps);
579   curl_easy_setopt (s->client_put, CURLOPT_READFUNCTION, client_send_cb);
580   curl_easy_setopt (s->client_put, CURLOPT_READDATA, s);
581   curl_easy_setopt (s->client_put, CURLOPT_WRITEFUNCTION, client_receive);
582   curl_easy_setopt (s->client_put, CURLOPT_WRITEDATA, s);
583   curl_easy_setopt (s->client_put, CURLOPT_TIMEOUT_MS,
584                     (long) GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value);
585   curl_easy_setopt (s->client_put, CURLOPT_PRIVATE, s);
586   curl_easy_setopt (s->client_put, CURLOPT_CONNECTTIMEOUT_MS,
587                     (long) HTTP_NOT_VALIDATED_TIMEOUT.rel_value);
588   curl_easy_setopt (s->client_put, CURLOPT_BUFFERSIZE,
589                     2 * GNUNET_SERVER_MAX_MESSAGE_SIZE);
590 #if CURL_TCP_NODELAY
591   curl_easy_setopt (s->client_put, CURLOPT_TCP_NODELAY, 1);
592 #endif
593
594   GNUNET_free (url);
595
596   mret = curl_multi_add_handle (plugin->client_mh, s->client_get);
597   if (mret != CURLM_OK)
598   {
599     curl_easy_cleanup (s->client_get);
600     res = GNUNET_SYSERR;
601     GNUNET_break (0);
602   }
603
604   mret = curl_multi_add_handle (plugin->client_mh, s->client_put);
605   if (mret != CURLM_OK)
606   {
607     curl_multi_remove_handle (plugin->client_mh, s->client_get);
608     curl_easy_cleanup (s->client_get);
609     curl_easy_cleanup (s->client_put);
610     res = GNUNET_SYSERR;
611     GNUNET_break (0);
612   }
613
614   /* Perform connect */
615   plugin->cur_connections += 2;
616
617   plugin->outbound_sessions ++;
618   GNUNET_STATISTICS_set (plugin->env->stats,
619       "# HTTP outbound sessions",
620       plugin->outbound_sessions,
621       GNUNET_NO);
622
623   /* Re-schedule since handles have changed */
624   if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
625   {
626     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
627     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
628   }
629   plugin->client_perform_task = GNUNET_SCHEDULER_add_now (client_run, plugin);
630
631   return res;
632 }
633
634 int
635 client_start (struct Plugin *plugin)
636 {
637   int res = GNUNET_OK;
638   p = plugin;
639
640   curl_global_init (CURL_GLOBAL_ALL);
641   plugin->client_mh = curl_multi_init ();
642
643   if (NULL == plugin->client_mh)
644   {
645     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
646                      _
647                      ("Could not initialize curl multi handle, failed to start %s plugin!\n"),
648                          plugin->name);
649     res = GNUNET_SYSERR;
650   }
651   return res;
652 }
653
654 void
655 client_stop (struct Plugin *plugin)
656 {
657   p = NULL;
658   if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
659   {
660     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
661     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
662   }
663
664   curl_multi_cleanup (plugin->client_mh);
665   curl_global_cleanup ();
666 }
667
668
669
670 /* end of plugin_transport_http_client.c */