- fix for 601 assertion
[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 void
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;
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 }
363
364 static void
365 client_wake_up (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
366 {
367   struct Session *s = cls;
368
369   if (GNUNET_YES != exist_session(p, s))
370   {
371     GNUNET_break (0);
372     return;
373   }
374
375   s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK;
376
377   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
378     return;
379
380   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
381                    "Client: %X Waking up receive handle\n", s->client_get);
382
383   if (s->client_get != NULL)
384     curl_easy_pause (s->client_get, CURLPAUSE_CONT);
385
386 }
387
388 /**
389 * Callback method used with libcurl
390 * Method is called when libcurl needs to write data during sending
391 * @param stream pointer where to write data
392 * @param size size of an individual element
393 * @param nmemb count of elements that can be written to the buffer
394 * @param cls destination pointer, passed to the libcurl handle
395 * @return bytes read from stream
396 */
397 static size_t
398 client_receive (void *stream, size_t size, size_t nmemb, void *cls)
399 {
400   struct Session *s = cls;
401   struct GNUNET_TIME_Absolute now;
402   size_t len = size * nmemb;
403   struct Plugin *plugin = s->plugin;
404
405   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
406                    "Client: Received %Zu bytes from peer `%s'\n", len,
407                    GNUNET_i2s (&s->target));
408   now = GNUNET_TIME_absolute_get ();
409   if (now.abs_value < s->next_receive.abs_value)
410   {
411     struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
412     struct GNUNET_TIME_Relative delta =
413         GNUNET_TIME_absolute_get_difference (now, s->next_receive);
414     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
415                      "Client: %X No inbound bandwidth available! Next read was delayed for %llu ms\n",
416                      s->client_get, delta.rel_value);
417     if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK)
418     {
419       GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
420       s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK;
421     }
422     s->recv_wakeup_task =
423         GNUNET_SCHEDULER_add_delayed (delta, &client_wake_up, s);
424     return CURLPAUSE_ALL;
425   }
426
427
428   if (s->msg_tk == NULL)
429     s->msg_tk = GNUNET_SERVER_mst_create (&client_receive_mst_cb, s);
430
431   GNUNET_SERVER_mst_receive (s->msg_tk, s, stream, len, GNUNET_NO, GNUNET_NO);
432
433   return len;
434 }
435
436 /**
437  * Callback method used with libcurl
438  * Method is called when libcurl needs to read data during sending
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   size_t bytes_sent = 0;
451   size_t len;
452
453   if (GNUNET_YES != exist_session(plugin, s))
454   {
455     GNUNET_break (0);
456     return GNUNET_SYSERR;
457   }
458
459   struct HTTP_Message *msg = s->msg_head;
460
461   if (msg == NULL)
462   {
463     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
464                      "Client: %X Nothing to send! Suspending PUT handle!\n",
465                      s->client_put);
466     s->client_put_paused = GNUNET_YES;
467     return CURL_READFUNC_PAUSE;
468   }
469
470   GNUNET_assert (msg != NULL);
471   /* data to send */
472   if (msg->pos < msg->size)
473   {
474     /* data fit in buffer */
475     if ((msg->size - msg->pos) <= (size * nmemb))
476     {
477       len = (msg->size - msg->pos);
478       memcpy (stream, &msg->buf[msg->pos], len);
479       msg->pos += len;
480       bytes_sent = len;
481     }
482     else
483     {
484       len = size * nmemb;
485       memcpy (stream, &msg->buf[msg->pos], len);
486       msg->pos += len;
487       bytes_sent = len;
488     }
489   }
490   /* no data to send */
491   else
492   {
493     GNUNET_assert (0);
494     bytes_sent = 0;
495   }
496
497   if (msg->pos == msg->size)
498   {
499     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
500                      "Client: %X Message with %u bytes sent, removing message from queue\n",
501                      s->client_put, msg->size, msg->pos);
502     /* Calling transmit continuation  */
503     if (NULL != msg->transmit_cont)
504       msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_OK);
505     GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg);
506     GNUNET_free (msg);
507   }
508   return bytes_sent;
509 }
510
511 int
512 client_connect (struct Session *s)
513 {
514   struct Plugin *plugin = s->plugin;
515   int res = GNUNET_OK;
516   char *url;
517   CURLMcode mret;
518
519   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
520                    "Initiating outbound session peer `%s'\n",
521                    GNUNET_i2s (&s->target));
522   s->inbound = GNUNET_NO;
523   plugin->last_tag++;
524   /* create url */
525   GNUNET_asprintf (&url, "%s%s;%u",
526                    http_plugin_address_to_string (plugin, s->addr, s->addrlen),
527                    GNUNET_h2s_full (&plugin->env->my_identity->hashPubKey),
528                    plugin->last_tag);
529 #if 0
530   GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name, "URL `%s'\n", url);
531 #endif
532   /* create get connection */
533   s->client_get = curl_easy_init ();
534 #if VERBOSE_CURL
535   curl_easy_setopt (s->client_get, CURLOPT_VERBOSE, 1L);
536   curl_easy_setopt (s->client_get, CURLOPT_DEBUGFUNCTION, &client_log);
537   curl_easy_setopt (s->client_get, CURLOPT_DEBUGDATA, s->client_get);
538 #endif
539 #if BUILD_HTTPS
540   curl_easy_setopt (s->client_get, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
541   curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYPEER, 0);
542   curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYHOST, 0);
543 #endif
544   curl_easy_setopt (s->client_get, CURLOPT_URL, url);
545   //curl_easy_setopt (s->client_get, CURLOPT_HEADERFUNCTION, &curl_get_header_cb);
546   //curl_easy_setopt (s->client_get, CURLOPT_WRITEHEADER, ps);
547   curl_easy_setopt (s->client_get, CURLOPT_READFUNCTION, client_send_cb);
548   curl_easy_setopt (s->client_get, CURLOPT_READDATA, s);
549   curl_easy_setopt (s->client_get, CURLOPT_WRITEFUNCTION, client_receive);
550   curl_easy_setopt (s->client_get, CURLOPT_WRITEDATA, s);
551   curl_easy_setopt (s->client_get, CURLOPT_TIMEOUT_MS,
552                     (long) GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value);
553   curl_easy_setopt (s->client_get, CURLOPT_PRIVATE, s);
554   curl_easy_setopt (s->client_get, CURLOPT_CONNECTTIMEOUT_MS,
555                     (long) HTTP_NOT_VALIDATED_TIMEOUT.rel_value);
556   curl_easy_setopt (s->client_get, CURLOPT_BUFFERSIZE,
557                     2 * GNUNET_SERVER_MAX_MESSAGE_SIZE);
558 #if CURL_TCP_NODELAY
559   curl_easy_setopt (ps->recv_endpoint, CURLOPT_TCP_NODELAY, 1);
560 #endif
561
562   /* create put connection */
563   s->client_put = curl_easy_init ();
564 #if VERBOSE_CURL
565   curl_easy_setopt (s->client_put, CURLOPT_VERBOSE, 1L);
566   curl_easy_setopt (s->client_put, CURLOPT_DEBUGFUNCTION, &client_log);
567   curl_easy_setopt (s->client_put, CURLOPT_DEBUGDATA, s->client_put);
568 #endif
569 #if BUILD_HTTPS
570   curl_easy_setopt (s->client_put, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
571   curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYPEER, 0);
572   curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYHOST, 0);
573 #endif
574   curl_easy_setopt (s->client_put, CURLOPT_URL, url);
575   curl_easy_setopt (s->client_put, CURLOPT_PUT, 1L);
576   //curl_easy_setopt (s->client_put, CURLOPT_HEADERFUNCTION, &curl_put_header_cb);
577   //curl_easy_setopt (s->client_put, CURLOPT_WRITEHEADER, ps);
578   curl_easy_setopt (s->client_put, CURLOPT_READFUNCTION, client_send_cb);
579   curl_easy_setopt (s->client_put, CURLOPT_READDATA, s);
580   curl_easy_setopt (s->client_put, CURLOPT_WRITEFUNCTION, client_receive);
581   curl_easy_setopt (s->client_put, CURLOPT_WRITEDATA, s);
582   curl_easy_setopt (s->client_put, CURLOPT_TIMEOUT_MS,
583                     (long) GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value);
584   curl_easy_setopt (s->client_put, CURLOPT_PRIVATE, s);
585   curl_easy_setopt (s->client_put, CURLOPT_CONNECTTIMEOUT_MS,
586                     (long) HTTP_NOT_VALIDATED_TIMEOUT.rel_value);
587   curl_easy_setopt (s->client_put, CURLOPT_BUFFERSIZE,
588                     2 * GNUNET_SERVER_MAX_MESSAGE_SIZE);
589 #if CURL_TCP_NODELAY
590   curl_easy_setopt (s->client_put, CURLOPT_TCP_NODELAY, 1);
591 #endif
592
593   GNUNET_free (url);
594
595   mret = curl_multi_add_handle (plugin->client_mh, s->client_get);
596   if (mret != CURLM_OK)
597   {
598     curl_easy_cleanup (s->client_get);
599     res = GNUNET_SYSERR;
600     GNUNET_break (0);
601   }
602
603   mret = curl_multi_add_handle (plugin->client_mh, s->client_put);
604   if (mret != CURLM_OK)
605   {
606     curl_multi_remove_handle (plugin->client_mh, s->client_get);
607     curl_easy_cleanup (s->client_get);
608     curl_easy_cleanup (s->client_put);
609     res = GNUNET_SYSERR;
610     GNUNET_break (0);
611   }
612
613   /* Perform connect */
614   plugin->cur_connections += 2;
615
616   plugin->outbound_sessions ++;
617   GNUNET_STATISTICS_set (plugin->env->stats,
618       "# HTTP outbound sessions",
619       plugin->outbound_sessions,
620       GNUNET_NO);
621
622   /* Re-schedule since handles have changed */
623   if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
624   {
625     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
626     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
627   }
628   plugin->client_perform_task = GNUNET_SCHEDULER_add_now (client_run, plugin);
629
630   return res;
631 }
632
633 int
634 client_start (struct Plugin *plugin)
635 {
636   int res = GNUNET_OK;
637   p = plugin;
638
639   curl_global_init (CURL_GLOBAL_ALL);
640   plugin->client_mh = curl_multi_init ();
641
642   if (NULL == plugin->client_mh)
643   {
644     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
645                      _
646                      ("Could not initialize curl multi handle, failed to start %s plugin!\n"),
647                          plugin->name);
648     res = GNUNET_SYSERR;
649   }
650   return res;
651 }
652
653 void
654 client_stop (struct Plugin *plugin)
655 {
656   p = NULL;
657   if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
658   {
659     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
660     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
661   }
662
663   curl_multi_cleanup (plugin->client_mh);
664   curl_global_cleanup ();
665 }
666
667
668
669 /* end of plugin_transport_http_client.c */