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