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