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