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