changes
[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
36 #define HTTP_NOT_VALIDATED_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 15)
37
38 #include "platform.h"
39 #include "gnunet_protocols.h"
40 #include "gnunet_connection_lib.h"
41 #include "gnunet_server_lib.h"
42 #include "gnunet_service_lib.h"
43 #include "gnunet_statistics_service.h"
44 #include "gnunet_transport_service.h"
45 #include "gnunet_transport_plugin.h"
46 #include "plugin_transport_http_common.h"
47 #include <curl/curl.h>
48
49
50 /**
51  * Encapsulation of all of the state of the plugin.
52  */
53 struct HTTP_Client_Plugin;
54
55
56 /**
57  *  Message to send using http
58  */
59 struct HTTP_Message
60 {
61   /**
62    * next pointer for double linked list
63    */
64   struct HTTP_Message *next;
65
66   /**
67    * previous pointer for double linked list
68    */
69   struct HTTP_Message *prev;
70
71   /**
72    * buffer containing data to send
73    */
74   char *buf;
75
76   /**
77    * amount of data already sent
78    */
79   size_t pos;
80
81   /**
82    * buffer length
83    */
84   size_t size;
85
86   /**
87    * Continuation function to call once the transmission buffer
88    * has again space available.  NULL if there is no
89    * continuation to call.
90    */
91   GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
92
93   /**
94    * Closure for transmit_cont.
95    */
96   void *transmit_cont_cls;
97 };
98
99
100 /**
101  * Session handle for connections.
102  */
103 struct Session
104 {
105   /**
106    * To whom are we talking to (set to our identity
107    * if we are still waiting for the welcome message)
108    */
109   struct GNUNET_PeerIdentity target;
110
111   /**
112    * Stored in a linked list.
113    */
114   struct Session *next;
115
116   /**
117    * Stored in a linked list.
118    */
119   struct Session *prev;
120
121   /**
122    * Address
123    */
124   void *addr;
125
126   /**
127    * Address length
128    */
129   size_t addrlen;
130
131   /**
132    * ATS network type in NBO
133    */
134   uint32_t ats_address_network_type;
135
136   /**
137    * Pointer to the global plugin struct.
138    */
139   struct HTTP_Client_Plugin *plugin;
140
141   /**
142    * Is client send handle paused since there are no data to send?
143    * GNUNET_YES/NO
144    */
145   int client_put_paused;
146
147
148   /**
149    * Client send handle
150    */
151   void *client_put;
152
153   /**
154    * Client receive handle
155    */
156   void *client_get;
157
158   /**
159    * next pointer for double linked list
160    */
161   struct HTTP_Message *msg_head;
162
163   /**
164    * previous pointer for double linked list
165    */
166   struct HTTP_Message *msg_tail;
167
168   /**
169    * Message stream tokenizer for incoming data
170    */
171   struct GNUNET_SERVER_MessageStreamTokenizer *msg_tk;
172
173   /**
174    * Session timeout task
175    */
176   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
177
178   /**
179    * Task to wake up client receive handle when receiving is allowed again
180    */
181   GNUNET_SCHEDULER_TaskIdentifier recv_wakeup_task;
182
183   /**
184   * Absolute time when to receive data again
185   * Used for receive throttling
186   */
187  struct GNUNET_TIME_Absolute next_receive;
188 };
189
190 /**
191  * Encapsulation of all of the state of the plugin.
192  */
193 struct HTTP_Client_Plugin
194 {
195   /**
196    * Our environment.
197    */
198   struct GNUNET_TRANSPORT_PluginEnvironment *env;
199
200   /**
201    * Linked list head of open sessions.
202    */
203   struct Session *head;
204
205   /**
206    * Linked list tail of open sessions.
207    */
208   struct Session *tail;
209
210   /**
211    * Plugin name
212    */
213   char *name;
214
215   /**
216    * Protocol
217    */
218   char *protocol;
219
220   /**
221    * Maximum number of sockets the plugin can use
222    * Each http inbound /outbound connections are two connections
223    */
224   unsigned int max_connections;
225
226   /**
227    * Current number of sockets the plugin can use
228    * Each http inbound /outbound connections are two connections
229    */
230   unsigned int cur_connections;
231
232   /**
233    * Last used unique HTTP connection tag
234    */
235   uint32_t last_tag;
236
237   /**
238    * use IPv6
239    */
240   uint16_t use_ipv6;
241
242   /**
243    * use IPv4
244    */
245   uint16_t use_ipv4;
246
247   /**
248    * cURL Multihandle
249    */
250   CURLM *curl_multi_handle;
251
252   /**
253    * curl perform task
254    */
255   GNUNET_SCHEDULER_TaskIdentifier client_perform_task;
256 };
257
258 /**
259  * Encapsulation of all of the state of the plugin.
260  */
261 struct HTTP_Client_Plugin *p;
262
263 /**
264  * Start session timeout
265  */
266 static void
267 client_start_session_timeout (struct Session *s);
268
269 /**
270  * Increment session timeout due to activity
271  */
272 static void
273 client_reschedule_session_timeout (struct Session *s);
274
275 /**
276  * Cancel timeout
277  */
278 static void
279 client_stop_session_timeout (struct Session *s);
280
281
282 /**
283  * Function that can be used by the transport service to transmit
284  * a message using the plugin.   Note that in the case of a
285  * peer disconnecting, the continuation MUST be called
286  * prior to the disconnect notification itself.  This function
287  * will be called with this peer's HELLO message to initiate
288  * a fresh connection to another peer.
289  *
290  * @param cls closure
291  * @param session which session must be used
292  * @param msgbuf the message to transmit
293  * @param msgbuf_size number of bytes in 'msgbuf'
294  * @param priority how important is the message (most plugins will
295  *                 ignore message priority and just FIFO)
296  * @param to how long to wait at most for the transmission (does not
297  *                require plugins to discard the message after the timeout,
298  *                just advisory for the desired delay; most plugins will ignore
299  *                this as well)
300  * @param cont continuation to call once the message has
301  *        been transmitted (or if the transport is ready
302  *        for the next transmission call; or if the
303  *        peer disconnected...); can be NULL
304  * @param cont_cls closure for cont
305  * @return number of bytes used (on the physical network, with overheads);
306  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
307  *         and does NOT mean that the message was not transmitted (DV)
308  */
309 static ssize_t
310 http_client_plugin_send (void *cls,
311                   struct Session *session,
312                   const char *msgbuf, size_t msgbuf_size,
313                   unsigned int priority,
314                   struct GNUNET_TIME_Relative to,
315                   GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
316 {
317   struct HTTP_Client_Plugin *plugin = cls;
318   int bytes_sent = 0;
319
320   GNUNET_assert (plugin != NULL);
321   GNUNET_assert (session != NULL);
322
323   GNUNET_break (0);
324
325   /*  struct Plugin *plugin = cls; */
326   return bytes_sent;
327 }
328
329
330
331 /**
332  * Function that can be used to force the plugin to disconnect
333  * from the given peer and cancel all previous transmissions
334  * (and their continuationc).
335  *
336  * @param cls closure
337  * @param target peer from which to disconnect
338  */
339 static void
340 http_client_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *target)
341 {
342   // struct Plugin *plugin = cls;
343   // FIXME
344   GNUNET_break (0);
345 }
346
347 static struct Session *
348 client_lookup_session (struct HTTP_Client_Plugin *plugin,
349                        const struct GNUNET_HELLO_Address *address)
350 {
351   struct Session *pos;
352
353   for (pos = plugin->head; NULL != pos; pos = pos->next)
354     if ((0 == memcmp (&address->peer, &pos->target, sizeof (struct GNUNET_PeerIdentity))) &&
355         (address->address_length == pos->addrlen) &&
356         (0 == memcmp (address->address, pos->addr, pos->addrlen)))
357       return pos;
358   return NULL;
359 }
360
361 static int
362 client_exist_session (struct HTTP_Client_Plugin *plugin, struct Session *s)
363 {
364   struct Session * head;
365
366   GNUNET_assert (NULL != plugin);
367   GNUNET_assert (NULL != s);
368
369   for (head = plugin->head; head != NULL; head = head->next)
370   {
371     if (head == s)
372       return GNUNET_YES;
373   }
374   return GNUNET_NO;
375 }
376
377 /**
378  * Callback method used with libcurl
379  * Method is called when libcurl needs to read data during sending
380  *
381  * @param stream pointer where to write data
382  * @param size size of an individual element
383  * @param nmemb count of elements that can be written to the buffer
384  * @param cls source pointer, passed to the libcurl handle
385  * @return bytes written to stream, returning 0 will terminate connection!
386  */
387 static size_t
388 client_send_cb (void *stream, size_t size, size_t nmemb, void *cls)
389 {
390   struct Session *s = cls;
391   struct HTTP_Client_Plugin *plugin = s->plugin;
392   struct HTTP_Message *msg = s->msg_head;
393   size_t len;
394
395   if (GNUNET_YES != client_exist_session (plugin, s))
396   {
397     GNUNET_break (0);
398     return 0;
399   }
400   if (NULL == msg)
401   {
402     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
403                      "Nothing to for session %p send! Suspending PUT handle!\n", s);
404     s->client_put_paused = GNUNET_YES;
405     return CURL_READFUNC_PAUSE;
406   }
407   /* data to send */
408   GNUNET_assert (msg->pos < msg->size);
409   /* calculate how much fits in buffer */
410   len = GNUNET_MIN (msg->size - msg->pos,
411                     size * nmemb);
412   memcpy (stream, &msg->buf[msg->pos], len);
413   msg->pos += len;
414   if (msg->pos == msg->size)
415   {
416     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
417                      "Session %p message with %u bytes sent, removing message from queue\n",
418                      s, msg->size, msg->pos);
419     /* Calling transmit continuation  */
420     GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg);
421     if (NULL != msg->transmit_cont)
422       msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_OK);
423     GNUNET_free (msg);
424   }
425   client_reschedule_session_timeout (s);
426   return len;
427 }
428
429
430 static void
431 client_wake_up (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
432 {
433   struct Session *s = cls;
434
435   if (GNUNET_YES != client_exist_session(p, s))
436   {
437     GNUNET_break (0);
438     return;
439   }
440   s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK;
441   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
442     return;
443   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
444                    "Client: %p Waking up receive handle\n", s->client_get);
445   if (s->client_get != NULL)
446     curl_easy_pause (s->client_get, CURLPAUSE_CONT);
447 }
448
449
450
451 static int
452 client_receive_mst_cb (void *cls, void *client,
453                        const struct GNUNET_MessageHeader *message)
454 {
455   struct Session *s = cls;
456   struct HTTP_Client_Plugin *plugin;
457   struct GNUNET_TIME_Relative delay;
458   struct GNUNET_ATS_Information atsi[2];
459
460   if (GNUNET_YES != client_exist_session(p, s))
461   {
462     GNUNET_break (0);
463     return GNUNET_OK;
464   }
465   plugin = s->plugin;
466
467
468   atsi[0].type = htonl (GNUNET_ATS_QUALITY_NET_DISTANCE);
469   atsi[0].value = htonl (1);
470   atsi[1].type = htonl (GNUNET_ATS_NETWORK_TYPE);
471   atsi[1].value = s->ats_address_network_type;
472   GNUNET_break (s->ats_address_network_type != ntohl (GNUNET_ATS_NET_UNSPECIFIED));
473
474   delay = s->plugin->env->receive (plugin->env->cls, &s->target, message,
475                                    (const struct GNUNET_ATS_Information *) &atsi, 2,
476                                    s, s->addr, s->addrlen);
477   s->next_receive =
478       GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), delay);
479
480   if (GNUNET_TIME_absolute_get ().abs_value < s->next_receive.abs_value)
481   {
482
483     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
484                      "Client: peer `%s' address `%s' next read delayed for %llu ms\n",
485                      GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen),
486                      delay);
487   }
488   client_reschedule_session_timeout (s);
489   return GNUNET_OK;
490 }
491
492
493
494 /**
495  * Callback method used with libcurl
496  * Method is called when libcurl needs to write data during sending
497  *
498  * @param stream pointer where to write data
499  * @param size size of an individual element
500  * @param nmemb count of elements that can be written to the buffer
501  * @param cls destination pointer, passed to the libcurl handle
502  * @return bytes read from stream
503  */
504 static size_t
505 client_receive (void *stream, size_t size, size_t nmemb, void *cls)
506 {
507   return 0;
508
509   struct Session *s = cls;
510   struct GNUNET_TIME_Absolute now;
511   size_t len = size * nmemb;
512   struct HTTP_Client_Plugin *plugin = s->plugin;
513
514   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
515                    "Received %u bytes from peer `%s'\n", len,
516                    GNUNET_i2s (&s->target));
517   now = GNUNET_TIME_absolute_get ();
518   if (now.abs_value < s->next_receive.abs_value)
519   {
520     struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
521     struct GNUNET_TIME_Relative delta =
522         GNUNET_TIME_absolute_get_difference (now, s->next_receive);
523     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
524                      "No inbound bandwidth for session %p available! Next read was delayed for %llu ms\n",
525                      s, delta.rel_value);
526     if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK)
527     {
528       GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
529       s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK;
530     }
531     s->recv_wakeup_task =
532         GNUNET_SCHEDULER_add_delayed (delta, &client_wake_up, s);
533     return CURLPAUSE_ALL;
534   }
535   if (NULL == s->msg_tk)
536     s->msg_tk = GNUNET_SERVER_mst_create (&client_receive_mst_cb, s);
537   GNUNET_SERVER_mst_receive (s->msg_tk, s, stream, len, GNUNET_NO, GNUNET_NO);
538   return len;
539
540 }
541
542 /**
543  * Task performing curl operations
544  *
545  * @param cls plugin as closure
546  * @param tc gnunet scheduler task context
547  */
548 static void
549 client_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
550 {
551   GNUNET_break (0);
552 #if 0
553   struct HTTP_Client_Plugin *plugin = cls;
554   int running;
555   CURLMcode mret;
556
557   GNUNET_assert (cls != NULL);
558
559   plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
560   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
561     return;
562
563   do
564   {
565     running = 0;
566     mret = curl_multi_perform (plugin->client_mh, &running);
567
568     CURLMsg *msg;
569     int msgs_left;
570
571     while ((msg = curl_multi_info_read (plugin->client_mh, &msgs_left)))
572     {
573       CURL *easy_h = msg->easy_handle;
574       struct Session *s = NULL;
575       char *d = (char *) s;
576
577       if (easy_h == NULL)
578       {
579         GNUNET_break (0);
580         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
581                          "Client: connection to ended with reason %i: `%s', %i handles running\n",
582                          msg->data.result,
583                          curl_easy_strerror (msg->data.result), running);
584         continue;
585       }
586
587       GNUNET_assert (CURLE_OK ==
588                      curl_easy_getinfo (easy_h, CURLINFO_PRIVATE, &d));
589       s = (struct Session *) d;
590
591       if (GNUNET_YES != exist_session(plugin, s))
592       {
593         GNUNET_break (0);
594         return;
595       }
596
597       GNUNET_assert (s != NULL);
598       if (msg->msg == CURLMSG_DONE)
599       {
600         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
601                          "Client: %p connection to '%s'  %s ended with reason %i: `%s'\n",
602                          msg->easy_handle, GNUNET_i2s (&s->target),
603                          http_plugin_address_to_string (NULL, s->addr,
604                                                         s->addrlen),
605                          msg->data.result,
606                          curl_easy_strerror (msg->data.result));
607
608         /* Disconnect other transmission direction and tell transport */
609         client_disconnect (s);
610       }
611     }
612   }
613   while (mret == CURLM_CALL_MULTI_PERFORM);
614   client_schedule (plugin, GNUNET_NO);
615 #endif
616 }
617
618
619 static int
620 client_connect (struct Session *s)
621 {
622
623   struct HTTP_Client_Plugin *plugin = s->plugin;
624   int res = GNUNET_OK;
625   char *url;
626   CURLMcode mret;
627
628   /* create url */
629   if (NULL == http_common_plugin_address_to_string (NULL, s->addr, s->addrlen))
630   {
631     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
632                      "Invalid address peer `%s'\n",
633                      GNUNET_i2s (&s->target));
634     return GNUNET_SYSERR;
635   }
636
637   GNUNET_asprintf (&url, "%s%s;%u",
638       http_common_plugin_address_to_string (plugin, s->addr, s->addrlen),
639                    GNUNET_h2s_full (&plugin->env->my_identity->hashPubKey),
640                    plugin->last_tag);
641
642   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
643                    "Initiating outbound session peer `%s' using address `%s'\n",
644                    GNUNET_i2s (&s->target), url);
645
646   /* create get connection */
647   s->client_get = curl_easy_init ();
648 #if VERBOSE_CURL
649   curl_easy_setopt (s->client_get, CURLOPT_VERBOSE, 1L);
650   curl_easy_setopt (s->client_get, CURLOPT_DEBUGFUNCTION, &client_log);
651   curl_easy_setopt (s->client_get, CURLOPT_DEBUGDATA, s->client_get);
652 #endif
653 #if BUILD_HTTPS
654   curl_easy_setopt (s->client_get, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
655   curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYPEER, 0);
656   curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYHOST, 0);
657 #endif
658   curl_easy_setopt (s->client_get, CURLOPT_URL, url);
659   //curl_easy_setopt (s->client_get, CURLOPT_HEADERFUNCTION, &curl_get_header_cb);
660   //curl_easy_setopt (s->client_get, CURLOPT_WRITEHEADER, ps);
661   curl_easy_setopt (s->client_get, CURLOPT_READFUNCTION, client_send_cb);
662   curl_easy_setopt (s->client_get, CURLOPT_READDATA, s);
663   curl_easy_setopt (s->client_get, CURLOPT_WRITEFUNCTION, client_receive);
664   curl_easy_setopt (s->client_get, CURLOPT_WRITEDATA, s);
665   curl_easy_setopt (s->client_get, CURLOPT_TIMEOUT_MS,
666                     (long) GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value);
667   curl_easy_setopt (s->client_get, CURLOPT_PRIVATE, s);
668   curl_easy_setopt (s->client_get, CURLOPT_CONNECTTIMEOUT_MS,
669                     (long) HTTP_NOT_VALIDATED_TIMEOUT.rel_value);
670   curl_easy_setopt (s->client_get, CURLOPT_BUFFERSIZE,
671                     2 * GNUNET_SERVER_MAX_MESSAGE_SIZE);
672 #if CURL_TCP_NODELAY
673   curl_easy_setopt (ps->recv_endpoint, CURLOPT_TCP_NODELAY, 1);
674 #endif
675
676   /* create put connection */
677   s->client_put = curl_easy_init ();
678 #if VERBOSE_CURL
679   curl_easy_setopt (s->client_put, CURLOPT_VERBOSE, 1L);
680   curl_easy_setopt (s->client_put, CURLOPT_DEBUGFUNCTION, &client_log);
681   curl_easy_setopt (s->client_put, CURLOPT_DEBUGDATA, s->client_put);
682 #endif
683 #if BUILD_HTTPS
684   curl_easy_setopt (s->client_put, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
685   curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYPEER, 0);
686   curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYHOST, 0);
687 #endif
688   curl_easy_setopt (s->client_put, CURLOPT_URL, url);
689   curl_easy_setopt (s->client_put, CURLOPT_PUT, 1L);
690   //curl_easy_setopt (s->client_put, CURLOPT_HEADERFUNCTION, &curl_put_header_cb);
691   //curl_easy_setopt (s->client_put, CURLOPT_WRITEHEADER, ps);
692   curl_easy_setopt (s->client_put, CURLOPT_READFUNCTION, client_send_cb);
693   curl_easy_setopt (s->client_put, CURLOPT_READDATA, s);
694   curl_easy_setopt (s->client_put, CURLOPT_WRITEFUNCTION, client_receive);
695   curl_easy_setopt (s->client_put, CURLOPT_WRITEDATA, s);
696   curl_easy_setopt (s->client_put, CURLOPT_TIMEOUT_MS,
697                     (long) GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT.rel_value);
698   curl_easy_setopt (s->client_put, CURLOPT_PRIVATE, s);
699   curl_easy_setopt (s->client_put, CURLOPT_CONNECTTIMEOUT_MS,
700                     (long) HTTP_NOT_VALIDATED_TIMEOUT.rel_value);
701   curl_easy_setopt (s->client_put, CURLOPT_BUFFERSIZE,
702                     2 * GNUNET_SERVER_MAX_MESSAGE_SIZE);
703 #if CURL_TCP_NODELAY
704   curl_easy_setopt (s->client_put, CURLOPT_TCP_NODELAY, 1);
705 #endif
706   GNUNET_free (url);
707
708   mret = curl_multi_add_handle (plugin->curl_multi_handle, s->client_get);
709   if (mret != CURLM_OK)
710   {
711     curl_easy_cleanup (s->client_get);
712     GNUNET_break (0);
713     return GNUNET_SYSERR;
714   }
715
716   mret = curl_multi_add_handle (plugin->curl_multi_handle, s->client_put);
717   if (mret != CURLM_OK)
718   {
719     curl_multi_remove_handle (plugin->curl_multi_handle, s->client_get);
720     curl_easy_cleanup (s->client_get);
721     curl_easy_cleanup (s->client_put);
722     GNUNET_break (0);
723     return GNUNET_SYSERR;
724   }
725
726   /* Perform connect */
727   plugin->cur_connections += 2;
728   GNUNET_STATISTICS_set (plugin->env->stats,
729       "# HTTP client connections",
730       plugin->cur_connections,
731       GNUNET_NO);
732
733   /* Re-schedule since handles have changed */
734   if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
735   {
736     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
737     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
738   }
739   plugin->client_perform_task = GNUNET_SCHEDULER_add_now (client_run, plugin);
740   return res;
741 }
742
743 void
744 client_delete_session (struct Session *s)
745 {
746   struct HTTP_Client_Plugin *plugin = s->plugin;
747   struct HTTP_Message *pos = s->msg_head;
748   struct HTTP_Message *next = NULL;
749
750   client_stop_session_timeout (s);
751
752   GNUNET_CONTAINER_DLL_remove (plugin->head, plugin->tail, s);
753
754   while (NULL != (pos = next))
755   {
756     next = pos->next;
757     GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, pos);
758     if (pos->transmit_cont != NULL)
759       pos->transmit_cont (pos->transmit_cont_cls, &s->target, GNUNET_SYSERR);
760     GNUNET_free (pos);
761   }
762
763   if (s->msg_tk != NULL)
764   {
765     GNUNET_SERVER_mst_destroy (s->msg_tk);
766     s->msg_tk = NULL;
767   }
768   GNUNET_free (s->addr);
769   GNUNET_free (s);
770 }
771
772
773 /**
774  * Creates a new outbound session the transport service will use to send data to the
775  * peer
776  *
777  * @param cls the plugin
778  * @param address the address
779  * @return the session or NULL of max connections exceeded
780  */
781 static struct Session *
782 http_client_plugin_get_session (void *cls,
783                   const struct GNUNET_HELLO_Address *address)
784 {
785   struct HTTP_Client_Plugin *plugin = cls;
786   struct Session * s = NULL;
787
788   GNUNET_assert (plugin != NULL);
789   GNUNET_assert (address != NULL);
790   GNUNET_assert (address->address != NULL);
791
792
793   /* find existing session */
794   s = client_lookup_session (plugin, address);
795   if (s != NULL)
796     return s;
797
798   if (plugin->max_connections <= plugin->cur_connections)
799   {
800     GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name,
801                      "Maximum number of connections (%u) reached: "
802                      "cannot connect to peer `%s'\n",
803                      plugin->max_connections,
804                      GNUNET_i2s (&address->peer));
805     return NULL;
806   }
807
808   s = GNUNET_malloc (sizeof (struct Session));
809   memcpy (&s->target, &address->peer, sizeof (struct GNUNET_PeerIdentity));
810   s->plugin = plugin;
811   s->addr = GNUNET_malloc (address->address_length);
812   memcpy (s->addr, address->address, address->address_length);
813   s->addrlen = address->address_length;
814   //s->ats_address_network_type = ats.value;
815   GNUNET_break (0);
816
817   client_start_session_timeout (s);
818
819   /* add new session */
820   GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s);
821
822   /* initiate new connection */
823   if (GNUNET_SYSERR == client_connect (s))
824   {
825     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
826                      "Cannot connect to peer `%s' address `%s''\n",
827                      http_common_plugin_address_to_string (NULL, s->addr, s->addrlen),
828                      GNUNET_i2s (&s->target));
829     client_delete_session (s);
830     return NULL;
831   }
832   return s;
833 }
834
835 static int
836 client_start (struct HTTP_Client_Plugin *plugin)
837 {
838   curl_global_init (CURL_GLOBAL_ALL);
839   plugin->curl_multi_handle = curl_multi_init ();
840
841   if (NULL == plugin->curl_multi_handle)
842   {
843     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
844                      _("Could not initialize curl multi handle, failed to start %s plugin!\n"),
845                      plugin->name);
846     return GNUNET_SYSERR;
847   }
848   return GNUNET_OK;
849 }
850
851 static int
852 client_disconnect (struct Session *s)
853 {
854 #if 0
855   int res = GNUNET_OK;
856   CURLMcode mret;
857   struct Plugin *plugin = s->plugin;
858   struct HTTP_Message *msg;
859   struct HTTP_Message *t;
860
861   if (GNUNET_YES != exist_session(plugin, s))
862   {
863     GNUNET_break (0);
864     return GNUNET_SYSERR;
865   }
866
867   if (s->client_put != NULL)
868   {
869     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
870                      "Client: %p / %p Deleting outbound PUT session to peer `%s'\n",
871                      s, s->client_put, GNUNET_i2s (&s->target));
872
873     /* remove curl handle from multi handle */
874     mret = curl_multi_remove_handle (plugin->client_mh, s->client_put);
875     if (mret != CURLM_OK)
876     {
877       /* clean up easy handle, handle is now invalid and free'd */
878       res = GNUNET_SYSERR;
879       GNUNET_break (0);
880     }
881     curl_easy_cleanup (s->client_put);
882     s->client_put = NULL;
883   }
884
885
886   if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK)
887   {
888     GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
889     s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK;
890   }
891
892   if (s->client_get != NULL)
893   {
894     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
895                      "Client: %p / %p Deleting outbound GET session to peer `%s'\n",
896                      s,
897                      s->client_get, GNUNET_i2s (&s->target));
898
899     /* remove curl handle from multi handle */
900     mret = curl_multi_remove_handle (plugin->client_mh, s->client_get);
901     if (mret != CURLM_OK)
902     {
903       /* clean up easy handle, handle is now invalid and free'd */
904       res = GNUNET_SYSERR;
905       GNUNET_break (0);
906     }
907     curl_easy_cleanup (s->client_get);
908     s->client_get = NULL;
909   }
910
911   msg = s->msg_head;
912   while (msg != NULL)
913   {
914     t = msg->next;
915     if (NULL != msg->transmit_cont)
916       msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR);
917     GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg);
918     GNUNET_free (msg);
919     msg = t;
920   }
921
922   plugin->cur_connections -= 2;
923
924   notify_session_end (plugin, &s->target, s);
925
926   GNUNET_assert (plugin->outbound_sessions > 0);
927   plugin->outbound_sessions --;
928   GNUNET_STATISTICS_set (plugin->env->stats,
929       "# HTTP outbound sessions",
930       plugin->outbound_sessions,
931       GNUNET_NO);
932
933   /* Re-schedule since handles have changed */
934   if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
935   {
936     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
937     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
938   }
939   client_schedule (plugin, GNUNET_YES);
940
941   return res;
942 #endif
943   GNUNET_break (0);
944   return 0;
945 }
946
947 /**
948  * Session was idle, so disconnect it
949  */
950 static void
951 client_session_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
952 {
953   GNUNET_assert (NULL != cls);
954   struct Session *s = cls;
955
956   s->timeout_task = GNUNET_SCHEDULER_NO_TASK;
957   GNUNET_log (TIMEOUT_LOG,
958               "Session %p was idle for %llu ms, disconnecting\n",
959               s, (unsigned long long) TIMEOUT.rel_value);
960
961   /* call session destroy function */
962   GNUNET_assert (GNUNET_OK == client_disconnect (s));
963 }
964
965 /**
966 * Start session timeout
967 */
968 static void
969 client_start_session_timeout (struct Session *s)
970 {
971
972  GNUNET_assert (NULL != s);
973  GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == s->timeout_task);
974  s->timeout_task =  GNUNET_SCHEDULER_add_delayed (TIMEOUT,
975                                                   &client_session_timeout,
976                                                   s);
977  GNUNET_log (TIMEOUT_LOG,
978              "Timeout for session %p set to %llu ms\n",
979              s,  (unsigned long long) TIMEOUT.rel_value);
980  GNUNET_break (0);
981 }
982
983 /**
984 * Increment session timeout due to activity
985 */
986 static void
987 client_reschedule_session_timeout (struct Session *s)
988 {
989 #if 0
990  GNUNET_assert (NULL != s);
991  GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != s->timeout_task);
992
993  GNUNET_SCHEDULER_cancel (s->timeout_task);
994  s->timeout_task =  GNUNET_SCHEDULER_add_delayed (TIMEOUT,
995                                                   &server_session_timeout,
996                                                   s);
997  GNUNET_log (TIMEOUT_LOG,
998              "Timeout rescheduled for session %p set to %llu ms\n",
999              s, (unsigned long long) TIMEOUT.rel_value);
1000 #endif
1001  GNUNET_break (0);
1002 }
1003
1004 /**
1005 * Cancel timeout
1006 */
1007 static void
1008 client_stop_session_timeout (struct Session *s)
1009 {
1010  GNUNET_assert (NULL != s);
1011
1012  if (GNUNET_SCHEDULER_NO_TASK != s->timeout_task)
1013  {
1014    GNUNET_SCHEDULER_cancel (s->timeout_task);
1015    s->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1016    GNUNET_log (TIMEOUT_LOG, "Timeout stopped for session %p\n", s);
1017  }
1018 }
1019
1020
1021 /**
1022  * Another peer has suggested an address for this
1023  * peer and transport plugin.  Check that this could be a valid
1024  * address.  If so, consider adding it to the list
1025  * of addresses.
1026  *
1027  * @param cls closure
1028  * @param addr pointer to the address
1029  * @param addrlen length of addr
1030  * @return GNUNET_OK if this is a plausible address for this peer
1031  *         and transport
1032  */
1033 static int
1034 http_client_plugin_address_suggested (void *cls, const void *addr, size_t addrlen)
1035 {
1036   /* struct Plugin *plugin = cls; */
1037
1038   /* A HTTP/S client does not have any valid address so:*/
1039   return GNUNET_NO;
1040 }
1041
1042 /**
1043  * Exit point from the plugin.
1044  */
1045 void *
1046 LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls)
1047 {
1048   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
1049   struct HTTP_Client_Plugin *plugin = api->cls;
1050
1051   if (NULL != plugin->curl_multi_handle)
1052   {
1053     curl_multi_cleanup (plugin->curl_multi_handle);
1054     plugin->curl_multi_handle = NULL;
1055   }
1056   curl_global_cleanup ();
1057
1058   GNUNET_free (plugin);
1059   GNUNET_free (api);
1060   return NULL;
1061 }
1062
1063
1064 /**
1065  * Entry point for the plugin.
1066  */
1067 void *
1068 LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls)
1069 {
1070   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
1071   struct GNUNET_TRANSPORT_PluginFunctions *api;
1072   struct HTTP_Client_Plugin *plugin;
1073
1074   plugin = GNUNET_malloc (sizeof (struct HTTP_Client_Plugin));
1075   p = plugin;
1076   plugin->env = env;
1077   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
1078   api->cls = plugin;
1079   api->send = &http_client_plugin_send;
1080   api->disconnect = &http_client_plugin_disconnect;
1081   api->check_address = &http_client_plugin_address_suggested;
1082   api->get_session = &http_client_plugin_get_session;
1083
1084   api->address_to_string = &http_common_plugin_address_to_string;
1085   api->string_to_address = &http_common_plugin_string_to_address;
1086   api->address_pretty_printer = &http_common_plugin_address_pretty_printer;
1087
1088 #if BUILD_HTTPS
1089   plugin->name = "transport-https_client";
1090   plugin->protocol = "https";
1091 #else
1092   plugin->name = "transport-http_client";
1093   plugin->protocol = "http";
1094 #endif
1095
1096   /* Start client */
1097   if (GNUNET_SYSERR == client_start (plugin))
1098   {
1099       LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
1100       return NULL;
1101   }
1102   return api;
1103 }
1104
1105 /* end of plugin_transport_http_client.c */