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