- figure out http_client overhead
[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/S client transport plugin
24  * @author Matthias Wachs
25  */
26
27 #if BUILD_HTTPS
28 #define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_https_client_init
29 #define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_https_client_done
30 #else
31 #define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_http_client_init
32 #define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_http_client_done
33 #endif
34
35 #define VERBOSE_CURL GNUNET_YES
36
37 #define PUT_DISCONNECT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
38
39 #define ENABLE_PUT GNUNET_YES
40 #define ENABLE_GET GNUNET_YES
41
42 #include "platform.h"
43 #include "gnunet_protocols.h"
44 #include "gnunet_common.h"
45 #include "gnunet_server_lib.h"
46 #include "gnunet_transport_plugin.h"
47 #include "plugin_transport_http_common.h"
48 #include <curl/curl.h>
49
50
51 /**
52  * Encapsulation of all of the state of the plugin.
53  */
54 struct HTTP_Client_Plugin;
55
56
57 /**
58  *  Message to send using http
59  */
60 struct HTTP_Message
61 {
62   /**
63    * next pointer for double linked list
64    */
65   struct HTTP_Message *next;
66
67   /**
68    * previous pointer for double linked list
69    */
70   struct HTTP_Message *prev;
71
72   /**
73    * buffer containing data to send
74    */
75   char *buf;
76
77   /**
78    * amount of data already sent
79    */
80   size_t pos;
81
82   /**
83    * buffer length
84    */
85   size_t size;
86
87   /**
88    * HTTP overhead required to send this message
89    * FIXME: to implement
90    */
91   size_t overhead;
92
93   /**
94    * Continuation function to call once the transmission buffer
95    * has again space available.  NULL if there is no
96    * continuation to call.
97    */
98   GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
99
100   /**
101    * Closure for transmit_cont.
102    */
103   void *transmit_cont_cls;
104 };
105
106 /**
107  * Session handle for connections.
108  */
109 struct Session
110 {
111   /**
112    * To whom are we talking to (set to our identity
113    * if we are still waiting for the welcome message)
114    */
115   struct GNUNET_PeerIdentity target;
116
117   /**
118    * Stored in a linked list.
119    */
120   struct Session *next;
121
122   /**
123    * Stored in a linked list.
124    */
125   struct Session *prev;
126
127   /**
128    * The URL to connect to
129    */
130   char *url;
131
132   /**
133    * Address
134    */
135   void *addr;
136
137   /**
138    * Address length
139    */
140   size_t addrlen;
141
142   /**
143    * ATS network type in NBO
144    */
145   uint32_t ats_address_network_type;
146
147   /**
148    * Pointer to the global plugin struct.
149    */
150   struct HTTP_Client_Plugin *plugin;
151
152   /**
153    * Was session given to transport service?
154    */
155  // int session_passed;
156
157   /**
158    * Client send handle
159    */
160   void *client_put;
161
162
163   /**
164    * Is the client PUT handle currently paused
165    */
166   int put_paused;
167
168   /**
169    * Is the client PUT handle disconnect in progress?
170    */
171   int put_tmp_disconnecting;
172
173   /**
174    * Is the client PUT handle temporarily disconnected?
175    */
176   int put_tmp_disconnected;
177
178   /**
179    * We received data to send while disconnecting, reconnect immediately
180    */
181   int put_reconnect_required;
182
183   /**
184    * Client receive handle
185    */
186   void *client_get;
187
188   /**
189    * Outbound overhead due to HTTP connection
190    * Add to next message of this session when calling callback
191    */
192   size_t overhead;
193
194   /**
195    * next pointer for double linked list
196    */
197   struct HTTP_Message *msg_head;
198
199   /**
200    * previous pointer for double linked list
201    */
202   struct HTTP_Message *msg_tail;
203
204   /**
205    * Message stream tokenizer for incoming data
206    */
207   struct GNUNET_SERVER_MessageStreamTokenizer *msg_tk;
208
209   /**
210    * Session timeout task
211    */
212   GNUNET_SCHEDULER_TaskIdentifier put_disconnect_task;
213
214   /**
215    * Session timeout task
216    */
217   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
218
219   /**
220    * Task to wake up client receive handle when receiving is allowed again
221    */
222   GNUNET_SCHEDULER_TaskIdentifier recv_wakeup_task;
223
224   /**
225   * Absolute time when to receive data again
226   * Used for receive throttling
227   */
228  struct GNUNET_TIME_Absolute next_receive;
229 };
230
231
232 /**
233  * Encapsulation of all of the state of the plugin.
234  */
235 struct HTTP_Client_Plugin
236 {
237   /**
238    * Our environment.
239    */
240   struct GNUNET_TRANSPORT_PluginEnvironment *env;
241
242   /**
243    * Linked list head of open sessions.
244    */
245   struct Session *head;
246
247   /**
248    * Linked list tail of open sessions.
249    */
250   struct Session *tail;
251
252   /**
253    * Plugin name
254    */
255   char *name;
256
257   /**
258    * Protocol
259    */
260   char *protocol;
261
262   /**
263    * Maximum number of sockets the plugin can use
264    * Each http inbound /outbound connections are two connections
265    */
266   unsigned int max_connections;
267
268   /**
269    * Current number of sockets the plugin can use
270    * Each http inbound /outbound connections are two connections
271    */
272   unsigned int cur_connections;
273
274   /**
275    * Last used unique HTTP connection tag
276    */
277   uint32_t last_tag;
278
279   /**
280    * use IPv6
281    */
282   uint16_t use_ipv6;
283
284   /**
285    * use IPv4
286    */
287   uint16_t use_ipv4;
288
289   /**
290    * cURL Multihandle
291    */
292   CURLM *curl_multi_handle;
293
294   /**
295    * curl perform task
296    */
297   GNUNET_SCHEDULER_TaskIdentifier client_perform_task;
298 };
299
300
301 /**
302  * Encapsulation of all of the state of the plugin.
303  */
304 struct HTTP_Client_Plugin *p;
305
306
307 /**
308  * Start session timeout for a session
309  * @param s the session
310  */
311 static void
312 client_start_session_timeout (struct Session *s);
313
314
315 /**
316  * Increment session timeout due to activity for a session
317  * @param s the session
318  */
319 static void
320 client_reschedule_session_timeout (struct Session *s);
321
322
323 /**
324  * Cancel timeout for a session
325  * @param s the session
326  */
327 static void
328 client_stop_session_timeout (struct Session *s);
329
330
331 /**
332  * Function setting up file descriptors and scheduling task to run
333  *
334  * @param  plugin plugin as closure
335  * @param now schedule task in 1ms, regardless of what curl may say
336  * @return GNUNET_SYSERR for hard failure, GNUNET_OK for ok
337  */
338 static int
339 client_schedule (struct HTTP_Client_Plugin *plugin, int now);
340
341 static int
342 client_connect_put (struct Session *s);
343
344 /**
345  * Does a session s exists?
346  *
347  * @param plugin the plugin
348  * @param s desired session
349  * @return GNUNET_YES or GNUNET_NO
350  */
351 static int
352 client_exist_session (struct HTTP_Client_Plugin *plugin, struct Session *s)
353 {
354   struct Session * head;
355
356   GNUNET_assert (NULL != plugin);
357   GNUNET_assert (NULL != s);
358
359   for (head = plugin->head; head != NULL; head = head->next)
360   {
361     if (head == s)
362       return GNUNET_YES;
363   }
364   return GNUNET_NO;
365 }
366
367 static void
368 client_remark_outbound_overhead (void *cls, size_t size)
369 {
370   //FIXME use this overhead
371   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-http_client",
372                    "Overhead: %u\n", size);
373 }
374
375 /**
376  * Function to log curl debug messages with GNUNET_log
377  *
378  * @param curl handle
379  * @param type curl_infotype
380  * @param data data
381  * @param size size
382  * @param cls  closure
383  * @return 0
384  */
385 static int
386 client_log (CURL * curl, curl_infotype type, char *data, size_t size, void *cls)
387 {
388   char *ttype = "UNSPECIFIED";
389   if ((type == CURLINFO_TEXT) || (type == CURLINFO_HEADER_IN) || (type == CURLINFO_HEADER_OUT))
390   {
391     char text[size + 2];
392
393     switch (type) {
394       case CURLINFO_TEXT:
395         ttype = "TEXT";
396         break;
397       case CURLINFO_HEADER_IN:
398         ttype = "HEADER_IN";
399         break;
400       case CURLINFO_HEADER_OUT:
401         ttype = "HEADER_OUT";
402         client_remark_outbound_overhead (cls, size);
403         break;
404       default:
405         ttype = "UNSPECIFIED";
406         break;
407     }
408 #if VERBOSE_CURL
409     memcpy (text, data, size);
410     if (text[size - 1] == '\n')
411       text[size] = '\0';
412     else
413     {
414       text[size] = '\n';
415       text[size + 1] = '\0';
416     }
417 #if BUILD_HTTPS
418     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-https_client",
419                      "Connection %p %s: %s", cls, ttype, text);
420 #else
421     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-http_client",
422                      "Connection %p %s: %s", cls, ttype, text);
423 #endif
424   }
425 #endif
426   return 0;
427 }
428
429
430
431 /**
432  * Function that can be used by the transport service to transmit
433  * a message using the plugin.   Note that in the case of a
434  * peer disconnecting, the continuation MUST be called
435  * prior to the disconnect notification itself.  This function
436  * will be called with this peer's HELLO message to initiate
437  * a fresh connection to another peer.
438  *
439  * @param cls closure
440  * @param s which session must be used
441  * @param msgbuf the message to transmit
442  * @param msgbuf_size number of bytes in 'msgbuf'
443  * @param priority how important is the message (most plugins will
444  *                 ignore message priority and just FIFO)
445  * @param to how long to wait at most for the transmission (does not
446  *                require plugins to discard the message after the timeout,
447  *                just advisory for the desired delay; most plugins will ignore
448  *                this as well)
449  * @param cont continuation to call once the message has
450  *        been transmitted (or if the transport is ready
451  *        for the next transmission call; or if the
452  *        peer disconnected...); can be NULL
453  * @param cont_cls closure for cont
454  * @return number of bytes used (on the physical network, with overheads);
455  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
456  *         and does NOT mean that the message was not transmitted (DV)
457  */
458 static ssize_t
459 http_client_plugin_send (void *cls,
460                   struct Session *s,
461                   const char *msgbuf, size_t msgbuf_size,
462                   unsigned int priority,
463                   struct GNUNET_TIME_Relative to,
464                   GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
465 {
466   struct HTTP_Client_Plugin *plugin = cls;
467   struct HTTP_Message *msg;
468   char *stat_txt;
469
470   GNUNET_assert (plugin != NULL);
471   GNUNET_assert (s != NULL);
472
473   /* lookup if session is really existing */
474   if (GNUNET_YES != client_exist_session (plugin, s))
475   {
476     GNUNET_break (0);
477     return GNUNET_SYSERR;
478   }
479
480   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
481                    "Session %p/connection %p: Sending message with %u to peer `%s' \n",
482                    s, s->client_put,
483                    msgbuf_size, GNUNET_i2s (&s->target));
484
485   /* create new message and schedule */
486   msg = GNUNET_malloc (sizeof (struct HTTP_Message) + msgbuf_size);
487   msg->next = NULL;
488   msg->size = msgbuf_size;
489   msg->pos = 0;
490   msg->buf = (char *) &msg[1];
491   msg->transmit_cont = cont;
492   msg->transmit_cont_cls = cont_cls;
493   memcpy (msg->buf, msgbuf, msgbuf_size);
494   GNUNET_CONTAINER_DLL_insert_tail (s->msg_head, s->msg_tail, msg);
495
496   GNUNET_asprintf (&stat_txt, "# bytes currently in %s_client buffers", plugin->protocol);
497   GNUNET_STATISTICS_update (plugin->env->stats,
498                             stat_txt, msgbuf_size, GNUNET_NO);
499
500   GNUNET_free (stat_txt);
501
502   if (GNUNET_YES == s->put_tmp_disconnecting)
503   {
504     /* PUT connection is currently getting disconnected */
505     s->put_reconnect_required = GNUNET_YES;
506     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
507                      "Session %p/connection %jp: currently disconnecting, reconnecting immediately\n",
508                      s, s->client_put);
509     return msgbuf_size;
510   }
511   else if (GNUNET_YES == s->put_paused)
512   {
513     /* PUT connection was paused, unpause */
514     GNUNET_assert (s->put_disconnect_task != GNUNET_SCHEDULER_NO_TASK);
515     GNUNET_SCHEDULER_cancel (s->put_disconnect_task);
516     s->put_disconnect_task = GNUNET_SCHEDULER_NO_TASK;
517     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
518                      "Session %p/connection %p: unpausing connection\n",
519                      s, s->client_put);
520     s->put_paused = GNUNET_NO;
521     curl_easy_pause (s->client_put, CURLPAUSE_CONT);
522   }
523   else if (GNUNET_YES == s->put_tmp_disconnected)
524   {
525     /* PUT connection was disconnected, reconnect */
526     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
527                      "Session %p: Reconnecting PUT connection\n",
528                      s);
529     s->put_tmp_disconnected = GNUNET_NO;
530     GNUNET_break (s->client_put == NULL);
531     if (GNUNET_SYSERR == client_connect_put (s))
532     {
533       return GNUNET_SYSERR;
534     }
535   }
536
537   client_schedule (s->plugin, GNUNET_YES);
538   client_reschedule_session_timeout (s);
539   return msgbuf_size;
540 }
541
542
543 /**
544  * Delete session s
545  *
546  * @param s the session to delete
547  */
548 static void
549 client_delete_session (struct Session *s)
550 {
551   struct HTTP_Client_Plugin *plugin = s->plugin;
552   struct HTTP_Message *pos;
553   struct HTTP_Message *next;
554
555   client_stop_session_timeout (s);
556
557   if (GNUNET_SCHEDULER_NO_TASK != s->put_disconnect_task)
558   {
559       GNUNET_SCHEDULER_cancel (s->put_disconnect_task);
560       s->put_disconnect_task = GNUNET_SCHEDULER_NO_TASK;
561   }
562
563   GNUNET_CONTAINER_DLL_remove (plugin->head, plugin->tail, s);
564
565   next = s->msg_head;
566   while (NULL != (pos = next))
567   {
568     next = pos->next;
569     GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, pos);
570     if (pos->transmit_cont != NULL)
571       pos->transmit_cont (pos->transmit_cont_cls, &s->target, GNUNET_SYSERR,
572                           pos->size, pos->pos + pos->overhead);
573     GNUNET_free (pos);
574   }
575
576   if (s->msg_tk != NULL)
577   {
578     GNUNET_SERVER_mst_destroy (s->msg_tk);
579     s->msg_tk = NULL;
580   }
581   GNUNET_free (s->addr);
582   GNUNET_free (s->url);
583   GNUNET_free (s);
584 }
585
586
587
588 /**
589  * Disconnect a session
590  *
591  * @param s session
592  * @return GNUNET_OK on success, GNUNET_SYSERR on error
593  */
594 static int
595 client_disconnect (struct Session *s)
596 {
597   struct HTTP_Client_Plugin *plugin = s->plugin;
598   struct HTTP_Message *msg;
599   struct HTTP_Message *t;
600   int res = GNUNET_OK;
601   CURLMcode mret;
602
603   if (GNUNET_YES != client_exist_session (plugin, s))
604   {
605     GNUNET_break (0);
606     return GNUNET_SYSERR;
607   }
608
609   if (s->client_put != NULL)
610   {
611     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
612                      "Session %p/connection %p: disconnecting PUT connection to peer `%s'\n",
613                      s, s->client_put, GNUNET_i2s (&s->target));
614
615     /* remove curl handle from multi handle */
616     mret = curl_multi_remove_handle (plugin->curl_multi_handle, s->client_put);
617     if (mret != CURLM_OK)
618     {
619       /* clean up easy handle, handle is now invalid and free'd */
620       res = GNUNET_SYSERR;
621       GNUNET_break (0);
622     }
623     curl_easy_cleanup (s->client_put);
624     s->client_put = NULL;
625   }
626
627
628   if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK)
629   {
630     GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
631     s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK;
632   }
633
634   if (s->client_get != NULL)
635   {
636       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
637                        "Session %p/connection %p: disconnecting GET connection to peer `%s'\n",
638                        s, s->client_get, GNUNET_i2s (&s->target));
639
640     /* remove curl handle from multi handle */
641     mret = curl_multi_remove_handle (plugin->curl_multi_handle, s->client_get);
642     if (mret != CURLM_OK)
643     {
644       /* clean up easy handle, handle is now invalid and free'd */
645       res = GNUNET_SYSERR;
646       GNUNET_break (0);
647     }
648     curl_easy_cleanup (s->client_get);
649     s->client_get = NULL;
650   }
651
652   msg = s->msg_head;
653   while (msg != NULL)
654   {
655     t = msg->next;
656     if (NULL != msg->transmit_cont)
657       msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR,
658                           msg->size, msg->pos + msg->overhead);
659     GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg);
660     GNUNET_free (msg);
661     msg = t;
662   }
663
664   GNUNET_assert (plugin->cur_connections >= 2);
665   plugin->cur_connections -= 2;
666   GNUNET_STATISTICS_set (plugin->env->stats,
667       "# HTTP client sessions",
668       plugin->cur_connections,
669       GNUNET_NO);
670
671   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
672                    "Session %p: notifying transport about ending session\n",s);
673
674   plugin->env->session_end (plugin->env->cls, &s->target, s);
675   client_delete_session (s);
676
677   /* Re-schedule since handles have changed */
678   if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
679   {
680     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
681     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
682   }
683   client_schedule (plugin, GNUNET_YES);
684
685   return res;
686 }
687
688
689 /**
690  * Function that can be used to force the plugin to disconnect
691  * from the given peer and cancel all previous transmissions
692  * (and their continuationc).
693  *
694  * @param cls closure
695  * @param target peer from which to disconnect
696  */
697 static void
698 http_client_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *target)
699 {
700   struct HTTP_Client_Plugin *plugin = cls;
701   struct Session *next = NULL;
702   struct Session *pos = NULL;
703
704   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
705                    "Transport tells me to disconnect `%s'\n",
706                    GNUNET_i2s (target));
707
708   next = plugin->head;
709   while (NULL != (pos = next))
710   {
711     next = pos->next;
712     if (0 == memcmp (target, &pos->target, sizeof (struct GNUNET_PeerIdentity)))
713     {
714       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
715                        "Disconnecting session %p to `%pos'\n",
716                        pos, GNUNET_i2s (target));
717       GNUNET_assert (GNUNET_OK == client_disconnect (pos));
718     }
719   }
720
721 }
722
723
724 static struct Session *
725 client_lookup_session (struct HTTP_Client_Plugin *plugin,
726                        const struct GNUNET_HELLO_Address *address)
727 {
728   struct Session *pos;
729
730   for (pos = plugin->head; NULL != pos; pos = pos->next)
731     if ((0 == memcmp (&address->peer, &pos->target, sizeof (struct GNUNET_PeerIdentity))) &&
732         (address->address_length == pos->addrlen) &&
733         (0 == memcmp (address->address, pos->addr, pos->addrlen)))
734       return pos;
735   return NULL;
736 }
737
738 static void
739 client_put_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
740 {
741   struct Session *s = cls;
742   s->put_disconnect_task = GNUNET_SCHEDULER_NO_TASK;
743   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
744                    "Session %p/connection %p: will be disconnected due to no activity\n",
745                    s, s->client_put);
746   s->put_paused = GNUNET_NO;
747   s->put_tmp_disconnecting = GNUNET_YES;
748   curl_easy_pause (s->client_put, CURLPAUSE_CONT);
749   client_schedule (s->plugin, GNUNET_YES);
750 }
751
752
753
754 /**
755  * Callback method used with libcurl
756  * Method is called when libcurl needs to read data during sending
757  *
758  * @param stream pointer where to write data
759  * @param size size of an individual element
760  * @param nmemb count of elements that can be written to the buffer
761  * @param cls source pointer, passed to the libcurl handle
762  * @return bytes written to stream, returning 0 will terminate connection!
763  */
764 static size_t
765 client_send_cb (void *stream, size_t size, size_t nmemb, void *cls)
766 {
767   struct Session *s = cls;
768   struct HTTP_Client_Plugin *plugin = s->plugin;
769   struct HTTP_Message *msg = s->msg_head;
770   size_t len;
771   char *stat_txt;
772
773   if (GNUNET_YES != client_exist_session (plugin, s))
774   {
775     GNUNET_break (0);
776     return 0;
777   }
778   if (GNUNET_YES == s->put_tmp_disconnecting)
779   {
780
781       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
782                        "Session %p/connection %p: disconnect due to inactivity\n",
783                        s, s->client_put);
784       return 0;
785   }
786
787   if (NULL == msg)
788   {
789     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
790                      "Session %p/connection %p: nothing to send, suspending\n",
791                      s, s->client_put);
792     s->put_disconnect_task = GNUNET_SCHEDULER_add_delayed (PUT_DISCONNECT_TIMEOUT, &client_put_disconnect, s);
793     s->put_paused = GNUNET_YES;
794     return CURL_READFUNC_PAUSE;
795   }
796   /* data to send */
797   GNUNET_assert (msg->pos < msg->size);
798   /* calculate how much fits in buffer */
799   len = GNUNET_MIN (msg->size - msg->pos,
800                     size * nmemb);
801   memcpy (stream, &msg->buf[msg->pos], len);
802   msg->pos += len;
803   if (msg->pos == msg->size)
804   {
805     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
806                      "Session %p/connection %p: sent message with %u bytes sent, removing message from queue\n",
807                      s, s->client_put, msg->size, msg->pos);
808     /* Calling transmit continuation  */
809     GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg);
810     if (NULL != msg->transmit_cont)
811       msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_OK,
812                           msg->size, msg->size + msg->overhead);
813     GNUNET_free (msg);
814   }
815
816   GNUNET_asprintf (&stat_txt, "# bytes currently in %s_client buffers", plugin->protocol);
817   GNUNET_STATISTICS_update (plugin->env->stats,
818                             stat_txt, -len, GNUNET_NO);
819   GNUNET_free (stat_txt);
820
821   GNUNET_asprintf (&stat_txt, "# bytes transmitted via %s_client", plugin->protocol);
822   GNUNET_STATISTICS_update (plugin->env->stats,
823                             stat_txt, len, GNUNET_NO);
824   GNUNET_free (stat_txt);
825
826   client_reschedule_session_timeout (s);
827   return len;
828 }
829
830
831 /**
832  * Wake up a curl handle which was suspended
833  *
834  * @param cls the session
835  * @param tc task context
836  */
837 static void
838 client_wake_up (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
839 {
840   struct Session *s = cls;
841
842   if (GNUNET_YES != client_exist_session(p, s))
843   {
844     GNUNET_break (0);
845     return;
846   }
847   s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK;
848   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
849     return;
850   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
851                    "Session %p/connection %p: Waking up GET handle\n", s, s->client_get);
852   if (s->client_get != NULL)
853     curl_easy_pause (s->client_get, CURLPAUSE_CONT);
854 }
855
856
857 /**
858  * Callback for message stream tokenizer
859  *
860  * @param cls the session
861  * @param client not used
862  * @param message the message received
863  * @return always GNUNET_OK
864  */
865 static int
866 client_receive_mst_cb (void *cls, void *client,
867                        const struct GNUNET_MessageHeader *message)
868 {
869   struct Session *s = cls;
870   struct HTTP_Client_Plugin *plugin;
871   struct GNUNET_TIME_Relative delay;
872   struct GNUNET_ATS_Information atsi[2];
873   char *stat_txt;
874   if (GNUNET_YES != client_exist_session(p, s))
875   {
876     GNUNET_break (0);
877     return GNUNET_OK;
878   }
879   plugin = s->plugin;
880
881   atsi[0].type = htonl (GNUNET_ATS_QUALITY_NET_DISTANCE);
882   atsi[0].value = htonl (1);
883   atsi[1].type = htonl (GNUNET_ATS_NETWORK_TYPE);
884   atsi[1].value = s->ats_address_network_type;
885   GNUNET_break (s->ats_address_network_type != ntohl (GNUNET_ATS_NET_UNSPECIFIED));
886
887   delay = s->plugin->env->receive (plugin->env->cls, &s->target, message,
888                                    (const struct GNUNET_ATS_Information *) &atsi, 2,
889                                    s, s->addr, s->addrlen);
890
891   GNUNET_asprintf (&stat_txt, "# bytes received via %s_client", plugin->protocol);
892   GNUNET_STATISTICS_update (plugin->env->stats,
893                             stat_txt, ntohs(message->size), GNUNET_NO);
894   GNUNET_free (stat_txt);
895
896   s->next_receive =
897       GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), delay);
898
899   if (GNUNET_TIME_absolute_get ().abs_value < s->next_receive.abs_value)
900   {
901
902     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
903                      "Client: peer `%s' address `%s' next read delayed for %llu ms\n",
904                      GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen),
905                      delay);
906   }
907   client_reschedule_session_timeout (s);
908   return GNUNET_OK;
909 }
910
911
912 /**
913  * Callback method used with libcurl when data for a PUT connection are
914  * received. We do not expect data here, so we just dismiss it
915  *
916  * @param stream pointer where to write data
917  * @param size size of an individual element
918  * @param nmemb count of elements that can be written to the buffer
919  * @param cls destination pointer, passed to the libcurl handle
920  * @return bytes read from stream
921  */
922 static size_t
923 client_receive_put (void *stream, size_t size, size_t nmemb, void *cls)
924 {
925   return size * nmemb;
926 }
927
928
929 /**
930  * Callback method used with libcurl when data for a GET connection are
931  * received. Forward to MST
932  *
933  * @param stream pointer where to write data
934  * @param size size of an individual element
935  * @param nmemb count of elements that can be written to the buffer
936  * @param cls destination pointer, passed to the libcurl handle
937  * @return bytes read from stream
938  */
939 static size_t
940 client_receive (void *stream, size_t size, size_t nmemb, void *cls)
941 {
942   struct Session *s = cls;
943   struct GNUNET_TIME_Absolute now;
944   size_t len = size * nmemb;
945
946   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
947                    "Session %p / connection %p: Received %u bytes from peer `%s'\n",
948                    s, s->client_get,
949                    len, GNUNET_i2s (&s->target));
950   now = GNUNET_TIME_absolute_get ();
951   if (now.abs_value < s->next_receive.abs_value)
952   {
953     struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
954     struct GNUNET_TIME_Relative delta =
955         GNUNET_TIME_absolute_get_difference (now, s->next_receive);
956     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
957                      "Session %p / connection %p: No inbound bandwidth available! Next read was delayed for %llu ms\n",
958                      s, s->client_get, delta.rel_value);
959     if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK)
960     {
961       GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
962       s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK;
963     }
964     s->recv_wakeup_task =
965         GNUNET_SCHEDULER_add_delayed (delta, &client_wake_up, s);
966     return CURL_WRITEFUNC_PAUSE;
967   }
968   if (NULL == s->msg_tk)
969     s->msg_tk = GNUNET_SERVER_mst_create (&client_receive_mst_cb, s);
970   GNUNET_SERVER_mst_receive (s->msg_tk, s, stream, len, GNUNET_NO, GNUNET_NO);
971   return len;
972 }
973
974
975 /**
976  * Task performing curl operations
977  *
978  * @param cls plugin as closure
979  * @param tc gnunet scheduler task context
980  */
981 static void
982 client_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
983
984
985 /**
986  * Function setting up file descriptors and scheduling task to run
987  *
988  * @param  plugin plugin as closure
989  * @param now schedule task in 1ms, regardless of what curl may say
990  * @return GNUNET_SYSERR for hard failure, GNUNET_OK for ok
991  */
992 static int
993 client_schedule (struct HTTP_Client_Plugin *plugin, int now)
994 {
995   fd_set rs;
996   fd_set ws;
997   fd_set es;
998   int max;
999   struct GNUNET_NETWORK_FDSet *grs;
1000   struct GNUNET_NETWORK_FDSet *gws;
1001   long to;
1002   CURLMcode mret;
1003   struct GNUNET_TIME_Relative timeout;
1004
1005   /* Cancel previous scheduled task */
1006   if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
1007   {
1008     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
1009     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
1010   }
1011   max = -1;
1012   FD_ZERO (&rs);
1013   FD_ZERO (&ws);
1014   FD_ZERO (&es);
1015   mret = curl_multi_fdset (plugin->curl_multi_handle, &rs, &ws, &es, &max);
1016   if (mret != CURLM_OK)
1017   {
1018     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
1019                 "curl_multi_fdset", __FILE__, __LINE__,
1020                 curl_multi_strerror (mret));
1021     return GNUNET_SYSERR;
1022   }
1023   mret = curl_multi_timeout (plugin->curl_multi_handle, &to);
1024   if (to == -1)
1025     timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1);
1026   else
1027     timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
1028   if (now == GNUNET_YES)
1029     timeout = GNUNET_TIME_UNIT_MILLISECONDS;
1030
1031   if (mret != CURLM_OK)
1032   {
1033     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
1034                 "curl_multi_timeout", __FILE__, __LINE__,
1035                 curl_multi_strerror (mret));
1036     return GNUNET_SYSERR;
1037   }
1038
1039   grs = GNUNET_NETWORK_fdset_create ();
1040   gws = GNUNET_NETWORK_fdset_create ();
1041   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
1042   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
1043
1044   plugin->client_perform_task =
1045       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
1046                                    timeout, grs, gws,
1047                                    &client_run, plugin);
1048   GNUNET_NETWORK_fdset_destroy (gws);
1049   GNUNET_NETWORK_fdset_destroy (grs);
1050   return GNUNET_OK;
1051 }
1052
1053
1054 /**
1055  * Task performing curl operations
1056  *
1057  * @param cls plugin as closure
1058  * @param tc gnunet scheduler task context
1059  */
1060 static void
1061 client_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1062 {
1063   struct HTTP_Client_Plugin *plugin = cls;
1064   int running;
1065   long http_statuscode;
1066   CURLMcode mret;
1067
1068   GNUNET_assert (cls != NULL);
1069
1070   plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
1071   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1072     return;
1073
1074   do
1075   {
1076     running = 0;
1077     mret = curl_multi_perform (plugin->curl_multi_handle, &running);
1078
1079     CURLMsg *msg;
1080     int msgs_left;
1081
1082     while ((msg = curl_multi_info_read (plugin->curl_multi_handle, &msgs_left)))
1083     {
1084       CURL *easy_h = msg->easy_handle;
1085       struct Session *s = NULL;
1086       char *d = (char *) s;
1087
1088       if (easy_h == NULL)
1089       {
1090         GNUNET_break (0);
1091         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1092                          "Client: connection to ended with reason %i: `%s', %i handles running\n",
1093                          msg->data.result,
1094                          curl_easy_strerror (msg->data.result), running);
1095         continue;
1096       }
1097
1098       GNUNET_assert (CURLE_OK ==
1099                      curl_easy_getinfo (easy_h, CURLINFO_PRIVATE, &d));
1100       s = (struct Session *) d;
1101
1102       if (GNUNET_YES != client_exist_session(plugin, s))
1103       {
1104         GNUNET_break (0);
1105         return;
1106       }
1107
1108       GNUNET_assert (s != NULL);
1109       if (msg->msg == CURLMSG_DONE)
1110       {
1111         curl_easy_getinfo (easy_h, CURLINFO_RESPONSE_CODE, &http_statuscode);
1112         if (easy_h == s->client_put)
1113         {
1114             if  ((0 != msg->data.result) || (http_statuscode != 200))
1115             {
1116                 GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1117                   "Session %p/connection %p: PUT connection to `%s' ended with status %i reason %i: `%s'\n",
1118                   s, msg->easy_handle, GNUNET_i2s (&s->target),
1119                   http_statuscode,
1120                   msg->data.result,
1121                   curl_easy_strerror (msg->data.result));
1122             }
1123             else
1124               GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1125                 "Session %p/connection %p: PUT connection to `%s' ended normal\n",
1126                 s, msg->easy_handle, GNUNET_i2s (&s->target));
1127             if (s->client_get == NULL)
1128             {
1129               /* Disconnect other transmission direction and tell transport */
1130             }
1131             curl_multi_remove_handle (plugin->curl_multi_handle, easy_h);
1132             curl_easy_cleanup (easy_h);
1133             s->put_tmp_disconnecting = GNUNET_NO;
1134             s->put_tmp_disconnected = GNUNET_YES;
1135             s->client_put = NULL;
1136
1137             /*
1138              * Handling a rare case:
1139              * plugin_send was called during temporary put disconnect,
1140              * reconnect required after connection was disconnected
1141              */
1142             if (GNUNET_YES == s->put_reconnect_required)
1143             {
1144                 s->put_reconnect_required = GNUNET_NO;
1145                 client_connect_put(s);
1146             }
1147         }
1148         if (easy_h == s->client_get)
1149         {
1150             if  ((0 != msg->data.result) || (http_statuscode != 200))
1151             {
1152               GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1153                 "Session %p/connection %p: GET connection to `%s' ended with status %i reason %i: `%s'\n",
1154                 s, msg->easy_handle, GNUNET_i2s (&s->target),
1155                 http_statuscode,
1156                 msg->data.result,
1157                 curl_easy_strerror (msg->data.result));
1158
1159             }
1160             else
1161               GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1162                 "Session %p/connection %p: GET connection to `%s' ended normal\n",
1163                 s, msg->easy_handle, GNUNET_i2s (&s->target));
1164             /* Disconnect other transmission direction and tell transport */
1165             client_disconnect (s);
1166         }
1167       }
1168     }
1169   }
1170   while (mret == CURLM_CALL_MULTI_PERFORM);
1171   client_schedule (plugin, GNUNET_NO);
1172 }
1173
1174 static int
1175 client_connect_get (struct Session *s)
1176 {
1177   CURLMcode mret;
1178   /* create get connection */
1179   s->client_get = curl_easy_init ();
1180 #if VERBOSE_CURL
1181   curl_easy_setopt (s->client_get, CURLOPT_VERBOSE, 1L);
1182   curl_easy_setopt (s->client_get, CURLOPT_DEBUGFUNCTION, &client_log);
1183   curl_easy_setopt (s->client_get, CURLOPT_DEBUGDATA, s->client_get);
1184 #endif
1185 #if BUILD_HTTPS
1186   curl_easy_setopt (s->client_get, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
1187   curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYPEER, 0);
1188   curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYHOST, 0);
1189 #endif
1190   curl_easy_setopt (s->client_get, CURLOPT_URL, s->url);
1191   //curl_easy_setopt (s->client_get, CURLOPT_HEADERFUNCTION, &curl_get_header_cb);
1192   //curl_easy_setopt (s->client_get, CURLOPT_WRITEHEADER, ps);
1193   curl_easy_setopt (s->client_get, CURLOPT_READFUNCTION, client_send_cb);
1194   curl_easy_setopt (s->client_get, CURLOPT_READDATA, s);
1195   curl_easy_setopt (s->client_get, CURLOPT_WRITEFUNCTION, client_receive);
1196   curl_easy_setopt (s->client_get, CURLOPT_WRITEDATA, s);
1197   /* No timeout by default, timeout done with session timeout */
1198   curl_easy_setopt (s->client_get, CURLOPT_TIMEOUT, 0);
1199   curl_easy_setopt (s->client_get, CURLOPT_PRIVATE, s);
1200   curl_easy_setopt (s->client_get, CURLOPT_CONNECTTIMEOUT_MS,
1201                     (long) HTTP_CLIENT_NOT_VALIDATED_TIMEOUT.rel_value);
1202   curl_easy_setopt (s->client_get, CURLOPT_BUFFERSIZE,
1203                     2 * GNUNET_SERVER_MAX_MESSAGE_SIZE);
1204 #if CURL_TCP_NODELAY
1205   curl_easy_setopt (ps->recv_endpoint, CURLOPT_TCP_NODELAY, 1);
1206 #endif
1207   mret = curl_multi_add_handle (s->plugin->curl_multi_handle, s->client_get);
1208   if (mret != CURLM_OK)
1209   {
1210       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, s->plugin->name,
1211                        "Session %p : Failed to add GET handle to multihandle: `%s'\n",
1212                        s, curl_multi_strerror (mret));
1213     curl_easy_cleanup (s->client_get);
1214     s->client_get = NULL;
1215     GNUNET_break (0);
1216     return GNUNET_SYSERR;
1217   }
1218
1219   return GNUNET_OK;
1220 }
1221
1222 static int
1223 client_connect_put (struct Session *s)
1224 {
1225   CURLMcode mret;
1226   /* create put connection */
1227   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
1228                        "Session %p : Init PUT handle \n", s);
1229   s->client_put = curl_easy_init ();
1230 #if VERBOSE_CURL
1231   curl_easy_setopt (s->client_put, CURLOPT_VERBOSE, 1L);
1232   curl_easy_setopt (s->client_put, CURLOPT_DEBUGFUNCTION, &client_log);
1233   curl_easy_setopt (s->client_put, CURLOPT_DEBUGDATA, s->client_put);
1234 #endif
1235 #if BUILD_HTTPS
1236   curl_easy_setopt (s->client_put, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
1237   curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYPEER, 0);
1238   curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYHOST, 0);
1239 #endif
1240   curl_easy_setopt (s->client_put, CURLOPT_URL, s->url);
1241   curl_easy_setopt (s->client_put, CURLOPT_UPLOAD, 1L);
1242   //curl_easy_setopt (s->client_put, CURLOPT_HEADERFUNCTION, &client_curl_header);
1243   //curl_easy_setopt (s->client_put, CURLOPT_WRITEHEADER, ps);
1244   curl_easy_setopt (s->client_put, CURLOPT_READFUNCTION, client_send_cb);
1245   curl_easy_setopt (s->client_put, CURLOPT_READDATA, s);
1246   curl_easy_setopt (s->client_put, CURLOPT_WRITEFUNCTION, client_receive_put);
1247   curl_easy_setopt (s->client_put, CURLOPT_WRITEDATA, s);
1248   /* No timeout by default, timeout done with session timeout */
1249   curl_easy_setopt (s->client_put, CURLOPT_TIMEOUT, 0);
1250   curl_easy_setopt (s->client_put, CURLOPT_PRIVATE, s);
1251   curl_easy_setopt (s->client_put, CURLOPT_CONNECTTIMEOUT_MS,
1252                     (long) HTTP_CLIENT_NOT_VALIDATED_TIMEOUT.rel_value);
1253   curl_easy_setopt (s->client_put, CURLOPT_BUFFERSIZE,
1254                     2 * GNUNET_SERVER_MAX_MESSAGE_SIZE);
1255 #if CURL_TCP_NODELAY
1256   curl_easy_setopt (s->client_put, CURLOPT_TCP_NODELAY, 1);
1257 #endif
1258   mret = curl_multi_add_handle (s->plugin->curl_multi_handle, s->client_put);
1259   if (mret != CURLM_OK)
1260   {
1261    GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, s->plugin->name,
1262                     "Session %p : Failed to add PUT handle to multihandle: `%s'\n",
1263                     s, curl_multi_strerror (mret));
1264     curl_easy_cleanup (s->client_put);
1265     s->client_put = NULL;
1266     return GNUNET_SYSERR;
1267   }
1268   return GNUNET_OK;
1269 }
1270
1271 static int
1272 client_connect (struct Session *s)
1273 {
1274
1275   struct HTTP_Client_Plugin *plugin = s->plugin;
1276   int res = GNUNET_OK;
1277
1278
1279   /* create url */
1280   if (NULL == http_common_plugin_address_to_string (NULL, s->addr, s->addrlen))
1281   {
1282     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1283                      "Invalid address peer `%s'\n",
1284                      GNUNET_i2s (&s->target));
1285     return GNUNET_SYSERR;
1286   }
1287
1288   GNUNET_asprintf (&s->url, "%s/%s;%u",
1289       http_common_plugin_address_to_string (plugin, s->addr, s->addrlen),
1290                    GNUNET_h2s_full (&plugin->env->my_identity->hashPubKey),
1291                    plugin->last_tag);
1292
1293   plugin->last_tag++;
1294
1295   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1296                    "Initiating outbound session peer `%s' using address `%s'\n",
1297                    GNUNET_i2s (&s->target), s->url);
1298
1299   if ((GNUNET_SYSERR == client_connect_get (s)) ||
1300       (GNUNET_SYSERR == client_connect_put (s)))
1301   {
1302       GNUNET_break (0);
1303       return GNUNET_SYSERR;
1304   }
1305
1306   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1307                "Session %p: connected with connections GET %p and PUT %p\n",
1308                s, s->client_get, s->client_put);
1309
1310   /* Perform connect */
1311   plugin->cur_connections += 2;
1312   GNUNET_STATISTICS_set (plugin->env->stats,
1313       "# HTTP client connections",
1314       plugin->cur_connections,
1315       GNUNET_NO);
1316
1317   /* Re-schedule since handles have changed */
1318   if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
1319   {
1320     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
1321     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
1322   }
1323   plugin->client_perform_task = GNUNET_SCHEDULER_add_now (client_run, plugin);
1324   return res;
1325 }
1326
1327
1328 /**
1329  * Creates a new outbound session the transport service will use to send data to the
1330  * peer
1331  *
1332  * @param cls the plugin
1333  * @param address the address
1334  * @return the session or NULL of max connections exceeded
1335  */
1336 static struct Session *
1337 http_client_plugin_get_session (void *cls,
1338                   const struct GNUNET_HELLO_Address *address)
1339 {
1340   struct HTTP_Client_Plugin *plugin = cls;
1341   struct Session * s = NULL;
1342   struct sockaddr *sa;
1343   struct GNUNET_ATS_Information ats;
1344   size_t salen = 0;
1345   int res;
1346
1347   GNUNET_assert (plugin != NULL);
1348   GNUNET_assert (address != NULL);
1349   GNUNET_assert (address->address != NULL);
1350
1351   /* find existing session */
1352   s = client_lookup_session (plugin, address);
1353   if (s != NULL)
1354     return s;
1355
1356   if (plugin->max_connections <= plugin->cur_connections)
1357   {
1358     GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name,
1359                      "Maximum number of connections (%u) reached: "
1360                      "cannot connect to peer `%s'\n",
1361                      plugin->max_connections,
1362                      GNUNET_i2s (&address->peer));
1363     return NULL;
1364   }
1365
1366   ats.type = htonl (GNUNET_ATS_NETWORK_TYPE);
1367   ats.value = htonl (GNUNET_ATS_NET_UNSPECIFIED);
1368   sa = http_common_socket_from_address (address->address, address->address_length, &res);
1369
1370   if (GNUNET_SYSERR == res)
1371   {
1372       return NULL;
1373   }
1374   else if (GNUNET_YES == res)
1375   {
1376       GNUNET_assert (NULL != sa);
1377       if (AF_INET == sa->sa_family)
1378       {
1379           salen = sizeof (struct sockaddr_in);
1380       }
1381       else if (AF_INET == sa->sa_family)
1382       {
1383           salen = sizeof (struct sockaddr_in6);
1384       }
1385       ats = plugin->env->get_address_type (plugin->env->cls, sa, salen);
1386       GNUNET_free (sa);
1387   }
1388   else if (GNUNET_NO == res)
1389   {
1390       ats.value = htonl (GNUNET_ATS_COST_WAN);
1391   }
1392
1393   if (GNUNET_ATS_NET_UNSPECIFIED == ntohl(ats.value))
1394   {
1395       GNUNET_break (0);
1396       return NULL;
1397   }
1398
1399   s = GNUNET_malloc (sizeof (struct Session));
1400   memcpy (&s->target, &address->peer, sizeof (struct GNUNET_PeerIdentity));
1401   s->plugin = plugin;
1402   s->addr = GNUNET_malloc (address->address_length);
1403   memcpy (s->addr, address->address, address->address_length);
1404   s->addrlen = address->address_length;
1405   s->ats_address_network_type = ats.value;
1406   s->put_paused = GNUNET_NO;
1407   s->put_tmp_disconnecting = GNUNET_NO;
1408   s->put_tmp_disconnected = GNUNET_NO;
1409   client_start_session_timeout (s);
1410
1411   /* add new session */
1412   GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s);
1413
1414   /* initiate new connection */
1415   if (GNUNET_SYSERR == client_connect (s))
1416   {
1417     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1418                      "Cannot connect to peer `%s' address `%s''\n",
1419                      http_common_plugin_address_to_string (NULL, s->addr, s->addrlen),
1420                      GNUNET_i2s (&s->target));
1421     client_delete_session (s);
1422     return NULL;
1423   }
1424   return s;
1425 }
1426
1427
1428 /**
1429  * Setup http_client plugin
1430  *
1431  * @param plugin the plugin handle
1432  * @return GNUNET_OK on success, GNUNET_SYSERR on error
1433  */
1434 static int
1435 client_start (struct HTTP_Client_Plugin *plugin)
1436 {
1437   curl_global_init (CURL_GLOBAL_ALL);
1438   plugin->curl_multi_handle = curl_multi_init ();
1439
1440   if (NULL == plugin->curl_multi_handle)
1441   {
1442     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1443                      _("Could not initialize curl multi handle, failed to start %s plugin!\n"),
1444                      plugin->name);
1445     return GNUNET_SYSERR;
1446   }
1447   return GNUNET_OK;
1448 }
1449
1450 /**
1451  * Session was idle, so disconnect it
1452  */
1453 static void
1454 client_session_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1455 {
1456   GNUNET_assert (NULL != cls);
1457   struct Session *s = cls;
1458
1459   s->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1460   GNUNET_log (TIMEOUT_LOG,
1461               "Session %p was idle for %llu ms, disconnecting\n",
1462               s, (unsigned long long) CLIENT_SESSION_TIMEOUT.rel_value);
1463
1464   /* call session destroy function */
1465   GNUNET_assert (GNUNET_OK == client_disconnect (s));
1466 }
1467
1468
1469 /**
1470  * Start session timeout for session s
1471  *
1472  * @param s the session
1473  */
1474 static void
1475 client_start_session_timeout (struct Session *s)
1476 {
1477
1478  GNUNET_assert (NULL != s);
1479  GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == s->timeout_task);
1480  s->timeout_task =  GNUNET_SCHEDULER_add_delayed (CLIENT_SESSION_TIMEOUT,
1481                                                   &client_session_timeout,
1482                                                   s);
1483  GNUNET_log (TIMEOUT_LOG,
1484              "Timeout for session %p set to %llu ms\n",
1485              s,  (unsigned long long) CLIENT_SESSION_TIMEOUT.rel_value);
1486 }
1487
1488
1489 /**
1490  * Increment session timeout due to activity for session s
1491  *
1492  * param s the session
1493  */
1494 static void
1495 client_reschedule_session_timeout (struct Session *s)
1496 {
1497
1498  GNUNET_assert (NULL != s);
1499  GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != s->timeout_task);
1500
1501  GNUNET_SCHEDULER_cancel (s->timeout_task);
1502  s->timeout_task =  GNUNET_SCHEDULER_add_delayed (CLIENT_SESSION_TIMEOUT,
1503                                                   &client_session_timeout,
1504                                                   s);
1505  GNUNET_log (TIMEOUT_LOG,
1506              "Timeout rescheduled for session %p set to %llu ms\n",
1507              s, (unsigned long long) CLIENT_SESSION_TIMEOUT.rel_value);
1508 }
1509
1510
1511 /**
1512  * Cancel timeout due to activity for session s
1513  *
1514  * param s the session
1515  */
1516 static void
1517 client_stop_session_timeout (struct Session *s)
1518 {
1519  GNUNET_assert (NULL != s);
1520
1521  if (GNUNET_SCHEDULER_NO_TASK != s->timeout_task)
1522  {
1523    GNUNET_SCHEDULER_cancel (s->timeout_task);
1524    s->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1525    GNUNET_log (TIMEOUT_LOG, "Timeout stopped for session %p\n", s);
1526  }
1527 }
1528
1529
1530 /**
1531  * Another peer has suggested an address for this
1532  * peer and transport plugin.  Check that this could be a valid
1533  * address.  If so, consider adding it to the list
1534  * of addresses.
1535  *
1536  * @param cls closure
1537  * @param addr pointer to the address
1538  * @param addrlen length of addr
1539  * @return GNUNET_OK if this is a plausible address for this peer
1540  *         and transport
1541  */
1542 static int
1543 http_client_plugin_address_suggested (void *cls, const void *addr, size_t addrlen)
1544 {
1545   /* struct Plugin *plugin = cls; */
1546
1547   /* A HTTP/S client does not have any valid address so:*/
1548   return GNUNET_NO;
1549 }
1550
1551
1552 /**
1553  * Exit point from the plugin.
1554  *
1555  * @param cls api as closure
1556  * @return NULL
1557  */
1558 void *
1559 LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls)
1560 {
1561   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
1562   struct HTTP_Client_Plugin *plugin = api->cls;
1563   struct Session *pos;
1564   struct Session *next;
1565
1566   if (NULL == api->cls)
1567   {
1568     /* Stub shutdown */
1569     GNUNET_free (api);
1570     return NULL;
1571   }
1572
1573   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1574                    _("Shutting down plugin `%s'\n"),
1575                    plugin->name);
1576
1577
1578   next = plugin->head;
1579   while (NULL != (pos = next))
1580   {
1581       next = pos->next;
1582       client_disconnect (pos);
1583   }
1584   if (GNUNET_SCHEDULER_NO_TASK != plugin->client_perform_task)
1585   {
1586       GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
1587       plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
1588   }
1589
1590
1591   if (NULL != plugin->curl_multi_handle)
1592   {
1593     curl_multi_cleanup (plugin->curl_multi_handle);
1594     plugin->curl_multi_handle = NULL;
1595   }
1596   curl_global_cleanup ();
1597
1598   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1599                    _("Shutdown for plugin `%s' complete\n"),
1600                    plugin->name);
1601
1602   GNUNET_free (plugin);
1603   GNUNET_free (api);
1604   return NULL;
1605 }
1606
1607
1608 /**
1609  * Configure plugin
1610  *
1611  * @param plugin the plugin handle
1612  * @return GNUNET_OK on success, GNUNET_SYSERR on failure
1613  */
1614 static int
1615 client_configure_plugin (struct HTTP_Client_Plugin *plugin)
1616 {
1617   unsigned long long max_connections;
1618
1619   /* Optional parameters */
1620   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (plugin->env->cfg,
1621                       plugin->name,
1622                       "MAX_CONNECTIONS", &max_connections))
1623     max_connections = 128;
1624   plugin->max_connections = max_connections;
1625
1626   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1627                    _("Maximum number of connections is %u\n"),
1628                    plugin->max_connections);
1629   return GNUNET_OK;
1630 }
1631
1632 /**
1633  * Entry point for the plugin.
1634  */
1635 void *
1636 LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls)
1637 {
1638   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
1639   struct GNUNET_TRANSPORT_PluginFunctions *api;
1640   struct HTTP_Client_Plugin *plugin;
1641
1642   if (NULL == env->receive)
1643   {
1644     /* run in 'stub' mode (i.e. as part of gnunet-peerinfo), don't fully
1645        initialze the plugin or the API */
1646     api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
1647     api->cls = NULL;
1648     api->address_to_string = &http_common_plugin_address_to_string;
1649     api->string_to_address = &http_common_plugin_string_to_address;
1650     api->address_pretty_printer = &http_common_plugin_address_pretty_printer;
1651     return api;
1652   }
1653
1654   plugin = GNUNET_malloc (sizeof (struct HTTP_Client_Plugin));
1655   p = plugin;
1656   plugin->env = env;
1657   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
1658   api->cls = plugin;
1659   api->send = &http_client_plugin_send;
1660   api->disconnect = &http_client_plugin_disconnect;
1661   api->check_address = &http_client_plugin_address_suggested;
1662   api->get_session = &http_client_plugin_get_session;
1663   api->address_to_string = &http_common_plugin_address_to_string;
1664   api->string_to_address = &http_common_plugin_string_to_address;
1665   api->address_pretty_printer = &http_common_plugin_address_pretty_printer;
1666
1667
1668 #if BUILD_HTTPS
1669   plugin->name = "transport-https_client";
1670   plugin->protocol = "https";
1671 #else
1672   plugin->name = "transport-http_client";
1673   plugin->protocol = "http";
1674 #endif
1675   plugin->last_tag = 1;
1676
1677   if (GNUNET_SYSERR == client_configure_plugin (plugin))
1678   {
1679       LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
1680       return NULL;
1681   }
1682
1683   /* Start client */
1684   if (GNUNET_SYSERR == client_start (plugin))
1685   {
1686       LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
1687       return NULL;
1688   }
1689   return api;
1690 }
1691
1692 /* end of plugin_transport_http_client.c */