68b25afe59ffd32c162d6bcb602106d15923b150
[oweals/gnunet.git] / src / transport / plugin_transport_http_client.c
1 /*
2      This file is part of GNUnet
3      (C) 2002-2014 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  * @author Christian Grothoff
26  */
27
28 #if BUILD_HTTPS
29 #define PLUGIN_NAME "https_client"
30 #define HTTP_STAT_STR_CONNECTIONS "# HTTPS client connections"
31 #define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_https_client_init
32 #define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_https_client_done
33 #else
34 #define PLUGIN_NAME "http_client"
35 #define HTTP_STAT_STR_CONNECTIONS "# HTTP client connections"
36 #define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_http_client_init
37 #define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_http_client_done
38 #endif
39
40 #define VERBOSE_CURL GNUNET_YES
41
42 #define PUT_DISCONNECT_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
43
44 #define ENABLE_PUT GNUNET_YES
45 #define ENABLE_GET GNUNET_YES
46
47 #include "platform.h"
48 #include "gnunet_util_lib.h"
49 #include "gnunet_protocols.h"
50 #include "gnunet_transport_plugin.h"
51 #include "plugin_transport_http_common.h"
52 #include <curl/curl.h>
53
54
55 #define LOG(kind,...) GNUNET_log_from(kind, PLUGIN_NAME, __VA_ARGS__)
56
57 /**
58  * Encapsulation of all of the state of the plugin.
59  */
60 struct HTTP_Client_Plugin;
61
62
63 /**
64  *  Message to send using http
65  */
66 struct HTTP_Message
67 {
68   /**
69    * next pointer for double linked list
70    */
71   struct HTTP_Message *next;
72
73   /**
74    * previous pointer for double linked list
75    */
76   struct HTTP_Message *prev;
77
78   /**
79    * buffer containing data to send
80    */
81   char *buf;
82
83   /**
84    * Continuation function to call once the transmission buffer
85    * has again space available.  NULL if there is no
86    * continuation to call.
87    */
88   GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
89
90   /**
91    * Closure for @e transmit_cont.
92    */
93   void *transmit_cont_cls;
94
95   /**
96    * amount of data already sent
97    */
98   size_t pos;
99
100   /**
101    * buffer length
102    */
103   size_t size;
104
105 };
106
107
108 /**
109  * Session handle for connections.
110  */
111 struct Session;
112
113
114 /**
115  * A connection handle
116  *
117  */
118 struct ConnectionHandle
119 {
120   /**
121    * The curl easy handle
122    */
123   CURL *easyhandle;
124
125   /**
126    * The related session
127    */
128   struct Session *s;
129 };
130
131
132 /**
133  * Session handle for connections.
134  */
135 struct Session
136 {
137   /**
138    * To whom are we talking to (set to our identity
139    * if we are still waiting for the welcome message)
140    */
141   struct GNUNET_PeerIdentity target;
142
143   /**
144    * The URL to connect to
145    */
146   char *url;
147
148   /**
149    * Address
150    */
151   struct GNUNET_HELLO_Address *address;
152
153   /**
154    * Pointer to the global plugin struct.
155    */
156   struct HTTP_Client_Plugin *plugin;
157
158   /**
159    * Client send handle
160    */
161   void *client_put;
162
163   /**
164    * Handle for the HTTP PUT request.
165    */
166   struct ConnectionHandle put;
167
168   /**
169    * Handle for the HTTP GET request.
170    */
171   struct ConnectionHandle get;
172
173   /**
174    * Client receive handle
175    */
176   void *client_get;
177
178   /**
179    * next pointer for double linked list
180    */
181   struct HTTP_Message *msg_head;
182
183   /**
184    * previous pointer for double linked list
185    */
186   struct HTTP_Message *msg_tail;
187
188   /**
189    * Message stream tokenizer for incoming data
190    */
191   struct GNUNET_SERVER_MessageStreamTokenizer *msg_tk;
192
193   /**
194    * Session timeout task
195    */
196   GNUNET_SCHEDULER_TaskIdentifier put_disconnect_task;
197
198   /**
199    * Session timeout task
200    */
201   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
202
203   /**
204    * Task to wake up client receive handle when receiving is allowed again
205    */
206   GNUNET_SCHEDULER_TaskIdentifier recv_wakeup_task;
207
208   /**
209    * Absolute time when to receive data again.
210    * Used for receive throttling.
211    */
212   struct GNUNET_TIME_Absolute next_receive;
213
214   /**
215    * When does this session time out.
216    */
217   struct GNUNET_TIME_Absolute timeout;
218
219   /**
220    * Number of bytes waiting for transmission to this peer.
221    */
222   unsigned long long bytes_in_queue;
223
224   /**
225    * Outbound overhead due to HTTP connection
226    * Add to next message of this session when calling callback
227    */
228   size_t overhead;
229
230   /**
231    * Number of messages waiting for transmission to this peer.
232    */
233   unsigned int msgs_in_queue;
234
235   /**
236    * ATS network type in NBO
237    */
238   uint32_t ats_address_network_type;
239
240   /**
241    * Is the client PUT handle currently paused?
242    */
243   int put_paused;
244
245   /**
246    * Is the client PUT handle disconnect in progress?
247    */
248   int put_tmp_disconnecting;
249
250   /**
251    * Is the client PUT handle temporarily disconnected?
252    */
253   int put_tmp_disconnected;
254
255   /**
256    * We received data to send while disconnecting, reconnect immediately
257    */
258   int put_reconnect_required;
259 };
260
261
262 /**
263  * Encapsulation of all of the state of the plugin.
264  */
265 struct HTTP_Client_Plugin
266 {
267   /**
268    * Our environment.
269    */
270   struct GNUNET_TRANSPORT_PluginEnvironment *env;
271
272   /**
273    * Open sessions.
274    */
275   struct GNUNET_CONTAINER_MultiPeerMap *sessions;
276
277   /**
278    * Function to call about session status changes.
279    */
280   GNUNET_TRANSPORT_SessionInfoCallback sic;
281
282   /**
283    * Closure for @e sic.
284    */
285   void *sic_cls;
286
287   /**
288    * Plugin name
289    */
290   char *name;
291
292   /**
293    * Protocol
294    */
295   char *protocol;
296
297   /**
298    * Proxy configuration: hostname or ip of the proxy server
299    */
300   char *proxy_hostname;
301
302   /**
303    * Username for the proxy server
304    */
305   char *proxy_username;
306
307   /**
308    * Password for the proxy server
309    */
310   char *proxy_password;
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    * Type of proxy server:
324    *
325    * Valid values as supported by curl:
326    * CURLPROXY_HTTP, CURLPROXY_HTTP_1_0 CURLPROXY_SOCKS4, CURLPROXY_SOCKS5,
327    * CURLPROXY_SOCKS4A, CURLPROXY_SOCKS5_HOSTNAME
328    */
329   curl_proxytype proxytype;
330
331   /**
332    * Use proxy tunneling:
333    * Tunnel all operations through a given HTTP instead of have the proxy
334    * evaluate the HTTP request
335    *
336    * Default: #GNUNET_NO, #GNUNET_YES experimental
337    */
338   int proxy_use_httpproxytunnel;
339
340   /**
341    * My options to be included in the address
342    */
343   uint32_t options;
344
345   /**
346    * Maximum number of sockets the plugin can use
347    * Each http inbound /outbound connections are two connections
348    */
349   unsigned int max_connections;
350
351   /**
352    * Current number of sockets the plugin can use
353    * Each http inbound /outbound connections are two connections
354    */
355   unsigned int cur_connections;
356
357   /**
358    * Last used unique HTTP connection tag
359    */
360   uint32_t last_tag;
361
362   /**
363    * use IPv6
364    */
365   uint16_t use_ipv6;
366
367   /**
368    * use IPv4
369    */
370   uint16_t use_ipv4;
371
372 };
373
374
375
376 /**
377  * If a session monitor is attached, notify it about the new
378  * session state.
379  *
380  * @param plugin our plugin
381  * @param session session that changed state
382  * @param state new state of the session
383  */
384 static void
385 notify_session_monitor (struct HTTP_Client_Plugin *plugin,
386                         struct Session *session,
387                         enum GNUNET_TRANSPORT_SessionState state)
388 {
389   struct GNUNET_TRANSPORT_SessionInfo info;
390
391   if (NULL == plugin->sic)
392     return;
393   memset (&info, 0, sizeof (info));
394   info.state = state;
395   info.is_inbound = GNUNET_NO;
396   info.num_msg_pending = session->msgs_in_queue;
397   info.num_bytes_pending = session->bytes_in_queue;
398   info.receive_delay = session->next_receive;
399   info.session_timeout = session->timeout;
400   info.address = session->address;
401   plugin->sic (plugin->sic_cls,
402                session,
403                &info);
404 }
405
406
407 /**
408  * Delete session @a s.
409  *
410  * @param s the session to delete
411  */
412 static void
413 client_delete_session (struct Session *s)
414 {
415   struct HTTP_Client_Plugin *plugin = s->plugin;
416   struct HTTP_Message *pos;
417   struct HTTP_Message *next;
418
419   if (GNUNET_SCHEDULER_NO_TASK != s->timeout_task)
420   {
421     GNUNET_SCHEDULER_cancel (s->timeout_task);
422     s->timeout_task = GNUNET_SCHEDULER_NO_TASK;
423     s->timeout = GNUNET_TIME_UNIT_ZERO_ABS;
424   }
425   if (GNUNET_SCHEDULER_NO_TASK != s->put_disconnect_task)
426   {
427     GNUNET_SCHEDULER_cancel (s->put_disconnect_task);
428     s->put_disconnect_task = GNUNET_SCHEDULER_NO_TASK;
429   }
430   GNUNET_assert (GNUNET_OK ==
431                  GNUNET_CONTAINER_multipeermap_remove (plugin->sessions,
432                                                        &s->target,
433                                                        s));
434
435   next = s->msg_head;
436   while (NULL != (pos = next))
437   {
438     next = pos->next;
439     GNUNET_CONTAINER_DLL_remove (s->msg_head,
440                                  s->msg_tail,
441                                  pos);
442     GNUNET_assert (0 < s->msgs_in_queue);
443     s->msgs_in_queue--;
444     GNUNET_assert (pos->size <= s->bytes_in_queue);
445     s->bytes_in_queue -= pos->size;
446     if (NULL != pos->transmit_cont)
447       pos->transmit_cont (pos->transmit_cont_cls,
448                           &s->target,
449                           GNUNET_SYSERR,
450                           pos->size,
451                           pos->pos + s->overhead);
452     s->overhead = 0;
453     GNUNET_free (pos);
454   }
455   GNUNET_assert (0 == s->msgs_in_queue);
456   GNUNET_assert (0 == s->bytes_in_queue);
457   notify_session_monitor (plugin,
458                           s,
459                           GNUNET_TRANSPORT_SS_DOWN);
460   if (NULL != s->msg_tk)
461   {
462     GNUNET_SERVER_mst_destroy (s->msg_tk);
463     s->msg_tk = NULL;
464   }
465   GNUNET_HELLO_address_free (s->address);
466   GNUNET_free (s->url);
467   GNUNET_free (s);
468 }
469
470
471 /**
472  * Increment session timeout due to activity for session @a s.
473  *
474  * @param s the session
475  */
476 static void
477 client_reschedule_session_timeout (struct Session *s)
478 {
479   GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != s->timeout_task);
480   s->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
481 }
482
483
484 /**
485  * Task performing curl operations
486  *
487  * @param cls plugin as closure
488  * @param tc gnunet scheduler task context
489  */
490 static void
491 client_run (void *cls,
492             const struct GNUNET_SCHEDULER_TaskContext *tc);
493
494
495 /**
496  * Function setting up file descriptors and scheduling task to run
497  *
498  * @param plugin the plugin as closure
499  * @param now schedule task in 1ms, regardless of what curl may say
500  * @return #GNUNET_SYSERR for hard failure, #GNUNET_OK for ok
501  */
502 static int
503 client_schedule (struct HTTP_Client_Plugin *plugin,
504                  int now)
505 {
506   fd_set rs;
507   fd_set ws;
508   fd_set es;
509   int max;
510   struct GNUNET_NETWORK_FDSet *grs;
511   struct GNUNET_NETWORK_FDSet *gws;
512   long to;
513   CURLMcode mret;
514   struct GNUNET_TIME_Relative timeout;
515
516   /* Cancel previous scheduled task */
517   if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
518   {
519     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
520     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
521   }
522   max = -1;
523   FD_ZERO (&rs);
524   FD_ZERO (&ws);
525   FD_ZERO (&es);
526   mret = curl_multi_fdset (plugin->curl_multi_handle, &rs, &ws, &es, &max);
527   if (mret != CURLM_OK)
528   {
529     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
530                 "curl_multi_fdset", __FILE__, __LINE__,
531                 curl_multi_strerror (mret));
532     return GNUNET_SYSERR;
533   }
534   mret = curl_multi_timeout (plugin->curl_multi_handle, &to);
535   if (to == -1)
536     timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1);
537   else
538     timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
539   if (now == GNUNET_YES)
540     timeout = GNUNET_TIME_UNIT_MILLISECONDS;
541
542   if (mret != CURLM_OK)
543   {
544     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
545                 _("%s failed at %s:%d: `%s'\n"),
546                 "curl_multi_timeout", __FILE__, __LINE__,
547                 curl_multi_strerror (mret));
548     return GNUNET_SYSERR;
549   }
550
551   grs = GNUNET_NETWORK_fdset_create ();
552   gws = GNUNET_NETWORK_fdset_create ();
553   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
554   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
555
556   plugin->client_perform_task =
557       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
558                                    timeout, grs, gws,
559                                    &client_run, plugin);
560   GNUNET_NETWORK_fdset_destroy (gws);
561   GNUNET_NETWORK_fdset_destroy (grs);
562   return GNUNET_OK;
563 }
564
565
566 /**
567  * Loggging function
568  *
569  * @param curl the curl easy handle
570  * @param type message type
571  * @param data data to log, NOT a 0-terminated string
572  * @param size data length
573  * @param cls the closure
574  * @return always 0
575  */
576 static int
577 client_log (CURL *curl,
578             curl_infotype type,
579             const char *data,
580             size_t size,
581             void *cls)
582 {
583   struct ConnectionHandle *ch = cls;
584   const char *ttype = "UNSPECIFIED";
585   char text[size + 2];
586
587   if (! ((type == CURLINFO_TEXT) || (type == CURLINFO_HEADER_IN) || (type == CURLINFO_HEADER_OUT)))
588     return 0;
589   switch (type)
590   {
591   case CURLINFO_TEXT:
592     ttype = "TEXT";
593     break;
594   case CURLINFO_HEADER_IN:
595     ttype = "HEADER_IN";
596     break;
597   case CURLINFO_HEADER_OUT:
598     ttype = "HEADER_OUT";
599     /* Overhead*/
600     GNUNET_assert (NULL != ch);
601     GNUNET_assert (NULL != ch->easyhandle);
602     GNUNET_assert (NULL != ch->s);
603     ch->s->overhead += size;
604     break;
605   default:
606     ttype = "UNSPECIFIED";
607     break;
608   }
609   memcpy (text, data, size);
610   if (text[size - 1] == '\n')
611   {
612     text[size] = '\0';
613   }
614   else
615   {
616     text[size] = '\n';
617     text[size + 1] = '\0';
618   }
619   LOG (GNUNET_ERROR_TYPE_DEBUG,
620        "Connection %p %s: %s",
621        ch->easyhandle,
622        ttype,
623        text);
624   return 0;
625 }
626
627
628 /**
629  * Connect a HTTP put connection
630  *
631  * @param s the session to connect
632  * @return #GNUNET_SYSERR for hard failure, #GNUNET_OK for success
633  */
634 static int
635 client_connect_put (struct Session *s);
636
637
638 /**
639  * Function that can be used by the transport service to transmit
640  * a message using the plugin.   Note that in the case of a
641  * peer disconnecting, the continuation MUST be called
642  * prior to the disconnect notification itself.  This function
643  * will be called with this peer's HELLO message to initiate
644  * a fresh connection to another peer.
645  *
646  * @param cls closure
647  * @param s which session must be used
648  * @param msgbuf the message to transmit
649  * @param msgbuf_size number of bytes in @a msgbuf
650  * @param priority how important is the message (most plugins will
651  *                 ignore message priority and just FIFO)
652  * @param to how long to wait at most for the transmission (does not
653  *                require plugins to discard the message after the timeout,
654  *                just advisory for the desired delay; most plugins will ignore
655  *                this as well)
656  * @param cont continuation to call once the message has
657  *        been transmitted (or if the transport is ready
658  *        for the next transmission call; or if the
659  *        peer disconnected...); can be NULL
660  * @param cont_cls closure for cont
661  * @return number of bytes used (on the physical network, with overheads);
662  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
663  *         and does NOT mean that the message was not transmitted (DV)
664  */
665 static ssize_t
666 http_client_plugin_send (void *cls,
667                          struct Session *s,
668                          const char *msgbuf,
669                          size_t msgbuf_size,
670                          unsigned int priority,
671                          struct GNUNET_TIME_Relative to,
672                          GNUNET_TRANSPORT_TransmitContinuation cont,
673                          void *cont_cls)
674 {
675   struct HTTP_Client_Plugin *plugin = cls;
676   struct HTTP_Message *msg;
677   char *stat_txt;
678
679   LOG (GNUNET_ERROR_TYPE_DEBUG,
680        "Session %p/connection %p: Sending message with %u to peer `%s' \n",
681        s, s->client_put,
682        msgbuf_size, GNUNET_i2s (&s->target));
683
684   /* create new message and schedule */
685   msg = GNUNET_malloc (sizeof (struct HTTP_Message) + msgbuf_size);
686   msg->next = NULL;
687   msg->size = msgbuf_size;
688   msg->pos = 0;
689   msg->buf = (char *) &msg[1];
690   msg->transmit_cont = cont;
691   msg->transmit_cont_cls = cont_cls;
692   memcpy (msg->buf, msgbuf, msgbuf_size);
693   GNUNET_CONTAINER_DLL_insert_tail (s->msg_head,
694                                     s->msg_tail,
695                                     msg);
696   s->msgs_in_queue++;
697   s->bytes_in_queue += msg->size;
698
699   GNUNET_asprintf (&stat_txt,
700                    "# bytes currently in %s_client buffers",
701                    plugin->protocol);
702   GNUNET_STATISTICS_update (plugin->env->stats,
703                             stat_txt, msgbuf_size, GNUNET_NO);
704   GNUNET_free (stat_txt);
705   notify_session_monitor (plugin,
706                           s,
707                           GNUNET_TRANSPORT_SS_UP);
708   if (GNUNET_YES == s->put_tmp_disconnecting)
709   {
710     /* PUT connection is currently getting disconnected */
711     s->put_reconnect_required = GNUNET_YES;
712     LOG (GNUNET_ERROR_TYPE_DEBUG,
713          "Session %p/connection %jp: currently disconnecting, reconnecting immediately\n",
714          s,
715          s->client_put);
716     return msgbuf_size;
717   }
718   if (GNUNET_YES == s->put_paused)
719   {
720     /* PUT connection was paused, unpause */
721     GNUNET_assert (s->put_disconnect_task != GNUNET_SCHEDULER_NO_TASK);
722     GNUNET_SCHEDULER_cancel (s->put_disconnect_task);
723     s->put_disconnect_task = GNUNET_SCHEDULER_NO_TASK;
724     LOG (GNUNET_ERROR_TYPE_DEBUG,
725          "Session %p/connection %p: unpausing connection\n",
726          s, s->client_put);
727     s->put_paused = GNUNET_NO;
728     if (NULL != s->client_put)
729       curl_easy_pause (s->client_put, CURLPAUSE_CONT);
730   }
731   else if (GNUNET_YES == s->put_tmp_disconnected)
732   {
733     /* PUT connection was disconnected, reconnect */
734     LOG (GNUNET_ERROR_TYPE_DEBUG,
735          "Session %p: Reconnecting PUT connection\n",
736          s);
737     s->put_tmp_disconnected = GNUNET_NO;
738     GNUNET_break (NULL == s->client_put);
739     if (GNUNET_SYSERR == client_connect_put (s))
740       return GNUNET_SYSERR;
741   }
742   client_schedule (s->plugin,
743                    GNUNET_YES);
744   return msgbuf_size;
745 }
746
747
748 /**
749  * Disconnect a session
750  *
751  * @param cls the `struct HTTP_Client_Plugin *`
752  * @param s session
753  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
754  */
755 static int
756 http_client_plugin_session_disconnect (void *cls,
757                                        struct Session *s)
758 {
759   struct HTTP_Client_Plugin *plugin = cls;
760   struct HTTP_Message *msg;
761   struct HTTP_Message *t;
762   int res = GNUNET_OK;
763   CURLMcode mret;
764
765   if (NULL != s->client_put)
766   {
767     LOG (GNUNET_ERROR_TYPE_DEBUG,
768          "Session %p/connection %p: disconnecting PUT connection to peer `%s'\n",
769          s,
770          s->client_put,
771          GNUNET_i2s (&s->target));
772
773     /* remove curl handle from multi handle */
774     mret = curl_multi_remove_handle (plugin->curl_multi_handle,
775                                      s->client_put);
776     if (mret != CURLM_OK)
777     {
778       /* clean up easy handle, handle is now invalid and free'd */
779       res = GNUNET_SYSERR;
780       GNUNET_break (0);
781     }
782     curl_easy_cleanup (s->client_put);
783     s->client_put = NULL;
784   }
785
786
787   if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK)
788   {
789     GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
790     s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK;
791   }
792
793   if (NULL != s->client_get)
794   {
795     LOG (GNUNET_ERROR_TYPE_DEBUG,
796          "Session %p/connection %p: disconnecting GET connection to peer `%s'\n",
797          s, s->client_get,
798          GNUNET_i2s (&s->target));
799     /* remove curl handle from multi handle */
800     mret = curl_multi_remove_handle (plugin->curl_multi_handle, s->client_get);
801     if (mret != CURLM_OK)
802     {
803       /* clean up easy handle, handle is now invalid and free'd */
804       res = GNUNET_SYSERR;
805       GNUNET_break (0);
806     }
807     curl_easy_cleanup (s->client_get);
808     s->client_get = NULL;
809   }
810
811   msg = s->msg_head;
812   while (NULL != msg)
813   {
814     t = msg->next;
815     if (NULL != msg->transmit_cont)
816       msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR,
817                           msg->size, msg->pos + s->overhead);
818     s->overhead = 0;
819     GNUNET_CONTAINER_DLL_remove (s->msg_head,
820                                  s->msg_tail,
821                                  msg);
822     GNUNET_assert (0 < s->msgs_in_queue);
823     s->msgs_in_queue--;
824     GNUNET_assert (msg->size <= s->bytes_in_queue);
825     s->bytes_in_queue -= msg->size;
826     GNUNET_free (msg);
827     msg = t;
828   }
829
830   GNUNET_assert (plugin->cur_connections >= 2);
831   plugin->cur_connections -= 2;
832   GNUNET_STATISTICS_set (plugin->env->stats,
833                          HTTP_STAT_STR_CONNECTIONS,
834                          plugin->cur_connections,
835                          GNUNET_NO);
836   LOG (GNUNET_ERROR_TYPE_DEBUG,
837        "Session %p: notifying transport about ending session\n",s);
838
839   plugin->env->session_end (plugin->env->cls, s->address, s);
840   client_delete_session (s);
841
842   /* Re-schedule since handles have changed */
843   if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
844   {
845     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
846     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
847   }
848   client_schedule (plugin, GNUNET_YES);
849
850   return res;
851 }
852
853
854 /**
855  * Function that is called to get the keepalive factor.
856  * #GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT is divided by this number to
857  * calculate the interval between keepalive packets.
858  *
859  * @param cls closure with the `struct Plugin`
860  * @return keepalive factor
861  */
862 static unsigned int
863 http_client_query_keepalive_factor (void *cls)
864 {
865   return 3;
866 }
867
868
869 /**
870  * Callback to destroys all sessions on exit.
871  *
872  * @param cls the `struct HTTP_Client_Plugin *`
873  * @param peer identity of the peer
874  * @param value the `struct Session *`
875  * @return #GNUNET_OK (continue iterating)
876  */
877 static int
878 destroy_session_cb (void *cls,
879                     const struct GNUNET_PeerIdentity *peer,
880                     void *value)
881 {
882   struct HTTP_Client_Plugin *plugin = cls;
883   struct Session *session = value;
884
885   http_client_plugin_session_disconnect (plugin, session);
886   return GNUNET_OK;
887 }
888
889
890 /**
891  * Function that can be used to force the plugin to disconnect
892  * from the given peer and cancel all previous transmissions
893  * (and their continuationc).
894  *
895  * @param cls closure
896  * @param target peer from which to disconnect
897  */
898 static void
899 http_client_plugin_peer_disconnect (void *cls,
900                                     const struct GNUNET_PeerIdentity *target)
901 {
902   struct HTTP_Client_Plugin *plugin = cls;
903
904   LOG (GNUNET_ERROR_TYPE_DEBUG,
905        "Transport tells me to disconnect `%s'\n",
906        GNUNET_i2s (target));
907   GNUNET_CONTAINER_multipeermap_get_multiple (plugin->sessions,
908                                               target,
909                                               &destroy_session_cb,
910                                               plugin);
911 }
912
913
914 /**
915  * Closure for #session_lookup_client_by_address().
916  */
917 struct SessionClientCtx
918 {
919   /**
920    * Address we are looking for.
921    */
922   const struct GNUNET_HELLO_Address *address;
923
924   /**
925    * Session that was found.
926    */
927   struct Session *ret;
928 };
929
930
931 /**
932  * Locate the seession object for a given address.
933  *
934  * @param cls the `struct SessionClientCtx *`
935  * @param key peer identity
936  * @param value the `struct Session` to check
937  * @return #GNUNET_NO if found, #GNUNET_OK if not
938  */
939 static int
940 session_lookup_client_by_address (void *cls,
941                                   const struct GNUNET_PeerIdentity *key,
942                                   void *value)
943 {
944   struct SessionClientCtx *sc_ctx = cls;
945   struct Session *s = value;
946
947   if (0 == GNUNET_HELLO_address_cmp (sc_ctx->address,
948                                      s->address))
949   {
950     sc_ctx->ret = s;
951     return GNUNET_NO;
952   }
953   return GNUNET_YES;
954 }
955
956
957 /**
958  * Check if a sessions exists for an specific address
959  *
960  * @param plugin the plugin
961  * @param address the address
962  * @return the session or NULL
963  */
964 static struct Session *
965 client_lookup_session (struct HTTP_Client_Plugin *plugin,
966                        const struct GNUNET_HELLO_Address *address)
967 {
968   struct SessionClientCtx sc_ctx;
969
970   sc_ctx.address = address;
971   sc_ctx.ret = NULL;
972   GNUNET_CONTAINER_multipeermap_iterate (plugin->sessions,
973                                          &session_lookup_client_by_address,
974                                          &sc_ctx);
975   return sc_ctx.ret;
976 }
977
978
979 /**
980  * When we have nothing to transmit, we pause the HTTP PUT
981  * after a while (so that gnurl stops asking).  This task
982  * is the delayed task that actually pauses the PUT.
983  *
984  * @param cls the `struct Session *` with the put
985  * @param tc scheduler context
986  */
987 static void
988 client_put_disconnect (void *cls,
989                        const struct GNUNET_SCHEDULER_TaskContext *tc)
990 {
991   struct Session *s = cls;
992
993   s->put_disconnect_task = GNUNET_SCHEDULER_NO_TASK;
994   LOG (GNUNET_ERROR_TYPE_DEBUG,
995        "Session %p/connection %p: will be disconnected due to no activity\n",
996        s, s->client_put);
997   s->put_paused = GNUNET_NO;
998   s->put_tmp_disconnecting = GNUNET_YES;
999   if (NULL != s->client_put)
1000     curl_easy_pause (s->client_put, CURLPAUSE_CONT);
1001   client_schedule (s->plugin, GNUNET_YES);
1002 }
1003
1004
1005 /**
1006  * Callback method used with libcurl
1007  * Method is called when libcurl needs to read data during sending
1008  *
1009  * @param stream pointer where to write data
1010  * @param size size of an individual element
1011  * @param nmemb count of elements that can be written to the buffer
1012  * @param cls our `struct Session`
1013  * @return bytes written to stream, returning 0 will terminate connection!
1014  */
1015 static size_t
1016 client_send_cb (void *stream,
1017                 size_t size,
1018                 size_t nmemb,
1019                 void *cls)
1020 {
1021   struct Session *s = cls;
1022   struct HTTP_Client_Plugin *plugin = s->plugin;
1023   struct HTTP_Message *msg = s->msg_head;
1024   size_t len;
1025   char *stat_txt;
1026
1027   if (GNUNET_YES == s->put_tmp_disconnecting)
1028   {
1029     LOG (GNUNET_ERROR_TYPE_DEBUG,
1030          "Session %p/connection %p: disconnect due to inactivity\n",
1031          s,
1032          s->client_put);
1033     return 0;
1034   }
1035
1036   if (NULL == msg)
1037   {
1038     LOG (GNUNET_ERROR_TYPE_DEBUG,
1039          "Session %p/connection %p: nothing to send, suspending\n",
1040          s, s->client_put);
1041     s->put_disconnect_task = GNUNET_SCHEDULER_add_delayed (PUT_DISCONNECT_TIMEOUT,
1042                                                            &client_put_disconnect,
1043                                                            s);
1044     s->put_paused = GNUNET_YES;
1045     return CURL_READFUNC_PAUSE;
1046   }
1047   /* data to send */
1048   GNUNET_assert (msg->pos < msg->size);
1049   /* calculate how much fits in buffer */
1050   len = GNUNET_MIN (msg->size - msg->pos,
1051                     size * nmemb);
1052   memcpy (stream, &msg->buf[msg->pos], len);
1053   msg->pos += len;
1054   if (msg->pos == msg->size)
1055   {
1056     LOG (GNUNET_ERROR_TYPE_DEBUG,
1057          "Session %p/connection %p: sent message with %u bytes sent, removing message from queue\n",
1058          s, s->client_put, msg->size, msg->pos);
1059     /* Calling transmit continuation  */
1060     GNUNET_CONTAINER_DLL_remove (s->msg_head,
1061                                  s->msg_tail,
1062                                  msg);
1063     GNUNET_assert (0 < s->msgs_in_queue);
1064     s->msgs_in_queue--;
1065     GNUNET_assert (msg->size <= s->bytes_in_queue);
1066     s->bytes_in_queue -= msg->size;
1067     if (NULL != msg->transmit_cont)
1068       msg->transmit_cont (msg->transmit_cont_cls,
1069                           &s->target,
1070                           GNUNET_OK,
1071                           msg->size,
1072                           msg->size + s->overhead);
1073     s->overhead = 0;
1074     GNUNET_free (msg);
1075   }
1076   notify_session_monitor (plugin,
1077                           s,
1078                           GNUNET_TRANSPORT_SS_UP);
1079   GNUNET_asprintf (&stat_txt,
1080                    "# bytes currently in %s_client buffers",
1081                    plugin->protocol);
1082   GNUNET_STATISTICS_update (plugin->env->stats,
1083                             stat_txt,
1084                             - len,
1085                             GNUNET_NO);
1086   GNUNET_free (stat_txt);
1087   GNUNET_asprintf (&stat_txt,
1088                    "# bytes transmitted via %s_client",
1089                    plugin->protocol);
1090   GNUNET_STATISTICS_update (plugin->env->stats,
1091                             stat_txt,
1092                             len,
1093                             GNUNET_NO);
1094   GNUNET_free (stat_txt);
1095   return len;
1096 }
1097
1098
1099 /**
1100  * Wake up a curl handle which was suspended
1101  *
1102  * @param cls the session
1103  * @param tc task context
1104  */
1105 static void
1106 client_wake_up (void *cls,
1107                 const struct GNUNET_SCHEDULER_TaskContext *tc)
1108 {
1109   struct Session *s = cls;
1110
1111   s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK;
1112   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1113     return;
1114   LOG (GNUNET_ERROR_TYPE_DEBUG,
1115        "Session %p/connection %p: Waking up GET handle\n",
1116        s,
1117        s->client_get);
1118   if (GNUNET_YES == s->put_paused)
1119   {
1120     /* PUT connection was paused, unpause */
1121     GNUNET_assert (s->put_disconnect_task != GNUNET_SCHEDULER_NO_TASK);
1122     GNUNET_SCHEDULER_cancel (s->put_disconnect_task);
1123     s->put_disconnect_task = GNUNET_SCHEDULER_NO_TASK;
1124     s->put_paused = GNUNET_NO;
1125     if (NULL != s->client_put)
1126       curl_easy_pause (s->client_put, CURLPAUSE_CONT);
1127   }
1128   if (NULL != s->client_get)
1129     curl_easy_pause (s->client_get,
1130                      CURLPAUSE_CONT);
1131 }
1132
1133
1134 /**
1135  * Callback for message stream tokenizer
1136  *
1137  * @param cls the session
1138  * @param client not used
1139  * @param message the message received
1140  * @return always #GNUNET_OK
1141  */
1142 static int
1143 client_receive_mst_cb (void *cls,
1144                        void *client,
1145                        const struct GNUNET_MessageHeader *message)
1146 {
1147   struct Session *s = cls;
1148   struct HTTP_Client_Plugin *plugin;
1149   struct GNUNET_TIME_Relative delay;
1150   struct GNUNET_ATS_Information atsi;
1151   char *stat_txt;
1152
1153   plugin = s->plugin;
1154   atsi.type = htonl (GNUNET_ATS_NETWORK_TYPE);
1155   atsi.value = s->ats_address_network_type;
1156   GNUNET_break (s->ats_address_network_type != ntohl (GNUNET_ATS_NET_UNSPECIFIED));
1157
1158   delay = s->plugin->env->receive (plugin->env->cls,
1159                                    s->address,
1160                                    s,
1161                                    message);
1162   plugin->env->update_address_metrics (plugin->env->cls,
1163                                        s->address, s,
1164                                        &atsi, 1);
1165
1166   GNUNET_asprintf (&stat_txt,
1167                    "# bytes received via %s_client",
1168                    plugin->protocol);
1169   GNUNET_STATISTICS_update (plugin->env->stats,
1170                             stat_txt,
1171                             ntohs (message->size),
1172                             GNUNET_NO);
1173   GNUNET_free (stat_txt);
1174
1175   s->next_receive = GNUNET_TIME_relative_to_absolute (delay);
1176   if (GNUNET_TIME_absolute_get ().abs_value_us < s->next_receive.abs_value_us)
1177   {
1178     LOG (GNUNET_ERROR_TYPE_DEBUG,
1179          "Client: peer `%s' address `%s' next read delayed for %s\n",
1180          GNUNET_i2s (&s->target),
1181          http_common_plugin_address_to_string (s->plugin->protocol,
1182                                                s->address->address,
1183                                                s->address->address_length),
1184          GNUNET_STRINGS_relative_time_to_string (delay,
1185                                                  GNUNET_YES));
1186   }
1187   client_reschedule_session_timeout (s);
1188   return GNUNET_OK;
1189 }
1190
1191
1192 /**
1193  * Callback method used with libcurl when data for a PUT connection are
1194  * received.  We do not expect data here, so we just discard it.
1195  *
1196  * @param stream pointer where to write data
1197  * @param size size of an individual element
1198  * @param nmemb count of elements that can be written to the buffer
1199  * @param cls destination pointer, passed to the libcurl handle
1200  * @return bytes read from stream
1201  */
1202 static size_t
1203 client_receive_put (void *stream,
1204                     size_t size,
1205                     size_t nmemb,
1206                     void *cls)
1207 {
1208   return size * nmemb;
1209 }
1210
1211
1212 /**
1213  * Callback method used with libcurl when data for a GET connection are
1214  * received. Forward to MST
1215  *
1216  * @param stream pointer where to write data
1217  * @param size size of an individual element
1218  * @param nmemb count of elements that can be written to the buffer
1219  * @param cls destination pointer, passed to the libcurl handle
1220  * @return bytes read from stream
1221  */
1222 static size_t
1223 client_receive (void *stream,
1224                 size_t size,
1225                 size_t nmemb,
1226                 void *cls)
1227 {
1228   struct Session *s = cls;
1229   struct GNUNET_TIME_Absolute now;
1230   size_t len = size * nmemb;
1231
1232   LOG (GNUNET_ERROR_TYPE_DEBUG,
1233        "Session %p / connection %p: Received %u bytes from peer `%s'\n",
1234        s, s->client_get,
1235        len, GNUNET_i2s (&s->target));
1236   now = GNUNET_TIME_absolute_get ();
1237   if (now.abs_value_us < s->next_receive.abs_value_us)
1238   {
1239     struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
1240     struct GNUNET_TIME_Relative delta
1241       = GNUNET_TIME_absolute_get_difference (now, s->next_receive);
1242
1243     LOG (GNUNET_ERROR_TYPE_DEBUG,
1244          "Session %p / connection %p: No inbound bandwidth available! Next read was delayed for %s\n",
1245          s,
1246          s->client_get,
1247          GNUNET_STRINGS_relative_time_to_string (delta,
1248                                                  GNUNET_YES));
1249     if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK)
1250     {
1251       GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
1252       s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK;
1253     }
1254     s->recv_wakeup_task
1255       = GNUNET_SCHEDULER_add_delayed (delta,
1256                                       &client_wake_up,
1257                                       s);
1258     return CURL_WRITEFUNC_PAUSE;
1259   }
1260   if (NULL == s->msg_tk)
1261     s->msg_tk = GNUNET_SERVER_mst_create (&client_receive_mst_cb,
1262                                           s);
1263   GNUNET_SERVER_mst_receive (s->msg_tk,
1264                              s,
1265                              stream,
1266                              len,
1267                              GNUNET_NO,
1268                              GNUNET_NO);
1269   return len;
1270 }
1271
1272
1273 /**
1274  * Task performing curl operations
1275  *
1276  * @param cls plugin as closure
1277  * @param tc scheduler task context
1278  */
1279 static void
1280 client_run (void *cls,
1281             const struct GNUNET_SCHEDULER_TaskContext *tc)
1282 {
1283   struct HTTP_Client_Plugin *plugin = cls;
1284   int running;
1285   long http_statuscode;
1286   CURLMcode mret;
1287
1288   plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
1289   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1290     return;
1291
1292   do
1293   {
1294     running = 0;
1295     mret = curl_multi_perform (plugin->curl_multi_handle, &running);
1296
1297     CURLMsg *msg;
1298     int msgs_left;
1299
1300     while ((msg = curl_multi_info_read (plugin->curl_multi_handle, &msgs_left)))
1301     {
1302       CURL *easy_h = msg->easy_handle;
1303       struct Session *s = NULL;
1304       char *d = (char *) s;
1305
1306       if (NULL == easy_h)
1307       {
1308         GNUNET_break (0);
1309         LOG (GNUNET_ERROR_TYPE_DEBUG,
1310              "Client: connection to ended with reason %i: `%s', %i handles running\n",
1311              msg->data.result,
1312              curl_easy_strerror (msg->data.result), running);
1313         continue;
1314       }
1315
1316       GNUNET_assert (CURLE_OK ==
1317                      curl_easy_getinfo (easy_h, CURLINFO_PRIVATE, &d));
1318       s = (struct Session *) d;
1319       GNUNET_assert (s != NULL);
1320       if (msg->msg == CURLMSG_DONE)
1321       {
1322         GNUNET_break (CURLE_OK == curl_easy_getinfo (easy_h,
1323             CURLINFO_RESPONSE_CODE, &http_statuscode));
1324         if (easy_h == s->client_put)
1325         {
1326             if  ((0 != msg->data.result) || (http_statuscode != 200))
1327             {
1328               LOG (GNUNET_ERROR_TYPE_DEBUG,
1329                    "Session %p/connection %p: PUT connection to `%s' ended with status %i reason %i: `%s'\n",
1330                    s, msg->easy_handle,
1331                    GNUNET_i2s (&s->target),
1332                    http_statuscode,
1333                    msg->data.result,
1334                    curl_easy_strerror (msg->data.result));
1335             }
1336             else
1337               LOG (GNUNET_ERROR_TYPE_DEBUG,
1338                    "Session %p/connection %p: PUT connection to `%s' ended normal\n",
1339                    s, msg->easy_handle,
1340                    GNUNET_i2s (&s->target));
1341             if (NULL == s->client_get)
1342             {
1343               /* Disconnect other transmission direction and tell transport */
1344               /* FIXME? */
1345             }
1346             curl_multi_remove_handle (plugin->curl_multi_handle, easy_h);
1347             curl_easy_cleanup (easy_h);
1348             s->put_tmp_disconnecting = GNUNET_NO;
1349             s->put_tmp_disconnected = GNUNET_YES;
1350             s->client_put = NULL;
1351             s->put.easyhandle = NULL;
1352             s->put.s = NULL;
1353
1354             /*
1355              * Handling a rare case:
1356              * plugin_send was called during temporary put disconnect,
1357              * reconnect required after connection was disconnected
1358              */
1359             if (GNUNET_YES == s->put_reconnect_required)
1360             {
1361               s->put_reconnect_required = GNUNET_NO;
1362               if (GNUNET_SYSERR == client_connect_put (s))
1363               {
1364                 GNUNET_break (s->client_put == NULL);
1365                 GNUNET_break (s->put_tmp_disconnected == GNUNET_NO);
1366               }
1367             }
1368         }
1369         if (easy_h == s->client_get)
1370         {
1371             if  ((0 != msg->data.result) || (http_statuscode != 200))
1372             {
1373               LOG (GNUNET_ERROR_TYPE_DEBUG,
1374                    "Session %p/connection %p: GET connection to `%s' ended with status %i reason %i: `%s'\n",
1375                    s,
1376                    msg->easy_handle,
1377                    GNUNET_i2s (&s->target),
1378                    http_statuscode,
1379                    msg->data.result,
1380                    curl_easy_strerror (msg->data.result));
1381
1382             }
1383             else
1384               LOG (GNUNET_ERROR_TYPE_DEBUG,
1385                    "Session %p/connection %p: GET connection to `%s' ended normal\n",
1386                    s,
1387                    msg->easy_handle,
1388                    GNUNET_i2s (&s->target));
1389             /* Disconnect other transmission direction and tell transport */
1390             s->get.easyhandle = NULL;
1391             s->get.s = NULL;
1392             http_client_plugin_session_disconnect (plugin, s);
1393         }
1394       }
1395     }
1396   }
1397   while (mret == CURLM_CALL_MULTI_PERFORM);
1398   client_schedule (plugin, GNUNET_NO);
1399 }
1400
1401
1402 /**
1403  * Connect GET connection for a session
1404  *
1405  * @param s the session to connect
1406  * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1407  */
1408 static int
1409 client_connect_get (struct Session *s)
1410 {
1411   CURLMcode mret;
1412
1413   /* create get connection */
1414   s->client_get = curl_easy_init ();
1415   s->get.s = s;
1416   s->get.easyhandle = s->client_get;
1417 #if VERBOSE_CURL
1418   curl_easy_setopt (s->client_get, CURLOPT_VERBOSE, 1L);
1419   curl_easy_setopt (s->client_get, CURLOPT_DEBUGFUNCTION, &client_log);
1420   curl_easy_setopt (s->client_get, CURLOPT_DEBUGDATA, &s->get);
1421 #endif
1422 #if BUILD_HTTPS
1423   curl_easy_setopt (s->client_get, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
1424   {
1425     struct HttpAddress *ha;
1426     ha = (struct HttpAddress *) s->address->address;
1427
1428     if (HTTP_OPTIONS_VERIFY_CERTIFICATE ==
1429         (ntohl (ha->options) & HTTP_OPTIONS_VERIFY_CERTIFICATE))
1430     {
1431       curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYPEER, 1L);
1432       curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYHOST, 2L);
1433     }
1434     else
1435     {
1436       curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYPEER, 0);
1437       curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYHOST, 0);
1438     }
1439   }
1440   curl_easy_setopt (s->client_get, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
1441   curl_easy_setopt (s->client_get, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS);
1442 #else
1443   curl_easy_setopt (s->client_get, CURLOPT_PROTOCOLS, CURLPROTO_HTTP);
1444   curl_easy_setopt (s->client_get, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP);
1445 #endif
1446
1447   if (s->plugin->proxy_hostname != NULL)
1448   {
1449     curl_easy_setopt (s->client_get, CURLOPT_PROXY, s->plugin->proxy_hostname);
1450     curl_easy_setopt (s->client_get, CURLOPT_PROXYTYPE, s->plugin->proxytype);
1451     if (NULL != s->plugin->proxy_username)
1452       curl_easy_setopt (s->client_get, CURLOPT_PROXYUSERNAME,
1453           s->plugin->proxy_username);
1454     if (NULL != s->plugin->proxy_password)
1455       curl_easy_setopt (s->client_get, CURLOPT_PROXYPASSWORD,
1456           s->plugin->proxy_password);
1457     if (GNUNET_YES == s->plugin->proxy_use_httpproxytunnel)
1458       curl_easy_setopt (s->client_get, CURLOPT_HTTPPROXYTUNNEL,
1459           s->plugin->proxy_use_httpproxytunnel);
1460   }
1461
1462   curl_easy_setopt (s->client_get, CURLOPT_URL, s->url);
1463   //curl_easy_setopt (s->client_get, CURLOPT_HEADERFUNCTION, &curl_get_header_cb);
1464   //curl_easy_setopt (s->client_get, CURLOPT_WRITEHEADER, ps);
1465   curl_easy_setopt (s->client_get, CURLOPT_READFUNCTION, client_send_cb);
1466   curl_easy_setopt (s->client_get, CURLOPT_READDATA, s);
1467   curl_easy_setopt (s->client_get, CURLOPT_WRITEFUNCTION, client_receive);
1468   curl_easy_setopt (s->client_get, CURLOPT_WRITEDATA, s);
1469   /* No timeout by default, timeout done with session timeout */
1470   curl_easy_setopt (s->client_get, CURLOPT_TIMEOUT, 0);
1471   curl_easy_setopt (s->client_get, CURLOPT_PRIVATE, s);
1472   curl_easy_setopt (s->client_get, CURLOPT_CONNECTTIMEOUT_MS,
1473                     (long) (HTTP_CLIENT_NOT_VALIDATED_TIMEOUT.rel_value_us / 1000LL));
1474   curl_easy_setopt (s->client_get, CURLOPT_BUFFERSIZE,
1475                     2 * GNUNET_SERVER_MAX_MESSAGE_SIZE);
1476 #if CURL_TCP_NODELAY
1477   curl_easy_setopt (ps->recv_endpoint, CURLOPT_TCP_NODELAY, 1);
1478 #endif
1479   curl_easy_setopt (s->client_get, CURLOPT_FOLLOWLOCATION, 0);
1480
1481   mret = curl_multi_add_handle (s->plugin->curl_multi_handle, s->client_get);
1482   if (mret != CURLM_OK)
1483   {
1484     LOG (GNUNET_ERROR_TYPE_ERROR,
1485          "Session %p : Failed to add GET handle to multihandle: `%s'\n",
1486          s,
1487          curl_multi_strerror (mret));
1488     curl_easy_cleanup (s->client_get);
1489     s->client_get = NULL;
1490     s->get.s = NULL;
1491     s->get.easyhandle = NULL;
1492     GNUNET_break (0);
1493     return GNUNET_SYSERR;
1494   }
1495
1496   return GNUNET_OK;
1497 }
1498
1499
1500 /**
1501  * Connect a HTTP put connection
1502  *
1503  * @param s the session to connect
1504  * @return #GNUNET_SYSERR for hard failure, #GNUNET_OK for ok
1505  */
1506 static int
1507 client_connect_put (struct Session *s)
1508 {
1509   CURLMcode mret;
1510
1511   /* create put connection */
1512   LOG (GNUNET_ERROR_TYPE_DEBUG,
1513        "Session %p: Init PUT handle\n", s);
1514   s->client_put = curl_easy_init ();
1515   s->put.s = s;
1516   s->put.easyhandle = s->client_put;
1517 #if VERBOSE_CURL
1518   curl_easy_setopt (s->client_put, CURLOPT_VERBOSE, 1L);
1519   curl_easy_setopt (s->client_put, CURLOPT_DEBUGFUNCTION, &client_log);
1520   curl_easy_setopt (s->client_put, CURLOPT_DEBUGDATA, &s->put);
1521 #endif
1522 #if BUILD_HTTPS
1523   curl_easy_setopt (s->client_put, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
1524   {
1525     struct HttpAddress *ha;
1526     ha = (struct HttpAddress *) s->address->address;
1527
1528     if (HTTP_OPTIONS_VERIFY_CERTIFICATE ==
1529         (ntohl (ha->options) & HTTP_OPTIONS_VERIFY_CERTIFICATE))
1530     {
1531       curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYPEER, 1L);
1532       curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYHOST, 2L);
1533     }
1534     else
1535     {
1536       curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYPEER, 0);
1537       curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYHOST, 0);
1538     }
1539   }
1540   curl_easy_setopt (s->client_put, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
1541   curl_easy_setopt (s->client_put, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS);
1542 #else
1543   curl_easy_setopt (s->client_put, CURLOPT_PROTOCOLS, CURLPROTO_HTTP);
1544   curl_easy_setopt (s->client_put, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP);
1545 #endif
1546   if (s->plugin->proxy_hostname != NULL)
1547   {
1548     curl_easy_setopt (s->client_put, CURLOPT_PROXY, s->plugin->proxy_hostname);
1549     curl_easy_setopt (s->client_put, CURLOPT_PROXYTYPE, s->plugin->proxytype);
1550     if (NULL != s->plugin->proxy_username)
1551       curl_easy_setopt (s->client_put, CURLOPT_PROXYUSERNAME,
1552           s->plugin->proxy_username);
1553     if (NULL != s->plugin->proxy_password)
1554       curl_easy_setopt (s->client_put, CURLOPT_PROXYPASSWORD,
1555           s->plugin->proxy_password);
1556     if (GNUNET_YES == s->plugin->proxy_use_httpproxytunnel)
1557       curl_easy_setopt (s->client_put, CURLOPT_HTTPPROXYTUNNEL,
1558           s->plugin->proxy_use_httpproxytunnel);
1559   }
1560
1561   curl_easy_setopt (s->client_put, CURLOPT_URL, s->url);
1562   curl_easy_setopt (s->client_put, CURLOPT_UPLOAD, 1L);
1563   //curl_easy_setopt (s->client_put, CURLOPT_HEADERFUNCTION, &client_curl_header);
1564   //curl_easy_setopt (s->client_put, CURLOPT_WRITEHEADER, ps);
1565   curl_easy_setopt (s->client_put, CURLOPT_READFUNCTION, client_send_cb);
1566   curl_easy_setopt (s->client_put, CURLOPT_READDATA, s);
1567   curl_easy_setopt (s->client_put, CURLOPT_WRITEFUNCTION, client_receive_put);
1568   curl_easy_setopt (s->client_put, CURLOPT_WRITEDATA, s);
1569   /* No timeout by default, timeout done with session timeout */
1570   curl_easy_setopt (s->client_put, CURLOPT_TIMEOUT, 0);
1571   curl_easy_setopt (s->client_put, CURLOPT_PRIVATE, s);
1572   curl_easy_setopt (s->client_put, CURLOPT_CONNECTTIMEOUT_MS,
1573                     (long) (HTTP_CLIENT_NOT_VALIDATED_TIMEOUT.rel_value_us / 1000LL));
1574   curl_easy_setopt (s->client_put, CURLOPT_BUFFERSIZE,
1575                     2 * GNUNET_SERVER_MAX_MESSAGE_SIZE);
1576 #if CURL_TCP_NODELAY
1577   curl_easy_setopt (s->client_put, CURLOPT_TCP_NODELAY, 1);
1578 #endif
1579   mret = curl_multi_add_handle (s->plugin->curl_multi_handle, s->client_put);
1580   if (mret != CURLM_OK)
1581   {
1582     LOG (GNUNET_ERROR_TYPE_ERROR,
1583          "Session %p : Failed to add PUT handle to multihandle: `%s'\n",
1584          s,
1585          curl_multi_strerror (mret));
1586     curl_easy_cleanup (s->client_put);
1587     s->client_put = NULL;
1588     s->put.easyhandle = NULL;
1589     s->put.s = NULL;
1590     s->put_tmp_disconnected = GNUNET_YES;
1591     return GNUNET_SYSERR;
1592   }
1593   s->put_tmp_disconnected = GNUNET_NO;
1594   return GNUNET_OK;
1595 }
1596
1597
1598 /**
1599  * Connect both PUT and GET connection for a session
1600  *
1601  * @param s the session to connect
1602  * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1603  */
1604 static int
1605 client_connect (struct Session *s)
1606 {
1607   struct HTTP_Client_Plugin *plugin = s->plugin;
1608   int res = GNUNET_OK;
1609
1610   /* create url */
1611   if (NULL == http_common_plugin_address_to_string (plugin->protocol,
1612                                                     s->address->address,
1613                                                     s->address->address_length))
1614   {
1615     LOG (GNUNET_ERROR_TYPE_DEBUG,
1616          "Invalid address peer `%s'\n",
1617          GNUNET_i2s (&s->target));
1618     return GNUNET_SYSERR;
1619   }
1620
1621   GNUNET_asprintf (&s->url, "%s/%s;%u",
1622                    http_common_plugin_address_to_url (NULL,
1623                                                       s->address->address,
1624                                                       s->address->address_length),
1625                    GNUNET_i2s_full (plugin->env->my_identity),
1626                    plugin->last_tag);
1627
1628   plugin->last_tag++;
1629   LOG (GNUNET_ERROR_TYPE_DEBUG,
1630        "Initiating outbound session peer `%s' using address `%s'\n",
1631        GNUNET_i2s (&s->target), s->url);
1632
1633   if ((GNUNET_SYSERR == client_connect_get (s)) ||
1634       (GNUNET_SYSERR == client_connect_put (s)))
1635   {
1636     plugin->env->session_end (plugin->env->cls, s->address, s);
1637     client_delete_session (s);
1638     return GNUNET_SYSERR;
1639   }
1640
1641   LOG (GNUNET_ERROR_TYPE_DEBUG,
1642        "Session %p: connected with connections GET %p and PUT %p\n",
1643        s,
1644        s->client_get,
1645        s->client_put);
1646   /* Perform connect */
1647   plugin->cur_connections += 2;
1648   GNUNET_STATISTICS_set (plugin->env->stats,
1649                          HTTP_STAT_STR_CONNECTIONS,
1650                          plugin->cur_connections,
1651                          GNUNET_NO);
1652   /* Re-schedule since handles have changed */
1653   if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
1654   {
1655     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
1656     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
1657   }
1658   plugin->client_perform_task = GNUNET_SCHEDULER_add_now (client_run, plugin);
1659   return res;
1660 }
1661
1662
1663 /**
1664  * Function obtain the network type for a session
1665  *
1666  * @param cls closure (`struct Plugin*`)
1667  * @param session the session
1668  * @return the network type
1669  */
1670 static enum GNUNET_ATS_Network_Type
1671 http_client_plugin_get_network (void *cls,
1672                                 struct Session *session)
1673 {
1674   return ntohl (session->ats_address_network_type);
1675 }
1676
1677
1678 /**
1679  * Session was idle, so disconnect it
1680  *
1681  * @param cls the `struct Session` of the idle session
1682  * @param tc scheduler context
1683  */
1684 static void
1685 client_session_timeout (void *cls,
1686                         const struct GNUNET_SCHEDULER_TaskContext *tc)
1687 {
1688   struct Session *s = cls;
1689   struct GNUNET_TIME_Relative left;
1690
1691   s->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1692   left = GNUNET_TIME_absolute_get_remaining (s->timeout);
1693   if (0 != left.rel_value_us)
1694   {
1695     /* not actually our turn yet, but let's at least update
1696        the monitor, it may think we're about to die ... */
1697     notify_session_monitor (s->plugin,
1698                             s,
1699                             GNUNET_TRANSPORT_SS_UP);
1700     s->timeout_task = GNUNET_SCHEDULER_add_delayed (left,
1701                                                     &client_session_timeout,
1702                                                     s);
1703     return;
1704   }
1705   LOG (TIMEOUT_LOG,
1706        "Session %p was idle for %s, disconnecting\n",
1707        s,
1708        GNUNET_STRINGS_relative_time_to_string (HTTP_CLIENT_SESSION_TIMEOUT,
1709                                                GNUNET_YES));
1710   GNUNET_assert (GNUNET_OK ==
1711                  http_client_plugin_session_disconnect (s->plugin,
1712                                                  s));
1713 }
1714
1715
1716 /**
1717  * Creates a new outbound session the transport service will use to
1718  * send data to the peer
1719  *
1720  * @param cls the plugin
1721  * @param address the address
1722  * @return the session or NULL of max connections exceeded
1723  */
1724 static struct Session *
1725 http_client_plugin_get_session (void *cls,
1726                                 const struct GNUNET_HELLO_Address *address)
1727 {
1728   struct HTTP_Client_Plugin *plugin = cls;
1729   struct Session *s;
1730   struct sockaddr *sa;
1731   struct GNUNET_ATS_Information ats;
1732   size_t salen = 0;
1733   int res;
1734
1735   GNUNET_assert (NULL != address->address);
1736
1737   /* find existing session */
1738   s = client_lookup_session (plugin, address);
1739   if (NULL != s)
1740     return s;
1741
1742   if (plugin->max_connections <= plugin->cur_connections)
1743   {
1744     LOG (GNUNET_ERROR_TYPE_WARNING,
1745          "Maximum number of connections (%u) reached: "
1746          "cannot connect to peer `%s'\n",
1747          plugin->max_connections,
1748          GNUNET_i2s (&address->peer));
1749     return NULL;
1750   }
1751
1752   /* Determine network location */
1753   ats.type = htonl (GNUNET_ATS_NETWORK_TYPE);
1754   ats.value = htonl (GNUNET_ATS_NET_UNSPECIFIED);
1755   sa = http_common_socket_from_address (address->address, address->address_length, &res);
1756   if (GNUNET_SYSERR == res)
1757     return NULL;
1758   if (GNUNET_YES == res)
1759   {
1760     GNUNET_assert (NULL != sa);
1761     if (AF_INET == sa->sa_family)
1762     {
1763       salen = sizeof (struct sockaddr_in);
1764     }
1765     else if (AF_INET6 == sa->sa_family)
1766     {
1767       salen = sizeof (struct sockaddr_in6);
1768     }
1769     ats = plugin->env->get_address_type (plugin->env->cls, sa, salen);
1770     GNUNET_free (sa);
1771   }
1772   else if (GNUNET_NO == res)
1773   {
1774     /* Cannot convert to sockaddr -> is external hostname */
1775     ats.value = htonl (GNUNET_ATS_NET_WAN);
1776   }
1777   if (GNUNET_ATS_NET_UNSPECIFIED == ntohl (ats.value))
1778   {
1779     GNUNET_break (0);
1780     return NULL;
1781   }
1782
1783   s = GNUNET_new (struct Session);
1784   s->target = address->peer;
1785   s->plugin = plugin;
1786   s->address = GNUNET_HELLO_address_copy (address);
1787   s->ats_address_network_type = ats.value;
1788   s->put_paused = GNUNET_NO;
1789   s->put_tmp_disconnecting = GNUNET_NO;
1790   s->put_tmp_disconnected = GNUNET_NO;
1791   s->timeout = GNUNET_TIME_relative_to_absolute (HTTP_CLIENT_SESSION_TIMEOUT);
1792   s->timeout_task =  GNUNET_SCHEDULER_add_delayed (HTTP_CLIENT_SESSION_TIMEOUT,
1793                                                    &client_session_timeout,
1794                                                    s);
1795   LOG (GNUNET_ERROR_TYPE_DEBUG,
1796        "Created new session %p for `%s' address `%s''\n",
1797        s,
1798        http_common_plugin_address_to_string (plugin->protocol,
1799                                              s->address->address,
1800                                              s->address->address_length),
1801        GNUNET_i2s (&s->target));
1802
1803   /* add new session */
1804   (void) GNUNET_CONTAINER_multipeermap_put (plugin->sessions,
1805                                             &s->target,
1806                                             s,
1807                                             GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
1808   /* initiate new connection */
1809   if (GNUNET_SYSERR == client_connect (s))
1810   {
1811     LOG (GNUNET_ERROR_TYPE_ERROR,
1812          "Cannot connect to peer `%s' address `%s''\n",
1813          http_common_plugin_address_to_string (plugin->protocol,
1814                                                s->address->address,
1815                                                s->address->address_length),
1816          GNUNET_i2s (&s->target));
1817     client_delete_session (s);
1818     return NULL;
1819   }
1820   notify_session_monitor (plugin,
1821                           s,
1822                           GNUNET_TRANSPORT_SS_UP); /* or handshake? */
1823   return s;
1824 }
1825
1826
1827 /**
1828  * Setup http_client plugin
1829  *
1830  * @param plugin the plugin handle
1831  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1832  */
1833 static int
1834 client_start (struct HTTP_Client_Plugin *plugin)
1835 {
1836   curl_global_init (CURL_GLOBAL_ALL);
1837   plugin->curl_multi_handle = curl_multi_init ();
1838
1839   if (NULL == plugin->curl_multi_handle)
1840   {
1841     LOG (GNUNET_ERROR_TYPE_ERROR,
1842          _("Could not initialize curl multi handle, failed to start %s plugin!\n"),
1843          plugin->name);
1844     return GNUNET_SYSERR;
1845   }
1846   return GNUNET_OK;
1847 }
1848
1849
1850 /**
1851  * Another peer has suggested an address for this
1852  * peer and transport plugin.  Check that this could be a valid
1853  * address.  If so, consider adding it to the list
1854  * of addresses.
1855  *
1856  * @param cls closure with the `struct Plugin`
1857  * @param addr pointer to the address
1858  * @param addrlen length of @a addr
1859  * @return #GNUNET_OK if this is a plausible address for this peer
1860  *         and transport; always returns #GNUNET_NO (this is the client!)
1861  */
1862 static int
1863 http_client_plugin_address_suggested (void *cls,
1864                                       const void *addr,
1865                                       size_t addrlen)
1866 {
1867   /* A HTTP/S client does not have any valid address so:*/
1868   return GNUNET_NO;
1869 }
1870
1871
1872 /**
1873  * Exit point from the plugin.
1874  *
1875  * @param cls api as closure
1876  * @return NULL
1877  */
1878 void *
1879 LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls)
1880 {
1881   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
1882   struct HTTP_Client_Plugin *plugin = api->cls;
1883
1884   if (NULL == api->cls)
1885   {
1886     /* Stub shutdown */
1887     GNUNET_free (api);
1888     return NULL;
1889   }
1890   LOG (GNUNET_ERROR_TYPE_DEBUG,
1891        _("Shutting down plugin `%s'\n"),
1892        plugin->name);
1893   GNUNET_CONTAINER_multipeermap_iterate (plugin->sessions,
1894                                          &destroy_session_cb,
1895                                          plugin);
1896   if (GNUNET_SCHEDULER_NO_TASK != plugin->client_perform_task)
1897   {
1898     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
1899     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
1900   }
1901   if (NULL != plugin->curl_multi_handle)
1902   {
1903     curl_multi_cleanup (plugin->curl_multi_handle);
1904     plugin->curl_multi_handle = NULL;
1905   }
1906   curl_global_cleanup ();
1907   LOG (GNUNET_ERROR_TYPE_DEBUG,
1908        _("Shutdown for plugin `%s' complete\n"),
1909        plugin->name);
1910   GNUNET_CONTAINER_multipeermap_destroy (plugin->sessions);
1911   GNUNET_free_non_null (plugin->proxy_hostname);
1912   GNUNET_free_non_null (plugin->proxy_username);
1913   GNUNET_free_non_null (plugin->proxy_password);
1914   GNUNET_free (plugin);
1915   GNUNET_free (api);
1916   return NULL;
1917 }
1918
1919
1920 /**
1921  * Configure plugin
1922  *
1923  * @param plugin the plugin handle
1924  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
1925  */
1926 static int
1927 client_configure_plugin (struct HTTP_Client_Plugin *plugin)
1928 {
1929   unsigned long long max_connections;
1930   char *proxy_type;
1931
1932
1933   /* Optional parameters */
1934   if (GNUNET_OK !=
1935       GNUNET_CONFIGURATION_get_value_number (plugin->env->cfg,
1936                                              plugin->name,
1937                                              "MAX_CONNECTIONS",
1938                                              &max_connections))
1939     max_connections = 128;
1940   plugin->max_connections = max_connections;
1941
1942   LOG (GNUNET_ERROR_TYPE_DEBUG,
1943        _("Maximum number of connections is %u\n"),
1944        plugin->max_connections);
1945
1946   /* Read proxy configuration */
1947   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
1948       plugin->name, "PROXY", &plugin->proxy_hostname))
1949   {
1950     LOG (GNUNET_ERROR_TYPE_DEBUG,
1951          "Found proxy host: `%s'\n",
1952          plugin->proxy_hostname);
1953     /* proxy username */
1954     if (GNUNET_OK ==
1955         GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
1956                                                plugin->name,
1957                                                "PROXY_USERNAME",
1958                                                &plugin->proxy_username))
1959     {
1960       LOG (GNUNET_ERROR_TYPE_DEBUG,
1961            "Found proxy username name: `%s'\n",
1962            plugin->proxy_username);
1963     }
1964
1965     /* proxy password */
1966     if (GNUNET_OK ==
1967         GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
1968                                                plugin->name,
1969                                                "PROXY_PASSWORD",
1970                                                &plugin->proxy_password))
1971     {
1972       LOG (GNUNET_ERROR_TYPE_DEBUG,
1973            "Found proxy password name: `%s'\n",
1974            plugin->proxy_password);
1975     }
1976
1977     /* proxy type */
1978     if (GNUNET_OK ==
1979         GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
1980                                                plugin->name,
1981                                                "PROXY_TYPE",
1982                                                &proxy_type))
1983     {
1984       GNUNET_STRINGS_utf8_toupper (proxy_type, proxy_type);
1985
1986       if (0 == strcmp(proxy_type, "HTTP"))
1987         plugin->proxytype = CURLPROXY_HTTP;
1988       else if (0 == strcmp(proxy_type, "SOCKS4"))
1989         plugin->proxytype = CURLPROXY_SOCKS4;
1990       else if (0 == strcmp(proxy_type, "SOCKS5"))
1991         plugin->proxytype = CURLPROXY_SOCKS5;
1992       else if (0 == strcmp(proxy_type, "SOCKS4A"))
1993         plugin->proxytype = CURLPROXY_SOCKS4A;
1994       else if (0 == strcmp(proxy_type, "SOCKS5_HOSTNAME "))
1995         plugin->proxytype = CURLPROXY_SOCKS5_HOSTNAME ;
1996       else
1997       {
1998         LOG (GNUNET_ERROR_TYPE_ERROR,
1999              _("Invalid proxy type: `%s', disabling proxy! Check configuration!\n"),
2000              proxy_type);
2001
2002         GNUNET_free (proxy_type);
2003         GNUNET_free (plugin->proxy_hostname);
2004         plugin->proxy_hostname = NULL;
2005         GNUNET_free_non_null (plugin->proxy_username);
2006         plugin->proxy_username = NULL;
2007         GNUNET_free_non_null (plugin->proxy_password);
2008         plugin->proxy_password = NULL;
2009
2010         return GNUNET_SYSERR;
2011       }
2012
2013       LOG (GNUNET_ERROR_TYPE_DEBUG,
2014            "Found proxy type: `%s'\n",
2015            proxy_type);
2016     }
2017
2018     /* proxy http tunneling */
2019     plugin->proxy_use_httpproxytunnel
2020       = GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg,
2021                                               plugin->name,
2022                                               "PROXY_HTTP_TUNNELING");
2023     if (GNUNET_SYSERR == plugin->proxy_use_httpproxytunnel)
2024       plugin->proxy_use_httpproxytunnel = GNUNET_NO;
2025
2026     GNUNET_free_non_null (proxy_type);
2027   }
2028   return GNUNET_OK;
2029 }
2030
2031
2032 /**
2033  * Function called by the pretty printer for the resolved address for
2034  * each human-readable address obtained.  The callback can be called
2035  * several times. The last invocation must be with a @a address of
2036  * NULL and a @a res of #GNUNET_OK.  Thus, to indicate conversion
2037  * errors, the callback might be called first with @a address NULL and
2038  * @a res being #GNUNET_SYSERR.  In that case, there must still be a
2039  * subsequent call later with @a address NULL and @a res #GNUNET_OK.
2040  *
2041  * @param cls closure
2042  * @param address one of the names for the host, NULL on last callback
2043  * @param res #GNUNET_OK if conversion was successful, #GNUNET_SYSERR on failure,
2044  *      #GNUNET_OK on last callback
2045  */
2046 static const char *
2047 http_client_plugin_address_to_string (void *cls,
2048                                       const void *addr,
2049                                       size_t addrlen)
2050 {
2051   return http_common_plugin_address_to_string (PLUGIN_NAME,
2052                                                addr,
2053                                                addrlen);
2054 }
2055
2056
2057 /**
2058  * Function that will be called whenever the transport service wants to
2059  * notify the plugin that a session is still active and in use and
2060  * therefore the session timeout for this session has to be updated
2061  *
2062  * @param cls closure
2063  * @param peer which peer was the session for
2064  * @param session which session is being updated
2065  */
2066 static void
2067 http_client_plugin_update_session_timeout (void *cls,
2068                                            const struct GNUNET_PeerIdentity *peer,
2069                                            struct Session *session)
2070 {
2071   client_reschedule_session_timeout (session);
2072 }
2073
2074
2075 /**
2076  * Function that will be called whenever the transport service wants to
2077  * notify the plugin that the inbound quota changed and that the plugin
2078  * should update it's delay for the next receive value
2079  *
2080  * @param cls closure
2081  * @param peer which peer was the session for
2082  * @param session which session is being updated
2083  * @param delay new delay to use for receiving
2084  */
2085 static void
2086 http_client_plugin_update_inbound_delay (void *cls,
2087                                          const struct GNUNET_PeerIdentity *peer,
2088                                          struct Session *s,
2089                                          struct GNUNET_TIME_Relative delay)
2090 {
2091   s->next_receive = GNUNET_TIME_relative_to_absolute (delay);
2092   LOG (GNUNET_ERROR_TYPE_DEBUG,
2093        "New inbound delay %s\n",
2094        GNUNET_STRINGS_relative_time_to_string (delay,
2095                                                GNUNET_NO));
2096   if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK)
2097   {
2098     GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
2099     s->recv_wakeup_task
2100       = GNUNET_SCHEDULER_add_delayed (delay,
2101                                       &client_wake_up,
2102                                       s);
2103   }
2104 }
2105
2106
2107 /**
2108  * Return information about the given session to the
2109  * monitor callback.
2110  *
2111  * @param cls the `struct Plugin` with the monitor callback (`sic`)
2112  * @param peer peer we send information about
2113  * @param value our `struct Session` to send information about
2114  * @return #GNUNET_OK (continue to iterate)
2115  */
2116 static int
2117 send_session_info_iter (void *cls,
2118                         const struct GNUNET_PeerIdentity *peer,
2119                         void *value)
2120 {
2121   struct HTTP_Client_Plugin *plugin = cls;
2122   struct Session *session = value;
2123
2124   notify_session_monitor (plugin,
2125                           session,
2126                           GNUNET_TRANSPORT_SS_UP);
2127   return GNUNET_OK;
2128 }
2129
2130
2131 /**
2132  * Begin monitoring sessions of a plugin.  There can only
2133  * be one active monitor per plugin (i.e. if there are
2134  * multiple monitors, the transport service needs to
2135  * multiplex the generated events over all of them).
2136  *
2137  * @param cls closure of the plugin
2138  * @param sic callback to invoke, NULL to disable monitor;
2139  *            plugin will being by iterating over all active
2140  *            sessions immediately and then enter monitor mode
2141  * @param sic_cls closure for @a sic
2142  */
2143 static void
2144 http_client_plugin_setup_monitor (void *cls,
2145                                   GNUNET_TRANSPORT_SessionInfoCallback sic,
2146                                   void *sic_cls)
2147 {
2148   struct HTTP_Client_Plugin *plugin = cls;
2149
2150   plugin->sic = sic;
2151   plugin->sic_cls = sic_cls;
2152   if (NULL != sic)
2153   {
2154     GNUNET_CONTAINER_multipeermap_iterate (plugin->sessions,
2155                                            &send_session_info_iter,
2156                                            plugin);
2157     /* signal end of first iteration */
2158     sic (sic_cls, NULL, NULL);
2159   }
2160 }
2161
2162
2163 /**
2164  * Entry point for the plugin.
2165  */
2166 void *
2167 LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls)
2168 {
2169   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
2170   struct GNUNET_TRANSPORT_PluginFunctions *api;
2171   struct HTTP_Client_Plugin *plugin;
2172
2173   if (NULL == env->receive)
2174   {
2175     /* run in 'stub' mode (i.e. as part of gnunet-peerinfo), don't fully
2176        initialze the plugin or the API */
2177     api = GNUNET_new (struct GNUNET_TRANSPORT_PluginFunctions);
2178     api->cls = NULL;
2179     api->address_to_string = &http_client_plugin_address_to_string;
2180     api->string_to_address = &http_common_plugin_string_to_address;
2181     api->address_pretty_printer = &http_common_plugin_address_pretty_printer;
2182     return api;
2183   }
2184
2185   plugin = GNUNET_new (struct HTTP_Client_Plugin);
2186   plugin->env = env;
2187   plugin->sessions = GNUNET_CONTAINER_multipeermap_create (128,
2188                                                            GNUNET_YES);
2189   api = GNUNET_new (struct GNUNET_TRANSPORT_PluginFunctions);
2190   api->cls = plugin;
2191   api->send = &http_client_plugin_send;
2192   api->disconnect_session = &http_client_plugin_session_disconnect;
2193   api->query_keepalive_factor = &http_client_query_keepalive_factor;
2194   api->disconnect_peer = &http_client_plugin_peer_disconnect;
2195   api->check_address = &http_client_plugin_address_suggested;
2196   api->get_session = &http_client_plugin_get_session;
2197   api->address_to_string = &http_client_plugin_address_to_string;
2198   api->string_to_address = &http_common_plugin_string_to_address;
2199   api->address_pretty_printer = &http_common_plugin_address_pretty_printer;
2200   api->get_network = &http_client_plugin_get_network;
2201   api->update_session_timeout = &http_client_plugin_update_session_timeout;
2202   api->update_inbound_delay = &http_client_plugin_update_inbound_delay;
2203   api->setup_monitor = &http_client_plugin_setup_monitor;
2204 #if BUILD_HTTPS
2205   plugin->name = "transport-https_client";
2206   plugin->protocol = "https";
2207 #else
2208   plugin->name = "transport-http_client";
2209   plugin->protocol = "http";
2210 #endif
2211   plugin->last_tag = 1;
2212
2213   if (GNUNET_SYSERR == client_configure_plugin (plugin))
2214   {
2215     LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
2216     return NULL;
2217   }
2218
2219   /* Start client */
2220   if (GNUNET_SYSERR == client_start (plugin))
2221   {
2222     LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
2223     return NULL;
2224   }
2225   return api;
2226 }
2227
2228 /* end of plugin_transport_http_client.c */