Check that you are not present in trail twice
[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_DOWN);
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_UP);
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_UP);
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_UP);
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, s, GNUNET_TRANSPORT_SS_UP); /* or handshake? */
1864   return s;
1865 }
1866
1867
1868 /**
1869  * Setup http_client plugin
1870  *
1871  * @param plugin the plugin handle
1872  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1873  */
1874 static int
1875 client_start (struct HTTP_Client_Plugin *plugin)
1876 {
1877   curl_global_init (CURL_GLOBAL_ALL);
1878   plugin->curl_multi_handle = curl_multi_init ();
1879
1880   if (NULL == plugin->curl_multi_handle)
1881   {
1882     LOG (GNUNET_ERROR_TYPE_ERROR,
1883          _("Could not initialize curl multi handle, failed to start %s plugin!\n"),
1884          plugin->name);
1885     return GNUNET_SYSERR;
1886   }
1887   return GNUNET_OK;
1888 }
1889
1890
1891 /**
1892  * Another peer has suggested an address for this
1893  * peer and transport plugin.  Check that this could be a valid
1894  * address.  If so, consider adding it to the list
1895  * of addresses.
1896  *
1897  * @param cls closure with the `struct Plugin`
1898  * @param addr pointer to the address
1899  * @param addrlen length of @a addr
1900  * @return #GNUNET_OK if this is a plausible address for this peer
1901  *         and transport; always returns #GNUNET_NO (this is the client!)
1902  */
1903 static int
1904 http_client_plugin_address_suggested (void *cls,
1905                                       const void *addr,
1906                                       size_t addrlen)
1907 {
1908   /* A HTTP/S client does not have any valid address so:*/
1909   return GNUNET_NO;
1910 }
1911
1912
1913 /**
1914  * Exit point from the plugin.
1915  *
1916  * @param cls api as closure
1917  * @return NULL
1918  */
1919 void *
1920 LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls)
1921 {
1922   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
1923   struct HTTP_Client_Plugin *plugin = api->cls;
1924
1925   if (NULL == api->cls)
1926   {
1927     /* Stub shutdown */
1928     GNUNET_free (api);
1929     return NULL;
1930   }
1931   LOG (GNUNET_ERROR_TYPE_DEBUG,
1932        _("Shutting down plugin `%s'\n"),
1933        plugin->name);
1934   GNUNET_CONTAINER_multipeermap_iterate (plugin->sessions,
1935                                          &destroy_session_cb,
1936                                          plugin);
1937   if (GNUNET_SCHEDULER_NO_TASK != plugin->client_perform_task)
1938   {
1939     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
1940     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
1941   }
1942   if (NULL != plugin->curl_multi_handle)
1943   {
1944     curl_multi_cleanup (plugin->curl_multi_handle);
1945     plugin->curl_multi_handle = NULL;
1946   }
1947   curl_global_cleanup ();
1948   LOG (GNUNET_ERROR_TYPE_DEBUG,
1949        _("Shutdown for plugin `%s' complete\n"),
1950        plugin->name);
1951   GNUNET_CONTAINER_multipeermap_destroy (plugin->sessions);
1952   GNUNET_free_non_null (plugin->proxy_hostname);
1953   GNUNET_free_non_null (plugin->proxy_username);
1954   GNUNET_free_non_null (plugin->proxy_password);
1955   GNUNET_free (plugin);
1956   GNUNET_free (api);
1957   return NULL;
1958 }
1959
1960
1961 /**
1962  * Configure plugin
1963  *
1964  * @param plugin the plugin handle
1965  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
1966  */
1967 static int
1968 client_configure_plugin (struct HTTP_Client_Plugin *plugin)
1969 {
1970   unsigned long long max_requests;
1971   char *proxy_type;
1972
1973
1974   /* Optional parameters */
1975   if (GNUNET_OK !=
1976       GNUNET_CONFIGURATION_get_value_number (plugin->env->cfg,
1977                                              plugin->name,
1978                                              "MAX_CONNECTIONS",
1979                                              &max_requests))
1980     max_requests = 128;
1981   plugin->max_requests = max_requests;
1982
1983   LOG (GNUNET_ERROR_TYPE_DEBUG,
1984        _("Maximum number of requests is %u\n"),
1985        plugin->max_requests);
1986
1987   /* Read proxy configuration */
1988   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
1989       plugin->name, "PROXY", &plugin->proxy_hostname))
1990   {
1991     LOG (GNUNET_ERROR_TYPE_DEBUG,
1992          "Found proxy host: `%s'\n",
1993          plugin->proxy_hostname);
1994     /* proxy username */
1995     if (GNUNET_OK ==
1996         GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
1997                                                plugin->name,
1998                                                "PROXY_USERNAME",
1999                                                &plugin->proxy_username))
2000     {
2001       LOG (GNUNET_ERROR_TYPE_DEBUG,
2002            "Found proxy username name: `%s'\n",
2003            plugin->proxy_username);
2004     }
2005
2006     /* proxy password */
2007     if (GNUNET_OK ==
2008         GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
2009                                                plugin->name,
2010                                                "PROXY_PASSWORD",
2011                                                &plugin->proxy_password))
2012     {
2013       LOG (GNUNET_ERROR_TYPE_DEBUG,
2014            "Found proxy password name: `%s'\n",
2015            plugin->proxy_password);
2016     }
2017
2018     /* proxy type */
2019     if (GNUNET_OK ==
2020         GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
2021                                                plugin->name,
2022                                                "PROXY_TYPE",
2023                                                &proxy_type))
2024     {
2025       GNUNET_STRINGS_utf8_toupper (proxy_type, proxy_type);
2026
2027       if (0 == strcmp(proxy_type, "HTTP"))
2028         plugin->proxytype = CURLPROXY_HTTP;
2029       else if (0 == strcmp(proxy_type, "SOCKS4"))
2030         plugin->proxytype = CURLPROXY_SOCKS4;
2031       else if (0 == strcmp(proxy_type, "SOCKS5"))
2032         plugin->proxytype = CURLPROXY_SOCKS5;
2033       else if (0 == strcmp(proxy_type, "SOCKS4A"))
2034         plugin->proxytype = CURLPROXY_SOCKS4A;
2035       else if (0 == strcmp(proxy_type, "SOCKS5_HOSTNAME "))
2036         plugin->proxytype = CURLPROXY_SOCKS5_HOSTNAME ;
2037       else
2038       {
2039         LOG (GNUNET_ERROR_TYPE_ERROR,
2040              _("Invalid proxy type: `%s', disabling proxy! Check configuration!\n"),
2041              proxy_type);
2042
2043         GNUNET_free (proxy_type);
2044         GNUNET_free (plugin->proxy_hostname);
2045         plugin->proxy_hostname = NULL;
2046         GNUNET_free_non_null (plugin->proxy_username);
2047         plugin->proxy_username = NULL;
2048         GNUNET_free_non_null (plugin->proxy_password);
2049         plugin->proxy_password = NULL;
2050
2051         return GNUNET_SYSERR;
2052       }
2053
2054       LOG (GNUNET_ERROR_TYPE_DEBUG,
2055            "Found proxy type: `%s'\n",
2056            proxy_type);
2057     }
2058
2059     /* proxy http tunneling */
2060     plugin->proxy_use_httpproxytunnel
2061       = GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg,
2062                                               plugin->name,
2063                                               "PROXY_HTTP_TUNNELING");
2064     if (GNUNET_SYSERR == plugin->proxy_use_httpproxytunnel)
2065       plugin->proxy_use_httpproxytunnel = GNUNET_NO;
2066
2067     GNUNET_free_non_null (proxy_type);
2068   }
2069
2070   /* Should we emulate an XHR client for testing? */
2071   plugin->emulate_xhr
2072     = GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg,
2073                                             plugin->name,
2074                                             "EMULATE_XHR");
2075   return GNUNET_OK;
2076 }
2077
2078
2079 /**
2080  * Function to convert an address to a human-readable string.
2081  *
2082  * @param cls closure
2083  * @param addr address to convert
2084  * @param addrlen address length
2085  * @return res string if conversion was successful, NULL otherwise
2086  */
2087 static const char *
2088 http_client_plugin_address_to_string (void *cls,
2089                                       const void *addr,
2090                                       size_t addrlen)
2091 {
2092   return http_common_plugin_address_to_string (PLUGIN_NAME,
2093                                                addr,
2094                                                addrlen);
2095 }
2096
2097
2098 /**
2099  * Function that will be called whenever the transport service wants to
2100  * notify the plugin that a session is still active and in use and
2101  * therefore the session timeout for this session has to be updated
2102  *
2103  * @param cls closure
2104  * @param peer which peer was the session for
2105  * @param session which session is being updated
2106  */
2107 static void
2108 http_client_plugin_update_session_timeout (void *cls,
2109                                            const struct GNUNET_PeerIdentity *peer,
2110                                            struct Session *session)
2111 {
2112   client_reschedule_session_timeout (session);
2113 }
2114
2115
2116 /**
2117  * Function that will be called whenever the transport service wants to
2118  * notify the plugin that the inbound quota changed and that the plugin
2119  * should update it's delay for the next receive value
2120  *
2121  * @param cls closure
2122  * @param peer which peer was the session for
2123  * @param s which session is being updated
2124  * @param delay new delay to use for receiving
2125  */
2126 static void
2127 http_client_plugin_update_inbound_delay (void *cls,
2128                                          const struct GNUNET_PeerIdentity *peer,
2129                                          struct Session *s,
2130                                          struct GNUNET_TIME_Relative delay)
2131 {
2132   s->next_receive = GNUNET_TIME_relative_to_absolute (delay);
2133   LOG (GNUNET_ERROR_TYPE_DEBUG,
2134        "New inbound delay %s\n",
2135        GNUNET_STRINGS_relative_time_to_string (delay,
2136                                                GNUNET_NO));
2137   if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK)
2138   {
2139     GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
2140     s->recv_wakeup_task = GNUNET_SCHEDULER_add_delayed (delay,
2141         &client_wake_up, s);
2142   }
2143 }
2144
2145
2146 /**
2147  * Return information about the given session to the
2148  * monitor callback.
2149  *
2150  * @param cls the `struct Plugin` with the monitor callback (`sic`)
2151  * @param peer peer we send information about
2152  * @param value our `struct Session` to send information about
2153  * @return #GNUNET_OK (continue to iterate)
2154  */
2155 static int
2156 send_session_info_iter (void *cls,
2157                         const struct GNUNET_PeerIdentity *peer,
2158                         void *value)
2159 {
2160   struct HTTP_Client_Plugin *plugin = cls;
2161   struct Session *session = value;
2162
2163   notify_session_monitor (plugin,
2164                           session,
2165                           GNUNET_TRANSPORT_SS_UP);
2166   return GNUNET_OK;
2167 }
2168
2169
2170 /**
2171  * Begin monitoring sessions of a plugin.  There can only
2172  * be one active monitor per plugin (i.e. if there are
2173  * multiple monitors, the transport service needs to
2174  * multiplex the generated events over all of them).
2175  *
2176  * @param cls closure of the plugin
2177  * @param sic callback to invoke, NULL to disable monitor;
2178  *            plugin will being by iterating over all active
2179  *            sessions immediately and then enter monitor mode
2180  * @param sic_cls closure for @a sic
2181  */
2182 static void
2183 http_client_plugin_setup_monitor (void *cls,
2184                                   GNUNET_TRANSPORT_SessionInfoCallback sic,
2185                                   void *sic_cls)
2186 {
2187   struct HTTP_Client_Plugin *plugin = cls;
2188
2189   plugin->sic = sic;
2190   plugin->sic_cls = sic_cls;
2191   if (NULL != sic)
2192   {
2193     GNUNET_CONTAINER_multipeermap_iterate (plugin->sessions,
2194                                            &send_session_info_iter,
2195                                            plugin);
2196     /* signal end of first iteration */
2197     sic (sic_cls, NULL, NULL);
2198   }
2199 }
2200
2201
2202 /**
2203  * Entry point for the plugin.
2204  */
2205 void *
2206 LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls)
2207 {
2208   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
2209   struct GNUNET_TRANSPORT_PluginFunctions *api;
2210   struct HTTP_Client_Plugin *plugin;
2211
2212   if (NULL == env->receive)
2213   {
2214     /* run in 'stub' mode (i.e. as part of gnunet-peerinfo), don't fully
2215        initialze the plugin or the API */
2216     api = GNUNET_new (struct GNUNET_TRANSPORT_PluginFunctions);
2217     api->cls = NULL;
2218     api->address_to_string = &http_client_plugin_address_to_string;
2219     api->string_to_address = &http_common_plugin_string_to_address;
2220     api->address_pretty_printer = &http_common_plugin_address_pretty_printer;
2221     return api;
2222   }
2223
2224   plugin = GNUNET_new (struct HTTP_Client_Plugin);
2225   plugin->env = env;
2226   plugin->sessions = GNUNET_CONTAINER_multipeermap_create (128,
2227                                                            GNUNET_YES);
2228   api = GNUNET_new (struct GNUNET_TRANSPORT_PluginFunctions);
2229   api->cls = plugin;
2230   api->send = &http_client_plugin_send;
2231   api->disconnect_session = &http_client_plugin_session_disconnect;
2232   api->query_keepalive_factor = &http_client_query_keepalive_factor;
2233   api->disconnect_peer = &http_client_plugin_peer_disconnect;
2234   api->check_address = &http_client_plugin_address_suggested;
2235   api->get_session = &http_client_plugin_get_session;
2236   api->address_to_string = &http_client_plugin_address_to_string;
2237   api->string_to_address = &http_common_plugin_string_to_address;
2238   api->address_pretty_printer = &http_common_plugin_address_pretty_printer;
2239   api->get_network = &http_client_plugin_get_network;
2240   api->update_session_timeout = &http_client_plugin_update_session_timeout;
2241   api->update_inbound_delay = &http_client_plugin_update_inbound_delay;
2242   api->setup_monitor = &http_client_plugin_setup_monitor;
2243 #if BUILD_HTTPS
2244   plugin->name = "transport-https_client";
2245   plugin->protocol = "https";
2246 #else
2247   plugin->name = "transport-http_client";
2248   plugin->protocol = "http";
2249 #endif
2250   plugin->last_tag = 1;
2251
2252   if (GNUNET_SYSERR == client_configure_plugin (plugin))
2253   {
2254     LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
2255     return NULL;
2256   }
2257
2258   /* Start client */
2259   if (GNUNET_SYSERR == client_start (plugin))
2260   {
2261     LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
2262     return NULL;
2263   }
2264   return api;
2265 }
2266
2267 /* end of plugin_transport_http_client.c */