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