tolerate additional IPv4 address now available for gnunet.org
[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      SPDX-License-Identifier: AGPL3.0-or-later
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 /* Just included for the right curl.h */
53 #include "gnunet_curl_lib.h"
54
55
56 #define LOG(kind,...) GNUNET_log_from(kind, PLUGIN_NAME, __VA_ARGS__)
57
58 /**
59  * Encapsulation of all of the state of the plugin.
60  */
61 struct HTTP_Client_Plugin;
62
63 /**
64  * State of a HTTP PUT request
65  */
66 enum HTTP_PUT_REQUEST_STATE
67 {
68   /**
69    *  Just created, not yet connected
70    */
71   H_NOT_CONNECTED,
72
73   /**
74    *  Connected
75    */
76   H_CONNECTED,
77
78   /**
79    *  Paused, nothing to send
80    */
81   H_PAUSED,
82
83   /**
84    * Temporary disconnect in progress due to inactivity
85    */
86   H_TMP_DISCONNECTING,
87
88   /**
89    * Send request while temporary disconnect, reconnect
90    */
91   H_TMP_RECONNECT_REQUIRED,
92
93   /**
94    * Temporarily disconnected
95    */
96   H_TMP_DISCONNECTED,
97
98   /**
99    * Disconnected
100    */
101   H_DISCONNECTED
102 };
103
104 /**
105  *  Message to send using http
106  */
107 struct HTTP_Message
108 {
109   /**
110    * next pointer for double linked list
111    */
112   struct HTTP_Message *next;
113
114   /**
115    * previous pointer for double linked list
116    */
117   struct HTTP_Message *prev;
118
119   /**
120    * buffer containing data to send
121    */
122   char *buf;
123
124   /**
125    * Continuation function to call once the transmission buffer
126    * has again space available.  NULL if there is no
127    * continuation to call.
128    */
129   GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
130
131   /**
132    * Closure for @e transmit_cont.
133    */
134   void *transmit_cont_cls;
135
136   /**
137    * amount of data already sent
138    */
139   size_t pos;
140
141   /**
142    * buffer length
143    */
144   size_t size;
145
146 };
147
148
149 /**
150  * Session handle for HTTP(S) connections.
151  */
152 struct GNUNET_ATS_Session;
153
154
155 /**
156  * A request handle
157  *
158  */
159 struct RequestHandle
160 {
161   /**
162    * Current state of this request
163    */
164   enum HTTP_PUT_REQUEST_STATE state;
165
166   /**
167    * The curl easy handle
168    */
169   CURL *easyhandle;
170
171   /**
172    * The related session
173    */
174   struct GNUNET_ATS_Session *s;
175 };
176
177
178 /**
179  * Session handle for connections.
180  */
181 struct GNUNET_ATS_Session
182 {
183   /**
184    * The URL to connect to
185    */
186   char *url;
187
188   /**
189    * Address
190    */
191   struct GNUNET_HELLO_Address *address;
192
193   /**
194    * Pointer to the global plugin struct.
195    */
196   struct HTTP_Client_Plugin *plugin;
197
198   /**
199    * Handle for the HTTP PUT request.
200    */
201   struct RequestHandle put;
202
203   /**
204    * Handle for the HTTP GET request.
205    */
206   struct RequestHandle get;
207
208   /**
209    * next pointer for double linked list
210    */
211   struct HTTP_Message *msg_head;
212
213   /**
214    * previous pointer for double linked list
215    */
216   struct HTTP_Message *msg_tail;
217
218   /**
219    * Message stream tokenizer for incoming data
220    */
221   struct GNUNET_MessageStreamTokenizer *msg_tk;
222
223   /**
224    * Session timeout task
225    */
226   struct GNUNET_SCHEDULER_Task *put_disconnect_task;
227
228   /**
229    * Session timeout task
230    */
231   struct GNUNET_SCHEDULER_Task *timeout_task;
232
233   /**
234    * Task to wake up client receive handle when receiving is allowed again
235    */
236   struct GNUNET_SCHEDULER_Task *recv_wakeup_task;
237
238   /**
239    * Absolute time when to receive data again.
240    * Used for receive throttling.
241    */
242   struct GNUNET_TIME_Absolute next_receive;
243
244   /**
245    * When does this session time out.
246    */
247   struct GNUNET_TIME_Absolute timeout;
248
249   /**
250    * Number of bytes waiting for transmission to this peer.
251    */
252   unsigned long long bytes_in_queue;
253
254   /**
255    * Outbound overhead due to HTTP connection
256    * Add to next message of this session when calling callback
257    */
258   size_t overhead;
259
260   /**
261    * Number of messages waiting for transmission to this peer.
262    */
263   unsigned int msgs_in_queue;
264
265   /**
266    * ATS network type.
267    */
268   enum GNUNET_NetworkType scope;
269 };
270
271
272 /**
273  * Encapsulation of all of the state of the plugin.
274  */
275 struct HTTP_Client_Plugin
276 {
277   /**
278    * Our environment.
279    */
280   struct GNUNET_TRANSPORT_PluginEnvironment *env;
281
282   /**
283    * Open sessions.
284    */
285   struct GNUNET_CONTAINER_MultiPeerMap *sessions;
286
287   /**
288    * Function to call about session status changes.
289    */
290   GNUNET_TRANSPORT_SessionInfoCallback sic;
291
292   /**
293    * Closure for @e sic.
294    */
295   void *sic_cls;
296
297   /**
298    * Plugin name
299    */
300   char *name;
301
302   /**
303    * Protocol
304    */
305   char *protocol;
306
307   /**
308    * Proxy configuration: hostname or ip of the proxy server
309    */
310   char *proxy_hostname;
311
312   /**
313    * Username for the proxy server
314    */
315   char *proxy_username;
316
317   /**
318    * Password for the proxy server
319    */
320   char *proxy_password;
321
322   /**
323    * cURL Multihandle
324    */
325   CURLM *curl_multi_handle;
326
327   /**
328    * curl perform task
329    */
330   struct GNUNET_SCHEDULER_Task * client_perform_task;
331
332   /**
333    * Type of proxy server:
334    *
335    * Valid values as supported by curl:
336    * CURLPROXY_HTTP, CURLPROXY_HTTP_1_0 CURLPROXY_SOCKS4, CURLPROXY_SOCKS5,
337    * CURLPROXY_SOCKS4A, CURLPROXY_SOCKS5_HOSTNAME
338    */
339   curl_proxytype proxytype;
340
341   /**
342    * Use proxy tunneling:
343    * Tunnel all operations through a given HTTP instead of have the proxy
344    * evaluate the HTTP request
345    *
346    * Default: #GNUNET_NO, #GNUNET_YES experimental
347    */
348   int proxy_use_httpproxytunnel;
349
350   /**
351    * My options to be included in the address
352    */
353   uint32_t options;
354
355   /**
356    * Maximum number of sockets the plugin can use
357    * Each http connections are two requests
358    */
359   unsigned int max_requests;
360
361   /**
362    * Current number of sockets the plugin can use
363    * Each http connections are two requests
364    */
365   unsigned int cur_requests;
366
367   /**
368    * Last used unique HTTP connection tag
369    */
370   uint32_t last_tag;
371
372   /**
373    * use IPv6
374    */
375   uint16_t use_ipv6;
376
377   /**
378    * use IPv4
379    */
380   uint16_t use_ipv4;
381
382   /**
383    * Should we emulate an XHR client for testing?
384    */
385   int emulate_xhr;
386 };
387
388
389 /**
390  * Disconnect a session
391  *
392  * @param cls the `struct HTTP_Client_Plugin *`
393  * @param s session
394  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
395  */
396 static int
397 http_client_plugin_session_disconnect (void *cls, struct GNUNET_ATS_Session *s);
398
399
400 /**
401  * If a session monitor is attached, notify it about the new
402  * session state.
403  *
404  * @param plugin our plugin
405  * @param session session that changed state
406  * @param state new state of the session
407  */
408 static void
409 notify_session_monitor (struct HTTP_Client_Plugin *plugin,
410                         struct GNUNET_ATS_Session *session,
411                         enum GNUNET_TRANSPORT_SessionState state)
412 {
413   struct GNUNET_TRANSPORT_SessionInfo info;
414
415   if (NULL == plugin->sic)
416     return;
417   memset (&info, 0, sizeof (info));
418   info.state = state;
419   info.is_inbound = GNUNET_NO;
420   info.num_msg_pending = session->msgs_in_queue;
421   info.num_bytes_pending = session->bytes_in_queue;
422   info.receive_delay = session->next_receive;
423   info.session_timeout = session->timeout;
424   info.address = session->address;
425   plugin->sic (plugin->sic_cls,
426                session,
427                &info);
428 }
429
430
431 /**
432  * Delete session @a s.
433  *
434  * @param s the session to delete
435  */
436 static void
437 client_delete_session (struct GNUNET_ATS_Session *s)
438 {
439   struct HTTP_Client_Plugin *plugin = s->plugin;
440   struct HTTP_Message *pos;
441   struct HTTP_Message *next;
442   CURLMcode mret;
443
444   if (NULL != s->timeout_task)
445   {
446     GNUNET_SCHEDULER_cancel (s->timeout_task);
447     s->timeout_task = NULL;
448     s->timeout = GNUNET_TIME_UNIT_ZERO_ABS;
449   }
450   if (NULL != s->put_disconnect_task)
451   {
452     GNUNET_SCHEDULER_cancel (s->put_disconnect_task);
453     s->put_disconnect_task = NULL;
454   }
455   if (NULL != s->recv_wakeup_task)
456   {
457     GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
458     s->recv_wakeup_task = NULL;
459   }
460   GNUNET_assert (GNUNET_OK ==
461                  GNUNET_CONTAINER_multipeermap_remove (plugin->sessions,
462                                                        &s->address->peer,
463                                                        s));
464   if (NULL != s->put.easyhandle)
465   {
466     LOG (GNUNET_ERROR_TYPE_DEBUG,
467          "Session %p/request %p: disconnecting PUT request to peer `%s'\n",
468          s,
469          s->put.easyhandle,
470          GNUNET_i2s (&s->address->peer));
471
472     /* remove curl handle from multi handle */
473     mret = curl_multi_remove_handle (plugin->curl_multi_handle,
474                                      s->put.easyhandle);
475     GNUNET_break (CURLM_OK == mret);
476     curl_easy_cleanup (s->put.easyhandle);
477     GNUNET_assert (plugin->cur_requests > 0);
478     plugin->cur_requests--;
479     s->put.easyhandle = NULL;
480   }
481   if (NULL != s->get.easyhandle)
482   {
483     LOG (GNUNET_ERROR_TYPE_DEBUG,
484          "Session %p/request %p: disconnecting GET request to peer `%s'\n",
485          s, s->get.easyhandle,
486          GNUNET_i2s (&s->address->peer));
487     /* remove curl handle from multi handle */
488     mret = curl_multi_remove_handle (plugin->curl_multi_handle,
489                                      s->get.easyhandle);
490     GNUNET_break (CURLM_OK == mret);
491     curl_easy_cleanup (s->get.easyhandle);
492     GNUNET_assert (plugin->cur_requests > 0);
493     plugin->cur_requests--;
494     s->get.easyhandle = NULL;
495   }
496
497   GNUNET_STATISTICS_set (plugin->env->stats,
498                          HTTP_STAT_STR_CONNECTIONS,
499                          plugin->cur_requests,
500                          GNUNET_NO);
501   next = s->msg_head;
502   while (NULL != (pos = next))
503   {
504     next = pos->next;
505     GNUNET_CONTAINER_DLL_remove (s->msg_head,
506                                  s->msg_tail,
507                                  pos);
508     GNUNET_assert (0 < s->msgs_in_queue);
509     s->msgs_in_queue--;
510     GNUNET_assert (pos->size <= s->bytes_in_queue);
511     s->bytes_in_queue -= pos->size;
512     if (NULL != pos->transmit_cont)
513       pos->transmit_cont (pos->transmit_cont_cls,
514                           &s->address->peer,
515                           GNUNET_SYSERR,
516                           pos->size,
517                           pos->pos + s->overhead);
518     s->overhead = 0;
519     GNUNET_free (pos);
520   }
521   GNUNET_assert (0 == s->msgs_in_queue);
522   GNUNET_assert (0 == s->bytes_in_queue);
523   notify_session_monitor (plugin,
524                           s,
525                           GNUNET_TRANSPORT_SS_DONE);
526   if (NULL != s->msg_tk)
527   {
528     GNUNET_MST_destroy (s->msg_tk);
529     s->msg_tk = NULL;
530   }
531   GNUNET_HELLO_address_free (s->address);
532   GNUNET_free (s->url);
533   GNUNET_free (s);
534 }
535
536
537 /**
538  * Increment session timeout due to activity for session @a s.
539  *
540  * @param s the session
541  */
542 static void
543 client_reschedule_session_timeout (struct GNUNET_ATS_Session *s)
544 {
545   GNUNET_assert (NULL != s->timeout_task);
546   s->timeout = GNUNET_TIME_relative_to_absolute (GNUNET_CONSTANTS_IDLE_CONNECTION_TIMEOUT);
547 }
548
549
550 /**
551  * Task performing curl operations
552  *
553  * @param cls plugin as closure
554  * @param tc gnunet scheduler task context
555  */
556 static void
557 client_run (void *cls);
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   GNUNET_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 GNUNET_ATS_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 GNUNET_ATS_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 GNUNET_ATS_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   GNUNET_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 GNUNET_ATS_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 (NULL != plugin->client_perform_task)
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 GNUNET_ATS_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 GNUNET_ATS_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 GNUNET_ATS_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 GNUNET_ATS_Session *ret;
939 };
940
941
942 /**
943  * Locate the seession object for a given address.
944  *
945  * @param cls the `struct GNUNET_ATS_SessionClientCtx *`
946  * @param key peer identity
947  * @param value the `struct GNUNET_ATS_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 GNUNET_ATS_SessionClientCtx *sc_ctx = cls;
956   struct GNUNET_ATS_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 GNUNET_ATS_Session *
976 client_lookup_session (struct HTTP_Client_Plugin *plugin,
977                        const struct GNUNET_HELLO_Address *address)
978 {
979   struct GNUNET_ATS_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 GNUNET_ATS_Session *` with the put
996  */
997 static void
998 client_put_disconnect (void *cls)
999 {
1000   struct GNUNET_ATS_Session *s = cls;
1001
1002   s->put_disconnect_task = NULL;
1003   LOG (GNUNET_ERROR_TYPE_DEBUG,
1004        "Session %p/request %p: will be disconnected due to no activity\n",
1005        s, s->put.easyhandle);
1006   s->put.state = H_TMP_DISCONNECTING;
1007   if (NULL != s->put.easyhandle)
1008     curl_easy_pause (s->put.easyhandle,
1009                      CURLPAUSE_CONT);
1010   client_schedule (s->plugin, GNUNET_YES);
1011 }
1012
1013
1014 /**
1015  * Callback method used with libcurl
1016  * Method is called when libcurl needs to read data during sending
1017  *
1018  * @param stream pointer where to write data
1019  * @param size size of an individual element
1020  * @param nmemb count of elements that can be written to the buffer
1021  * @param cls our `struct GNUNET_ATS_Session`
1022  * @return bytes written to stream, returning 0 will terminate request!
1023  */
1024 static size_t
1025 client_send_cb (void *stream,
1026                 size_t size,
1027                 size_t nmemb,
1028                 void *cls)
1029 {
1030   struct GNUNET_ATS_Session *s = cls;
1031   struct HTTP_Client_Plugin *plugin = s->plugin;
1032   struct HTTP_Message *msg = s->msg_head;
1033   size_t len;
1034   char *stat_txt;
1035
1036   if (H_TMP_DISCONNECTING == s->put.state)
1037   {
1038     LOG (GNUNET_ERROR_TYPE_DEBUG,
1039          "Session %p/request %p: disconnect due to inactivity\n",
1040          s, s->put.easyhandle);
1041     return 0;
1042   }
1043
1044   if (NULL == msg)
1045   {
1046     if (GNUNET_YES == plugin->emulate_xhr)
1047     {
1048       LOG (GNUNET_ERROR_TYPE_DEBUG,
1049            "Session %p/request %p: PUT request finished\n",
1050            s,
1051            s->put.easyhandle);
1052       s->put.state = H_TMP_DISCONNECTING;
1053       return 0;
1054     }
1055
1056     /* We have nothing to send, so pause PUT request */
1057     LOG (GNUNET_ERROR_TYPE_DEBUG,
1058          "Session %p/request %p: nothing to send, suspending\n",
1059          s,
1060          s->put.easyhandle);
1061     s->put_disconnect_task
1062       = GNUNET_SCHEDULER_add_delayed (PUT_DISCONNECT_TIMEOUT,
1063                                       &client_put_disconnect,
1064                                       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   GNUNET_memcpy (stream,
1074                  &msg->buf[msg->pos],
1075                  len);
1076   msg->pos += len;
1077   if (msg->pos == msg->size)
1078   {
1079     LOG (GNUNET_ERROR_TYPE_DEBUG,
1080          "Session %p/request %p: sent message with %u bytes sent, removing message from queue\n",
1081          s,
1082          s->put.easyhandle,
1083          msg->size,
1084          msg->pos);
1085     /* Calling transmit continuation  */
1086     GNUNET_CONTAINER_DLL_remove (s->msg_head,
1087                                  s->msg_tail,
1088                                  msg);
1089     GNUNET_assert (0 < s->msgs_in_queue);
1090     s->msgs_in_queue--;
1091     GNUNET_assert (msg->size <= s->bytes_in_queue);
1092     s->bytes_in_queue -= msg->size;
1093     if (NULL != msg->transmit_cont)
1094       msg->transmit_cont (msg->transmit_cont_cls,
1095                           &s->address->peer,
1096                           GNUNET_OK,
1097                           msg->size,
1098                           msg->size + s->overhead);
1099     s->overhead = 0;
1100     GNUNET_free (msg);
1101   }
1102   notify_session_monitor (plugin,
1103                           s,
1104                           GNUNET_TRANSPORT_SS_UPDATE);
1105   GNUNET_asprintf (&stat_txt,
1106                    "# bytes currently in %s_client buffers",
1107                    plugin->protocol);
1108   GNUNET_STATISTICS_update (plugin->env->stats,
1109                             stat_txt,
1110                             - len,
1111                             GNUNET_NO);
1112   GNUNET_free (stat_txt);
1113   GNUNET_asprintf (&stat_txt,
1114                    "# bytes transmitted via %s_client",
1115                    plugin->protocol);
1116   GNUNET_STATISTICS_update (plugin->env->stats,
1117                             stat_txt,
1118                             len,
1119                             GNUNET_NO);
1120   GNUNET_free (stat_txt);
1121   return len;
1122 }
1123
1124
1125 /**
1126  * Wake up a curl handle which was suspended
1127  *
1128  * @param cls the session
1129  */
1130 static void
1131 client_wake_up (void *cls)
1132 {
1133   struct GNUNET_ATS_Session *s = cls;
1134
1135   s->recv_wakeup_task = NULL;
1136   LOG (GNUNET_ERROR_TYPE_DEBUG,
1137        "Session %p/request %p: Waking up GET handle\n",
1138        s, s->get.easyhandle);
1139   if (H_PAUSED == s->put.state)
1140   {
1141     /* PUT request was paused, unpause */
1142     GNUNET_assert (s->put_disconnect_task != NULL);
1143     GNUNET_SCHEDULER_cancel (s->put_disconnect_task);
1144     s->put_disconnect_task = NULL;
1145     s->put.state = H_CONNECTED;
1146     if (NULL != s->put.easyhandle)
1147       curl_easy_pause (s->put.easyhandle, CURLPAUSE_CONT);
1148   }
1149   if (NULL != s->get.easyhandle)
1150     curl_easy_pause (s->get.easyhandle, CURLPAUSE_CONT);
1151 }
1152
1153
1154 /**
1155  * Callback for message stream tokenizer
1156  *
1157  * @param cls the session
1158  * @param message the message received
1159  * @return always #GNUNET_OK
1160  */
1161 static int
1162 client_receive_mst_cb (void *cls,
1163                        const struct GNUNET_MessageHeader *message)
1164 {
1165   struct GNUNET_ATS_Session *s = cls;
1166   struct HTTP_Client_Plugin *plugin;
1167   struct GNUNET_TIME_Relative delay;
1168   char *stat_txt;
1169
1170   plugin = s->plugin;
1171   delay = s->plugin->env->receive (plugin->env->cls,
1172                                    s->address,
1173                                    s,
1174                                    message);
1175   GNUNET_asprintf (&stat_txt,
1176                    "# bytes received via %s_client",
1177                    plugin->protocol);
1178   GNUNET_STATISTICS_update (plugin->env->stats,
1179                             stat_txt,
1180                             ntohs (message->size),
1181                             GNUNET_NO);
1182   GNUNET_free (stat_txt);
1183
1184   s->next_receive = GNUNET_TIME_relative_to_absolute (delay);
1185   if (GNUNET_TIME_absolute_get ().abs_value_us < s->next_receive.abs_value_us)
1186   {
1187     LOG (GNUNET_ERROR_TYPE_DEBUG,
1188          "Client: peer `%s' address `%s' next read delayed for %s\n",
1189          GNUNET_i2s (&s->address->peer),
1190          http_common_plugin_address_to_string (s->plugin->protocol,
1191                                                s->address->address,
1192                                                s->address->address_length),
1193          GNUNET_STRINGS_relative_time_to_string (delay,
1194                                                  GNUNET_YES));
1195   }
1196   client_reschedule_session_timeout (s);
1197   return GNUNET_OK;
1198 }
1199
1200
1201 /**
1202  * Callback method used with libcurl when data for a PUT request are
1203  * received.  We do not expect data here, so we just discard it.
1204  *
1205  * @param stream pointer where to write data
1206  * @param size size of an individual element
1207  * @param nmemb count of elements that can be written to the buffer
1208  * @param cls destination pointer, passed to the libcurl handle
1209  * @return bytes read from stream
1210  */
1211 static size_t
1212 client_receive_put (void *stream,
1213                     size_t size,
1214                     size_t nmemb,
1215                     void *cls)
1216 {
1217   return size * nmemb;
1218 }
1219
1220
1221 /**
1222  * Callback method used with libcurl when data for a GET request are
1223  * received. Forward to MST
1224  *
1225  * @param stream pointer where to write data
1226  * @param size size of an individual element
1227  * @param nmemb count of elements that can be written to the buffer
1228  * @param cls destination pointer, passed to the libcurl handle
1229  * @return bytes read from stream
1230  */
1231 static size_t
1232 client_receive (void *stream,
1233                 size_t size,
1234                 size_t nmemb,
1235                 void *cls)
1236 {
1237   struct GNUNET_ATS_Session *s = cls;
1238   struct GNUNET_TIME_Absolute now;
1239   size_t len = size * nmemb;
1240
1241   LOG (GNUNET_ERROR_TYPE_DEBUG,
1242        "Session %p / request %p: Received %u bytes from peer `%s'\n",
1243        s,
1244        s->get.easyhandle,
1245        len,
1246        GNUNET_i2s (&s->address->peer));
1247   now = GNUNET_TIME_absolute_get ();
1248   if (now.abs_value_us < s->next_receive.abs_value_us)
1249   {
1250     struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
1251     struct GNUNET_TIME_Relative delta
1252       = GNUNET_TIME_absolute_get_difference (now, s->next_receive);
1253
1254     LOG (GNUNET_ERROR_TYPE_DEBUG,
1255          "Session %p / request %p: No inbound bandwidth available! Next read was delayed for %s\n",
1256          s,
1257          s->get.easyhandle,
1258          GNUNET_STRINGS_relative_time_to_string (delta,
1259                                                  GNUNET_YES));
1260     if (s->recv_wakeup_task != NULL)
1261     {
1262       GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
1263       s->recv_wakeup_task = NULL;
1264     }
1265     s->recv_wakeup_task
1266       = GNUNET_SCHEDULER_add_delayed (delta,
1267                                       &client_wake_up,
1268                                       s);
1269     return CURL_WRITEFUNC_PAUSE;
1270   }
1271   if (NULL == s->msg_tk)
1272     s->msg_tk = GNUNET_MST_create (&client_receive_mst_cb,
1273                                    s);
1274   GNUNET_MST_from_buffer (s->msg_tk,
1275                           stream,
1276                           len,
1277                           GNUNET_NO,
1278                           GNUNET_NO);
1279   return len;
1280 }
1281
1282
1283 /**
1284  * Task performing curl operations
1285  *
1286  * @param cls plugin as closure
1287  */
1288 static void
1289 client_run (void *cls)
1290 {
1291   struct HTTP_Client_Plugin *plugin = cls;
1292   int running;
1293   long http_statuscode;
1294   CURLMcode mret;
1295   CURLMsg *msg;
1296   int put_request; /* GNUNET_YES if easy handle is put, GNUNET_NO for get */
1297   int msgs_left;
1298
1299   plugin->client_perform_task = NULL;
1300   /* While data are available or timeouts occured */
1301   do
1302   {
1303     running = 0;
1304     /* Perform operations for all handles */
1305     mret = curl_multi_perform (plugin->curl_multi_handle, &running);
1306
1307     /* Get additional information for all handles */
1308     while (NULL != (msg = curl_multi_info_read (plugin->curl_multi_handle, &msgs_left)))
1309     {
1310       CURL *easy_h = msg->easy_handle;
1311       struct GNUNET_ATS_Session *s = NULL;
1312       char *d = NULL; /* curl requires 'd' to be a 'char *' */
1313
1314       GNUNET_assert (NULL != easy_h);
1315
1316       /* Obtain session from easy handle */
1317       GNUNET_assert (CURLE_OK == curl_easy_getinfo (easy_h, CURLINFO_PRIVATE, &d));
1318       s = (struct GNUNET_ATS_Session *) d;
1319       GNUNET_assert (NULL != s);
1320
1321       if (msg->msg != CURLMSG_DONE)
1322         continue; /* This should not happen */
1323
1324       /* Get HTTP response code */
1325       GNUNET_break (CURLE_OK == curl_easy_getinfo (easy_h,
1326           CURLINFO_RESPONSE_CODE, &http_statuscode));
1327
1328       if (easy_h == s->put.easyhandle)
1329         put_request = GNUNET_YES;
1330       else
1331         put_request = GNUNET_NO;
1332
1333       /* Log status of terminated request */
1334       if  ((0 != msg->data.result) || (http_statuscode != 200))
1335         LOG (GNUNET_ERROR_TYPE_DEBUG,
1336              "Session %p/request %p: %s request to `%s' ended with status %i reason %i: `%s'\n",
1337              s, msg->easy_handle,
1338              (GNUNET_YES == put_request) ? "PUT" : "GET",
1339              GNUNET_i2s (&s->address->peer),
1340              http_statuscode,
1341              msg->data.result,
1342              curl_easy_strerror (msg->data.result));
1343       else
1344         LOG (GNUNET_ERROR_TYPE_DEBUG,
1345              "Session %p/request %p: %s request to `%s' ended normal\n",
1346              s, msg->easy_handle,
1347              (GNUNET_YES == put_request) ? "PUT" : "GET",
1348              GNUNET_i2s (&s->address->peer));
1349
1350       /* Remove easy handle from multi handle */
1351       curl_multi_remove_handle (plugin->curl_multi_handle, easy_h);
1352
1353       /* Clean up easy handle */
1354       curl_easy_cleanup (easy_h);
1355
1356       /* Remove information */
1357       GNUNET_assert (plugin->cur_requests > 0);
1358       plugin->cur_requests--;
1359       LOG  (GNUNET_ERROR_TYPE_INFO,
1360           "%s request to %s done, number of requests decreased to %u\n",
1361           (GNUNET_YES == put_request) ? "PUT" : "GET",
1362           s->url,
1363           plugin->cur_requests);
1364
1365       if (GNUNET_YES == put_request)
1366       {
1367         /* Clean up a PUT request */
1368         s->put.easyhandle = NULL;
1369         s->put.s = NULL;
1370
1371         switch (s->put.state) {
1372           case H_NOT_CONNECTED:
1373           case H_DISCONNECTED:
1374           case H_TMP_DISCONNECTED:
1375             /* This must not happen */
1376             GNUNET_break (0);
1377             break;
1378           case H_TMP_RECONNECT_REQUIRED:
1379             /* Transport called send while disconnect in progess, reconnect */
1380             if (GNUNET_SYSERR == client_connect_put (s))
1381             {
1382               /* Reconnect failed, disconnect session */
1383               http_client_plugin_session_disconnect (plugin, s);
1384             }
1385             break;
1386           case H_TMP_DISCONNECTING:
1387             /* PUT gets temporarily disconnected */
1388             s->put.state = H_TMP_DISCONNECTED;
1389             break;
1390           case H_PAUSED:
1391           case H_CONNECTED:
1392             /* PUT gets permanently disconnected */
1393             s->put.state = H_DISCONNECTED;
1394             http_client_plugin_session_disconnect (plugin, s);
1395             break;
1396           default:
1397             GNUNET_break (0);
1398             break;
1399         }
1400       }
1401       else if (GNUNET_NO == put_request)
1402       {
1403         /* Clean up a GET request */
1404         s->get.easyhandle = NULL;
1405         s->get.s = NULL;
1406
1407         /* If we are emulating an XHR client we need to make another GET
1408          * request.
1409          */
1410         if (GNUNET_YES == plugin->emulate_xhr)
1411         {
1412           if (GNUNET_SYSERR == client_connect_get (s))
1413             http_client_plugin_session_disconnect (plugin, s);
1414         }
1415         else
1416         {
1417           /* GET request was terminated, so disconnect session */
1418           http_client_plugin_session_disconnect (plugin, s);
1419         }
1420       }
1421       else
1422         GNUNET_break (0); /* Must not happen */
1423
1424       GNUNET_STATISTICS_set (plugin->env->stats,
1425                              HTTP_STAT_STR_CONNECTIONS,
1426                              plugin->cur_requests,
1427                              GNUNET_NO);
1428     }
1429   }
1430   while (mret == CURLM_CALL_MULTI_PERFORM);
1431   client_schedule (plugin, GNUNET_NO);
1432 }
1433
1434
1435 #ifdef TCP_STEALTH
1436 /**
1437  * Open TCP socket with TCP STEALTH enabled.
1438  *
1439  * @param clientp our `struct GNUNET_ATS_Session *`
1440  * @param purpose why does curl want to open a socket
1441  * @param address what kind of socket does curl want to have opened?
1442  * @return opened socket
1443  */
1444 static curl_socket_t
1445 open_tcp_stealth_socket_cb (void *clientp,
1446                             curlsocktype purpose,
1447                             struct curl_sockaddr *address)
1448 {
1449   struct GNUNET_ATS_Session *s = clientp;
1450   int ret;
1451
1452   switch (purpose)
1453   {
1454   case CURLSOCKTYPE_IPCXN:
1455     ret = socket (address->family,
1456                   address->socktype,
1457                   address->protocol);
1458     if (-1 == ret)
1459       return CURL_SOCKET_BAD;
1460     if ( ( (SOCK_STREAM != address->socktype) ||
1461            ( (0 != address->protocol) &&
1462              (IPPROTO_TCP != address->protocol))) )
1463       return (curl_socket_t) ret;
1464     if ( (0 != setsockopt (ret,
1465                            IPPROTO_TCP,
1466                            TCP_STEALTH,
1467                            &s->address->peer,
1468                            sizeof (struct GNUNET_PeerIdentity))) )
1469     {
1470       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1471                   _("TCP_STEALTH not supported on this platform.\n"));
1472       (void) close (ret);
1473       return CURL_SOCKET_BAD;
1474     }
1475     return (curl_socket_t) ret;
1476   case CURLSOCKTYPE_ACCEPT:
1477     GNUNET_break (0);
1478     return CURL_SOCKET_BAD;
1479     break;
1480   case CURLSOCKTYPE_LAST:
1481     GNUNET_break (0);
1482     return CURL_SOCKET_BAD;
1483   default:
1484     GNUNET_break (0);
1485     return CURL_SOCKET_BAD;
1486   }
1487 }
1488 #endif
1489
1490
1491 /**
1492  * Connect GET request for a session
1493  *
1494  * @param s the session to connect
1495  * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1496  */
1497 static int
1498 client_connect_get (struct GNUNET_ATS_Session *s)
1499 {
1500   CURLMcode mret;
1501   struct HttpAddress *ha;
1502   uint32_t options;
1503
1504   ha = (struct HttpAddress *) s->address->address;
1505   options = ntohl (ha->options);
1506   /* create get request */
1507   s->get.easyhandle = curl_easy_init ();
1508   s->get.s = s;
1509   if (0 != (options & HTTP_OPTIONS_TCP_STEALTH))
1510   {
1511 #ifdef TCP_STEALTH
1512     curl_easy_setopt (s->get.easyhandle,
1513                       CURLOPT_OPENSOCKETFUNCTION,
1514                       &open_tcp_stealth_socket_cb);
1515     curl_easy_setopt (s->get.easyhandle,
1516                       CURLOPT_OPENSOCKETDATA,
1517                       s);
1518 #else
1519     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1520                 "Cannot connect, TCP STEALTH needed and not supported by kernel.\n");
1521     curl_easy_cleanup (s->get.easyhandle);
1522     s->get.easyhandle = NULL;
1523     s->get.s = NULL;
1524     return GNUNET_SYSERR;
1525 #endif
1526   }
1527
1528 #if VERBOSE_CURL
1529   curl_easy_setopt (s->get.easyhandle,
1530                     CURLOPT_VERBOSE,
1531                     1L);
1532   curl_easy_setopt (s->get.easyhandle,
1533                     CURLOPT_DEBUGFUNCTION,
1534                     &client_log);
1535   curl_easy_setopt (s->get.easyhandle,
1536                     CURLOPT_DEBUGDATA,
1537                     &s->get);
1538 #endif
1539 #if BUILD_HTTPS
1540   curl_easy_setopt (s->get.easyhandle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
1541   {
1542     if (HTTP_OPTIONS_VERIFY_CERTIFICATE ==
1543         (options & HTTP_OPTIONS_VERIFY_CERTIFICATE))
1544     {
1545       curl_easy_setopt (s->get.easyhandle,
1546                         CURLOPT_SSL_VERIFYPEER, 1L);
1547       curl_easy_setopt (s->get.easyhandle,
1548                         CURLOPT_SSL_VERIFYHOST,
1549                         2L);
1550     }
1551     else
1552     {
1553       curl_easy_setopt (s->get.easyhandle,
1554                         CURLOPT_SSL_VERIFYPEER,
1555                         0L);
1556       curl_easy_setopt (s->get.easyhandle,
1557                         CURLOPT_SSL_VERIFYHOST,
1558                         0L);
1559     }
1560   }
1561   curl_easy_setopt (s->get.easyhandle,
1562                     CURLOPT_PROTOCOLS,
1563                     CURLPROTO_HTTPS);
1564   curl_easy_setopt (s->get.easyhandle,
1565                     CURLOPT_REDIR_PROTOCOLS,
1566                     CURLPROTO_HTTPS);
1567 #else
1568   curl_easy_setopt (s->get.easyhandle,
1569                     CURLOPT_PROTOCOLS,
1570                     CURLPROTO_HTTP);
1571   curl_easy_setopt (s->get.easyhandle,
1572                     CURLOPT_REDIR_PROTOCOLS,
1573                     CURLPROTO_HTTP);
1574 #endif
1575
1576   if (NULL != s->plugin->proxy_hostname)
1577   {
1578     curl_easy_setopt (s->get.easyhandle,
1579                       CURLOPT_PROXY,
1580                       s->plugin->proxy_hostname);
1581     curl_easy_setopt (s->get.easyhandle,
1582                       CURLOPT_PROXYTYPE,
1583                       s->plugin->proxytype);
1584     if (NULL != s->plugin->proxy_username)
1585       curl_easy_setopt (s->get.easyhandle,
1586                         CURLOPT_PROXYUSERNAME,
1587                         s->plugin->proxy_username);
1588     if (NULL != s->plugin->proxy_password)
1589       curl_easy_setopt (s->get.easyhandle,
1590                         CURLOPT_PROXYPASSWORD,
1591                         s->plugin->proxy_password);
1592     if (GNUNET_YES == s->plugin->proxy_use_httpproxytunnel)
1593       curl_easy_setopt (s->get.easyhandle,
1594                         CURLOPT_HTTPPROXYTUNNEL,
1595                         s->plugin->proxy_use_httpproxytunnel);
1596   }
1597
1598   if (GNUNET_YES == s->plugin->emulate_xhr)
1599   {
1600     char *url;
1601
1602     GNUNET_asprintf (&url,
1603                      "%s,1",
1604                      s->url);
1605     curl_easy_setopt (s->get.easyhandle,
1606                       CURLOPT_URL,
1607                       url);
1608     GNUNET_free(url);
1609   }
1610   else
1611   {
1612     curl_easy_setopt (s->get.easyhandle,
1613                       CURLOPT_URL,
1614                       s->url);
1615   }
1616   curl_easy_setopt (s->get.easyhandle,
1617                     CURLOPT_READFUNCTION,
1618                     &client_send_cb);
1619   curl_easy_setopt (s->get.easyhandle,
1620                     CURLOPT_READDATA,
1621                     s);
1622   curl_easy_setopt (s->get.easyhandle,
1623                     CURLOPT_WRITEFUNCTION,
1624                     &client_receive);
1625   curl_easy_setopt (s->get.easyhandle,
1626                     CURLOPT_WRITEDATA,
1627                     s);
1628   /* No timeout by default, timeout done with session timeout */
1629   curl_easy_setopt (s->get.easyhandle,
1630                     CURLOPT_TIMEOUT,
1631                     0L);
1632   curl_easy_setopt (s->get.easyhandle,
1633                     CURLOPT_PRIVATE, s);
1634   curl_easy_setopt (s->get.easyhandle,
1635                     CURLOPT_CONNECTTIMEOUT_MS,
1636                     (long) (HTTP_CLIENT_NOT_VALIDATED_TIMEOUT.rel_value_us / 1000LL));
1637   curl_easy_setopt (s->get.easyhandle, CURLOPT_BUFFERSIZE,
1638                     2 * GNUNET_MAX_MESSAGE_SIZE);
1639 #if CURL_TCP_NODELAY
1640   curl_easy_setopt (ps->recv_endpoint,
1641                     CURLOPT_TCP_NODELAY,
1642                     1L);
1643 #endif
1644   curl_easy_setopt (s->get.easyhandle,
1645                     CURLOPT_FOLLOWLOCATION,
1646                     0L);
1647
1648   mret = curl_multi_add_handle (s->plugin->curl_multi_handle,
1649                                 s->get.easyhandle);
1650   if (CURLM_OK != mret)
1651   {
1652     LOG (GNUNET_ERROR_TYPE_ERROR,
1653          "Session %p : Failed to add GET handle to multihandle: `%s'\n",
1654          s,
1655          curl_multi_strerror (mret));
1656     curl_easy_cleanup (s->get.easyhandle);
1657     s->get.easyhandle = NULL;
1658     s->get.s = NULL;
1659     GNUNET_break (0);
1660     return GNUNET_SYSERR;
1661   }
1662   s->plugin->cur_requests++;
1663   LOG (GNUNET_ERROR_TYPE_INFO,
1664        "GET request `%s' established, number of requests increased to %u\n",
1665        s->url,
1666        s->plugin->cur_requests);
1667   return GNUNET_OK;
1668 }
1669
1670
1671 /**
1672  * Connect a HTTP put request
1673  *
1674  * @param s the session to connect
1675  * @return #GNUNET_SYSERR for hard failure, #GNUNET_OK for ok
1676  */
1677 static int
1678 client_connect_put (struct GNUNET_ATS_Session *s)
1679 {
1680   CURLMcode mret;
1681   struct HttpAddress *ha;
1682   uint32_t options;
1683
1684   ha = (struct HttpAddress *) s->address->address;
1685   options = ntohl (ha->options);
1686   /* create put request */
1687   LOG (GNUNET_ERROR_TYPE_DEBUG,
1688        "Session %p: Init PUT handle\n",
1689        s);
1690   s->put.easyhandle = curl_easy_init ();
1691   s->put.s = s;
1692 #if VERBOSE_CURL
1693   curl_easy_setopt (s->put.easyhandle,
1694                     CURLOPT_VERBOSE,
1695                     1L);
1696   curl_easy_setopt (s->put.easyhandle,
1697                     CURLOPT_DEBUGFUNCTION,
1698                     &client_log);
1699   curl_easy_setopt (s->put.easyhandle,
1700                     CURLOPT_DEBUGDATA,
1701                     &s->put);
1702 #endif
1703   if (0 != (options & HTTP_OPTIONS_TCP_STEALTH))
1704   {
1705 #ifdef TCP_STEALTH
1706     curl_easy_setopt (s->put.easyhandle,
1707                       CURLOPT_OPENSOCKETFUNCTION,
1708                       &open_tcp_stealth_socket_cb);
1709     curl_easy_setopt (s->put.easyhandle,
1710                       CURLOPT_OPENSOCKETDATA,
1711                       s);
1712 #else
1713     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1714                 "Cannot connect, TCP STEALTH needed and not supported by kernel.\n");
1715     curl_easy_cleanup (s->put.easyhandle);
1716     s->put.easyhandle = NULL;
1717     s->put.s = NULL;
1718     s->put.state = H_DISCONNECTED;
1719     return GNUNET_SYSERR;
1720 #endif
1721   }
1722 #if BUILD_HTTPS
1723   curl_easy_setopt (s->put.easyhandle,
1724                     CURLOPT_SSLVERSION,
1725                     CURL_SSLVERSION_TLSv1);
1726   {
1727     struct HttpAddress *ha;
1728     ha = (struct HttpAddress *) s->address->address;
1729
1730     if (HTTP_OPTIONS_VERIFY_CERTIFICATE ==
1731         (ntohl (ha->options) & HTTP_OPTIONS_VERIFY_CERTIFICATE))
1732     {
1733       curl_easy_setopt (s->put.easyhandle,
1734                         CURLOPT_SSL_VERIFYPEER,
1735                         1L);
1736       curl_easy_setopt (s->put.easyhandle,
1737                         CURLOPT_SSL_VERIFYHOST,
1738                         2L);
1739     }
1740     else
1741     {
1742       curl_easy_setopt (s->put.easyhandle,
1743                         CURLOPT_SSL_VERIFYPEER,
1744                         0L);
1745       curl_easy_setopt (s->put.easyhandle,
1746                         CURLOPT_SSL_VERIFYHOST,
1747                         0L);
1748     }
1749   }
1750   curl_easy_setopt (s->put.easyhandle,
1751                     CURLOPT_PROTOCOLS,
1752                     CURLPROTO_HTTPS);
1753   curl_easy_setopt (s->put.easyhandle,
1754                     CURLOPT_REDIR_PROTOCOLS,
1755                     CURLPROTO_HTTPS);
1756 #else
1757   curl_easy_setopt (s->put.easyhandle,
1758                     CURLOPT_PROTOCOLS,
1759                     CURLPROTO_HTTP);
1760   curl_easy_setopt (s->put.easyhandle,
1761                     CURLOPT_REDIR_PROTOCOLS,
1762                     CURLPROTO_HTTP);
1763 #endif
1764   if (NULL != s->plugin->proxy_hostname)
1765   {
1766     curl_easy_setopt (s->put.easyhandle,
1767                       CURLOPT_PROXY,
1768                       s->plugin->proxy_hostname);
1769     curl_easy_setopt (s->put.easyhandle,
1770                       CURLOPT_PROXYTYPE,
1771                       s->plugin->proxytype);
1772     if (NULL != s->plugin->proxy_username)
1773       curl_easy_setopt (s->put.easyhandle,
1774                         CURLOPT_PROXYUSERNAME,
1775                         s->plugin->proxy_username);
1776     if (NULL != s->plugin->proxy_password)
1777       curl_easy_setopt (s->put.easyhandle,
1778                         CURLOPT_PROXYPASSWORD,
1779                         s->plugin->proxy_password);
1780     if (GNUNET_YES == s->plugin->proxy_use_httpproxytunnel)
1781       curl_easy_setopt (s->put.easyhandle,
1782                         CURLOPT_HTTPPROXYTUNNEL,
1783                         s->plugin->proxy_use_httpproxytunnel);
1784   }
1785
1786   curl_easy_setopt (s->put.easyhandle,
1787                     CURLOPT_URL,
1788                     s->url);
1789   curl_easy_setopt (s->put.easyhandle,
1790                     CURLOPT_UPLOAD,
1791                     1L);
1792   curl_easy_setopt (s->put.easyhandle,
1793                     CURLOPT_READFUNCTION,
1794                     &client_send_cb);
1795   curl_easy_setopt (s->put.easyhandle,
1796                     CURLOPT_READDATA,
1797                     s);
1798   curl_easy_setopt (s->put.easyhandle,
1799                     CURLOPT_WRITEFUNCTION,
1800                     &client_receive_put);
1801   curl_easy_setopt (s->put.easyhandle,
1802                     CURLOPT_WRITEDATA,
1803                     s);
1804   /* No timeout by default, timeout done with session timeout */
1805   curl_easy_setopt (s->put.easyhandle,
1806                     CURLOPT_TIMEOUT,
1807                     0L);
1808   curl_easy_setopt (s->put.easyhandle,
1809                     CURLOPT_PRIVATE,
1810                     s);
1811   curl_easy_setopt (s->put.easyhandle,
1812                     CURLOPT_CONNECTTIMEOUT_MS,
1813                     (long) (HTTP_CLIENT_NOT_VALIDATED_TIMEOUT.rel_value_us / 1000LL));
1814   curl_easy_setopt (s->put.easyhandle, CURLOPT_BUFFERSIZE,
1815                     2 * GNUNET_MAX_MESSAGE_SIZE);
1816 #if CURL_TCP_NODELAY
1817   curl_easy_setopt (s->put.easyhandle, CURLOPT_TCP_NODELAY, 1);
1818 #endif
1819   mret = curl_multi_add_handle (s->plugin->curl_multi_handle,
1820                                 s->put.easyhandle);
1821   if (CURLM_OK != mret)
1822   {
1823     LOG (GNUNET_ERROR_TYPE_ERROR,
1824          "Session %p : Failed to add PUT handle to multihandle: `%s'\n",
1825          s, curl_multi_strerror (mret));
1826     curl_easy_cleanup (s->put.easyhandle);
1827     s->put.easyhandle = NULL;
1828     s->put.s = NULL;
1829     s->put.state = H_DISCONNECTED;
1830     return GNUNET_SYSERR;
1831   }
1832   s->put.state = H_CONNECTED;
1833   s->plugin->cur_requests++;
1834
1835   LOG  (GNUNET_ERROR_TYPE_INFO,
1836       "PUT request `%s' established, number of requests increased to %u\n",
1837       s->url, s->plugin->cur_requests);
1838
1839   return GNUNET_OK;
1840 }
1841
1842
1843 /**
1844  * Connect both PUT and GET request for a session
1845  *
1846  * @param s the session to connect
1847  * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1848  */
1849 static int
1850 client_connect (struct GNUNET_ATS_Session *s)
1851 {
1852   struct HTTP_Client_Plugin *plugin = s->plugin;
1853   int res = GNUNET_OK;
1854
1855   /* create url */
1856   if (NULL ==
1857       http_common_plugin_address_to_string(plugin->protocol,
1858                                            s->address->address,
1859                                            s->address->address_length))
1860   {
1861     LOG (GNUNET_ERROR_TYPE_DEBUG,
1862          "Invalid address peer `%s'\n",
1863          GNUNET_i2s(&s->address->peer));
1864     return GNUNET_SYSERR;
1865   }
1866
1867   GNUNET_asprintf (&s->url,
1868                    "%s/%s;%u",
1869                    http_common_plugin_address_to_url (NULL,
1870                                                       s->address->address,
1871                                                       s->address->address_length),
1872                    GNUNET_i2s_full (plugin->env->my_identity),
1873                    plugin->last_tag);
1874
1875   plugin->last_tag++;
1876   LOG (GNUNET_ERROR_TYPE_DEBUG,
1877        "Initiating outbound session peer `%s' using address `%s'\n",
1878        GNUNET_i2s (&s->address->peer), s->url);
1879
1880   if (GNUNET_SYSERR == client_connect_get (s))
1881     return GNUNET_SYSERR;
1882   /* If we are emulating an XHR client then delay sending a PUT request until
1883    * there is something to send.
1884    */
1885   if (GNUNET_YES == plugin->emulate_xhr)
1886   {
1887     s->put.state = H_TMP_DISCONNECTED;
1888   }
1889   else if (GNUNET_SYSERR == client_connect_put (s))
1890     return GNUNET_SYSERR;
1891
1892   LOG (GNUNET_ERROR_TYPE_DEBUG,
1893        "Session %p: connected with GET %p and PUT %p\n",
1894        s, s->get.easyhandle,
1895        s->put.easyhandle);
1896   /* Perform connect */
1897   GNUNET_STATISTICS_set (plugin->env->stats,
1898                          HTTP_STAT_STR_CONNECTIONS,
1899                          plugin->cur_requests,
1900                          GNUNET_NO);
1901   /* Re-schedule since handles have changed */
1902   if (NULL != plugin->client_perform_task)
1903   {
1904     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
1905     plugin->client_perform_task = NULL;
1906   }
1907
1908   /* Schedule task to run immediately */
1909   plugin->client_perform_task = GNUNET_SCHEDULER_add_now (client_run,
1910                                                           plugin);
1911   return res;
1912 }
1913
1914
1915 /**
1916  * Function obtain the network type for a session
1917  *
1918  * @param cls closure (`struct Plugin*`)
1919  * @param session the session
1920  * @return the network type
1921  */
1922 static enum GNUNET_NetworkType
1923 http_client_plugin_get_network (void *cls,
1924                                 struct GNUNET_ATS_Session *session)
1925 {
1926   return session->scope;
1927 }
1928
1929
1930 /**
1931  * Function obtain the network type for an address.
1932  *
1933  * @param cls closure (`struct Plugin *`)
1934  * @param address the address
1935  * @return the network type
1936  */
1937 static enum GNUNET_NetworkType
1938 http_client_plugin_get_network_for_address (void *cls,
1939                                             const struct GNUNET_HELLO_Address *address)
1940 {
1941   struct HTTP_Client_Plugin *plugin = cls;
1942
1943   return http_common_get_network_for_address (plugin->env,
1944                                               address);
1945 }
1946
1947
1948 /**
1949  * Session was idle, so disconnect it
1950  *
1951  * @param cls the `struct GNUNET_ATS_Session` of the idle session
1952  */
1953 static void
1954 client_session_timeout (void *cls)
1955 {
1956   struct GNUNET_ATS_Session *s = cls;
1957   struct GNUNET_TIME_Relative left;
1958
1959   s->timeout_task = NULL;
1960   left = GNUNET_TIME_absolute_get_remaining (s->timeout);
1961   if (0 != left.rel_value_us)
1962   {
1963     /* not actually our turn yet, but let's at least update
1964        the monitor, it may think we're about to die ... */
1965     notify_session_monitor (s->plugin,
1966                             s,
1967                             GNUNET_TRANSPORT_SS_UPDATE);
1968     s->timeout_task = GNUNET_SCHEDULER_add_delayed (left,
1969                                                     &client_session_timeout,
1970                                                     s);
1971     return;
1972   }
1973   LOG (TIMEOUT_LOG,
1974        "Session %p was idle for %s, disconnecting\n",
1975        s,
1976        GNUNET_STRINGS_relative_time_to_string (HTTP_CLIENT_SESSION_TIMEOUT,
1977                                                GNUNET_YES));
1978   GNUNET_assert (GNUNET_OK ==
1979                  http_client_plugin_session_disconnect (s->plugin,
1980                                                  s));
1981 }
1982
1983
1984 /**
1985  * Creates a new outbound session the transport service will use to
1986  * send data to the peer
1987  *
1988  * @param cls the plugin
1989  * @param address the address
1990  * @return the session or NULL of max connections exceeded
1991  */
1992 static struct GNUNET_ATS_Session *
1993 http_client_plugin_get_session (void *cls,
1994                                 const struct GNUNET_HELLO_Address *address)
1995 {
1996   struct HTTP_Client_Plugin *plugin = cls;
1997   struct GNUNET_ATS_Session *s;
1998   struct sockaddr *sa;
1999   enum GNUNET_NetworkType net_type;
2000   size_t salen = 0;
2001   int res;
2002
2003   GNUNET_assert (NULL != address->address);
2004
2005   /* find existing session */
2006   s = client_lookup_session (plugin, address);
2007   if (NULL != s)
2008     return s;
2009
2010   /* create a new session */
2011   if (plugin->max_requests <= plugin->cur_requests)
2012   {
2013     LOG (GNUNET_ERROR_TYPE_WARNING,
2014          "Maximum number of requests (%u) reached: "
2015          "cannot connect to peer `%s'\n",
2016          plugin->max_requests,
2017          GNUNET_i2s (&address->peer));
2018     return NULL;
2019   }
2020
2021   /* Determine network location */
2022   net_type = GNUNET_NT_UNSPECIFIED;
2023   sa = http_common_socket_from_address (address->address,
2024                                         address->address_length,
2025                                         &res);
2026   if (GNUNET_SYSERR == res)
2027     return NULL;
2028   if (GNUNET_YES == res)
2029   {
2030     GNUNET_assert (NULL != sa);
2031     if (AF_INET == sa->sa_family)
2032     {
2033       salen = sizeof (struct sockaddr_in);
2034     }
2035     else if (AF_INET6 == sa->sa_family)
2036     {
2037       salen = sizeof (struct sockaddr_in6);
2038     }
2039     net_type = plugin->env->get_address_type (plugin->env->cls, sa, salen);
2040     GNUNET_free (sa);
2041   }
2042   else if (GNUNET_NO == res)
2043   {
2044     /* Cannot convert to sockaddr -> is external hostname */
2045     net_type = GNUNET_NT_WAN;
2046   }
2047   if (GNUNET_NT_UNSPECIFIED == net_type)
2048   {
2049     GNUNET_break (0);
2050     return NULL;
2051   }
2052
2053   s = GNUNET_new (struct GNUNET_ATS_Session);
2054   s->plugin = plugin;
2055   s->address = GNUNET_HELLO_address_copy (address);
2056   s->scope = net_type;
2057
2058   s->put.state = H_NOT_CONNECTED;
2059   s->timeout = GNUNET_TIME_relative_to_absolute (HTTP_CLIENT_SESSION_TIMEOUT);
2060   s->timeout_task =  GNUNET_SCHEDULER_add_delayed (HTTP_CLIENT_SESSION_TIMEOUT,
2061                                                    &client_session_timeout,
2062                                                    s);
2063   LOG (GNUNET_ERROR_TYPE_DEBUG,
2064        "Created new session %p for `%s' address `%s''\n",
2065        s,
2066        http_common_plugin_address_to_string (plugin->protocol,
2067                                              s->address->address,
2068                                              s->address->address_length),
2069        GNUNET_i2s (&s->address->peer));
2070
2071   /* add new session */
2072   (void) GNUNET_CONTAINER_multipeermap_put (plugin->sessions,
2073                                             &s->address->peer,
2074                                             s,
2075                                             GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
2076   /* initiate new connection */
2077   if (GNUNET_SYSERR == client_connect (s))
2078   {
2079     LOG (GNUNET_ERROR_TYPE_ERROR,
2080          "Cannot connect to peer `%s' address `%s''\n",
2081          http_common_plugin_address_to_string (plugin->protocol,
2082              s->address->address, s->address->address_length),
2083              GNUNET_i2s (&s->address->peer));
2084     client_delete_session (s);
2085     return NULL;
2086   }
2087   notify_session_monitor (plugin,
2088                           s,
2089                           GNUNET_TRANSPORT_SS_INIT);
2090   notify_session_monitor (plugin,
2091                           s,
2092                           GNUNET_TRANSPORT_SS_UP); /* or handshake? */
2093   return s;
2094 }
2095
2096
2097 /**
2098  * Setup http_client plugin
2099  *
2100  * @param plugin the plugin handle
2101  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
2102  */
2103 static int
2104 client_start (struct HTTP_Client_Plugin *plugin)
2105 {
2106   curl_global_init (CURL_GLOBAL_ALL);
2107   plugin->curl_multi_handle = curl_multi_init ();
2108
2109   if (NULL == plugin->curl_multi_handle)
2110   {
2111     LOG (GNUNET_ERROR_TYPE_ERROR,
2112          _("Could not initialize curl multi handle, failed to start %s plugin!\n"),
2113          plugin->name);
2114     return GNUNET_SYSERR;
2115   }
2116   return GNUNET_OK;
2117 }
2118
2119
2120 /**
2121  * Another peer has suggested an address for this
2122  * peer and transport plugin.  Check that this could be a valid
2123  * address.  If so, consider adding it to the list
2124  * of addresses.
2125  *
2126  * @param cls closure with the `struct Plugin`
2127  * @param addr pointer to the address
2128  * @param addrlen length of @a addr
2129  * @return #GNUNET_OK if this is a plausible address for this peer
2130  *         and transport; always returns #GNUNET_NO (this is the client!)
2131  */
2132 static int
2133 http_client_plugin_address_suggested (void *cls,
2134                                       const void *addr,
2135                                       size_t addrlen)
2136 {
2137   /* A HTTP/S client does not have any valid address so:*/
2138   return GNUNET_NO;
2139 }
2140
2141
2142 /**
2143  * Exit point from the plugin.
2144  *
2145  * @param cls api as closure
2146  * @return NULL
2147  */
2148 void *
2149 LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls)
2150 {
2151   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
2152   struct HTTP_Client_Plugin *plugin = api->cls;
2153
2154   if (NULL == api->cls)
2155   {
2156     /* Stub shutdown */
2157     GNUNET_free (api);
2158     return NULL;
2159   }
2160   LOG (GNUNET_ERROR_TYPE_DEBUG,
2161        _("Shutting down plugin `%s'\n"),
2162        plugin->name);
2163   GNUNET_CONTAINER_multipeermap_iterate (plugin->sessions,
2164                                          &destroy_session_cb,
2165                                          plugin);
2166   if (NULL != plugin->client_perform_task)
2167   {
2168     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
2169     plugin->client_perform_task = NULL;
2170   }
2171   if (NULL != plugin->curl_multi_handle)
2172   {
2173     curl_multi_cleanup (plugin->curl_multi_handle);
2174     plugin->curl_multi_handle = NULL;
2175   }
2176   curl_global_cleanup ();
2177   LOG (GNUNET_ERROR_TYPE_DEBUG,
2178        _("Shutdown for plugin `%s' complete\n"),
2179        plugin->name);
2180   GNUNET_CONTAINER_multipeermap_destroy (plugin->sessions);
2181   GNUNET_free_non_null (plugin->proxy_hostname);
2182   GNUNET_free_non_null (plugin->proxy_username);
2183   GNUNET_free_non_null (plugin->proxy_password);
2184   GNUNET_free (plugin);
2185   GNUNET_free (api);
2186   return NULL;
2187 }
2188
2189
2190 /**
2191  * Configure plugin
2192  *
2193  * @param plugin the plugin handle
2194  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
2195  */
2196 static int
2197 client_configure_plugin (struct HTTP_Client_Plugin *plugin)
2198 {
2199   unsigned long long max_requests;
2200   char *proxy_type;
2201
2202   /* Optional parameters */
2203   if (GNUNET_OK !=
2204       GNUNET_CONFIGURATION_get_value_number (plugin->env->cfg,
2205                                              plugin->name,
2206                                              "MAX_CONNECTIONS",
2207                                              &max_requests))
2208     max_requests = 128;
2209   plugin->max_requests = max_requests;
2210
2211   LOG (GNUNET_ERROR_TYPE_DEBUG,
2212        _("Maximum number of requests is %u\n"),
2213        plugin->max_requests);
2214
2215   /* Read proxy configuration */
2216   if (GNUNET_OK ==
2217       GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
2218                                              plugin->name,
2219                                              "PROXY",
2220                                              &plugin->proxy_hostname))
2221   {
2222     LOG (GNUNET_ERROR_TYPE_DEBUG,
2223          "Found proxy host: `%s'\n",
2224          plugin->proxy_hostname);
2225     /* proxy username */
2226     if (GNUNET_OK ==
2227         GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
2228                                                plugin->name,
2229                                                "PROXY_USERNAME",
2230                                                &plugin->proxy_username))
2231     {
2232       LOG (GNUNET_ERROR_TYPE_DEBUG,
2233            "Found proxy username name: `%s'\n",
2234            plugin->proxy_username);
2235     }
2236
2237     /* proxy password */
2238     if (GNUNET_OK ==
2239         GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
2240                                                plugin->name,
2241                                                "PROXY_PASSWORD",
2242                                                &plugin->proxy_password))
2243     {
2244       LOG (GNUNET_ERROR_TYPE_DEBUG,
2245            "Found proxy password name: `%s'\n",
2246            plugin->proxy_password);
2247     }
2248
2249     /* proxy type */
2250     if (GNUNET_OK ==
2251         GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
2252                                                plugin->name,
2253                                                "PROXY_TYPE",
2254                                                &proxy_type))
2255     {
2256       GNUNET_STRINGS_utf8_toupper (proxy_type, proxy_type);
2257
2258       if (0 == strcmp(proxy_type, "HTTP"))
2259         plugin->proxytype = CURLPROXY_HTTP;
2260       else if (0 == strcmp(proxy_type, "SOCKS4"))
2261         plugin->proxytype = CURLPROXY_SOCKS4;
2262       else if (0 == strcmp(proxy_type, "SOCKS5"))
2263         plugin->proxytype = CURLPROXY_SOCKS5;
2264       else if (0 == strcmp(proxy_type, "SOCKS4A"))
2265         plugin->proxytype = CURLPROXY_SOCKS4A;
2266       else if (0 == strcmp(proxy_type, "SOCKS5_HOSTNAME "))
2267         plugin->proxytype = CURLPROXY_SOCKS5_HOSTNAME ;
2268       else
2269       {
2270         LOG (GNUNET_ERROR_TYPE_ERROR,
2271              _("Invalid proxy type: `%s', disabling proxy! Check configuration!\n"),
2272              proxy_type);
2273
2274         GNUNET_free (proxy_type);
2275         GNUNET_free (plugin->proxy_hostname);
2276         plugin->proxy_hostname = NULL;
2277         GNUNET_free_non_null (plugin->proxy_username);
2278         plugin->proxy_username = NULL;
2279         GNUNET_free_non_null (plugin->proxy_password);
2280         plugin->proxy_password = NULL;
2281
2282         return GNUNET_SYSERR;
2283       }
2284
2285       LOG (GNUNET_ERROR_TYPE_DEBUG,
2286            "Found proxy type: `%s'\n",
2287            proxy_type);
2288     }
2289
2290     /* proxy http tunneling */
2291     plugin->proxy_use_httpproxytunnel
2292       = GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg,
2293                                               plugin->name,
2294                                               "PROXY_HTTP_TUNNELING");
2295     if (GNUNET_SYSERR == plugin->proxy_use_httpproxytunnel)
2296       plugin->proxy_use_httpproxytunnel = GNUNET_NO;
2297
2298     GNUNET_free_non_null (proxy_type);
2299   }
2300
2301   /* Should we emulate an XHR client for testing? */
2302   plugin->emulate_xhr
2303     = GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg,
2304                                             plugin->name,
2305                                             "EMULATE_XHR");
2306   return GNUNET_OK;
2307 }
2308
2309
2310 /**
2311  * Function to convert an address to a human-readable string.
2312  *
2313  * @param cls closure
2314  * @param addr address to convert
2315  * @param addrlen address length
2316  * @return res string if conversion was successful, NULL otherwise
2317  */
2318 static const char *
2319 http_client_plugin_address_to_string (void *cls,
2320                                       const void *addr,
2321                                       size_t addrlen)
2322 {
2323   return http_common_plugin_address_to_string (PLUGIN_NAME,
2324                                                addr,
2325                                                addrlen);
2326 }
2327
2328
2329 /**
2330  * Function that will be called whenever the transport service wants to
2331  * notify the plugin that a session is still active and in use and
2332  * therefore the session timeout for this session has to be updated
2333  *
2334  * @param cls closure
2335  * @param peer which peer was the session for
2336  * @param session which session is being updated
2337  */
2338 static void
2339 http_client_plugin_update_session_timeout (void *cls,
2340                                            const struct GNUNET_PeerIdentity *peer,
2341                                            struct GNUNET_ATS_Session *session)
2342 {
2343   client_reschedule_session_timeout (session);
2344 }
2345
2346
2347 /**
2348  * Function that will be called whenever the transport service wants to
2349  * notify the plugin that the inbound quota changed and that the plugin
2350  * should update it's delay for the next receive value
2351  *
2352  * @param cls closure
2353  * @param peer which peer was the session for
2354  * @param s which session is being updated
2355  * @param delay new delay to use for receiving
2356  */
2357 static void
2358 http_client_plugin_update_inbound_delay (void *cls,
2359                                          const struct GNUNET_PeerIdentity *peer,
2360                                          struct GNUNET_ATS_Session *s,
2361                                          struct GNUNET_TIME_Relative delay)
2362 {
2363   s->next_receive = GNUNET_TIME_relative_to_absolute (delay);
2364   LOG (GNUNET_ERROR_TYPE_DEBUG,
2365        "New inbound delay %s\n",
2366        GNUNET_STRINGS_relative_time_to_string (delay,
2367                                                GNUNET_NO));
2368   if (s->recv_wakeup_task != NULL)
2369   {
2370     GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
2371     s->recv_wakeup_task
2372       = GNUNET_SCHEDULER_add_delayed (delay,
2373                                       &client_wake_up,
2374                                       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 GNUNET_ATS_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 GNUNET_ATS_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->get_network_for_address = &http_client_plugin_get_network_for_address;
2477   api->update_session_timeout = &http_client_plugin_update_session_timeout;
2478   api->update_inbound_delay = &http_client_plugin_update_inbound_delay;
2479   api->setup_monitor = &http_client_plugin_setup_monitor;
2480 #if BUILD_HTTPS
2481   plugin->name = "transport-https_client";
2482   plugin->protocol = "https";
2483 #else
2484   plugin->name = "transport-http_client";
2485   plugin->protocol = "http";
2486 #endif
2487   plugin->last_tag = 1;
2488
2489   if (GNUNET_SYSERR == client_configure_plugin (plugin))
2490   {
2491     LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
2492     return NULL;
2493   }
2494
2495   /* Start client */
2496   if (GNUNET_SYSERR == client_start (plugin))
2497   {
2498     LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
2499     return NULL;
2500   }
2501   return api;
2502 }
2503
2504 /* end of plugin_transport_http_client.c */