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