87ad1fb60997d96594bd00e916324a80500af814
[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 done, number of requests decreased to %u\n",
1355           (GNUNET_YES == put_request) ? "PUT" : "GET",
1356           plugin->cur_requests);
1357
1358       if (GNUNET_YES == put_request)
1359       {
1360         /* Clean up a PUT request */
1361         s->put.easyhandle = NULL;
1362         s->put.s = NULL;
1363
1364         switch (s->put.state) {
1365           case H_NOT_CONNECTED:
1366           case H_DISCONNECTED:
1367           case H_TMP_DISCONNECTED:
1368             /* This must not happen */
1369             GNUNET_break (0);
1370             break;
1371           case H_TMP_RECONNECT_REQUIRED:
1372             /* Transport called send while disconnect in progess, reconnect */
1373             if (GNUNET_SYSERR == client_connect_put (s))
1374             {
1375               /* Reconnect failed, disconnect session */
1376               http_client_plugin_session_disconnect (plugin, s);
1377             }
1378             break;
1379           case H_TMP_DISCONNECTING:
1380             /* PUT gets temporarily disconnected */
1381             s->put.state = H_TMP_DISCONNECTED;
1382             break;
1383           case H_PAUSED:
1384           case H_CONNECTED:
1385             /* PUT gets permanently disconnected */
1386             s->put.state = H_DISCONNECTED;
1387             http_client_plugin_session_disconnect (plugin, s);
1388             break;
1389           default:
1390             GNUNET_break (0);
1391             break;
1392         }
1393       }
1394       else if (GNUNET_NO == put_request)
1395       {
1396         /* Clean up a GET request */
1397         s->get.easyhandle = NULL;
1398         s->get.s = NULL;
1399
1400         /* If we are emulating an XHR client we need to make another GET
1401          * request.
1402          */
1403         if (GNUNET_YES == plugin->emulate_xhr)
1404         {
1405           if (GNUNET_SYSERR == client_connect_get (s))
1406             http_client_plugin_session_disconnect (plugin, s);
1407         }
1408         else
1409         {
1410           /* GET request was terminated, so disconnect session */
1411           http_client_plugin_session_disconnect (plugin, s);
1412         }
1413       }
1414       else
1415         GNUNET_break (0); /* Must not happen */
1416     }
1417   }
1418   while (mret == CURLM_CALL_MULTI_PERFORM);
1419   client_schedule (plugin, GNUNET_NO);
1420 }
1421
1422
1423 /**
1424  * Connect GET request for a session
1425  *
1426  * @param s the session to connect
1427  * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1428  */
1429 static int
1430 client_connect_get (struct Session *s)
1431 {
1432   CURLMcode mret;
1433
1434   /* create get request */
1435   s->get.easyhandle = curl_easy_init ();
1436   s->get.s = s;
1437 #if VERBOSE_CURL
1438   curl_easy_setopt (s->get.easyhandle, CURLOPT_VERBOSE, 1L);
1439   curl_easy_setopt (s->get.easyhandle, CURLOPT_DEBUGFUNCTION, &client_log);
1440   curl_easy_setopt (s->get.easyhandle, CURLOPT_DEBUGDATA, &s->get);
1441 #endif
1442 #if BUILD_HTTPS
1443   curl_easy_setopt (s->get.easyhandle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
1444   {
1445     struct HttpAddress *ha;
1446
1447     ha = (struct HttpAddress *) s->address->address;
1448
1449     if (HTTP_OPTIONS_VERIFY_CERTIFICATE ==
1450         (ntohl (ha->options) & HTTP_OPTIONS_VERIFY_CERTIFICATE))
1451     {
1452       curl_easy_setopt (s->get.easyhandle, CURLOPT_SSL_VERIFYPEER, 1L);
1453       curl_easy_setopt (s->get.easyhandle, CURLOPT_SSL_VERIFYHOST, 2L);
1454     }
1455     else
1456     {
1457       curl_easy_setopt (s->get.easyhandle, CURLOPT_SSL_VERIFYPEER, 0);
1458       curl_easy_setopt (s->get.easyhandle, CURLOPT_SSL_VERIFYHOST, 0);
1459     }
1460   }
1461   curl_easy_setopt (s->get.easyhandle, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
1462   curl_easy_setopt (s->get.easyhandle, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS);
1463 #else
1464   curl_easy_setopt (s->get.easyhandle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP);
1465   curl_easy_setopt (s->get.easyhandle, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP);
1466 #endif
1467
1468   if (NULL != s->plugin->proxy_hostname)
1469   {
1470     curl_easy_setopt (s->get.easyhandle, CURLOPT_PROXY, s->plugin->proxy_hostname);
1471     curl_easy_setopt (s->get.easyhandle, CURLOPT_PROXYTYPE, s->plugin->proxytype);
1472     if (NULL != s->plugin->proxy_username)
1473       curl_easy_setopt (s->get.easyhandle, CURLOPT_PROXYUSERNAME,
1474           s->plugin->proxy_username);
1475     if (NULL != s->plugin->proxy_password)
1476       curl_easy_setopt (s->get.easyhandle, CURLOPT_PROXYPASSWORD,
1477           s->plugin->proxy_password);
1478     if (GNUNET_YES == s->plugin->proxy_use_httpproxytunnel)
1479       curl_easy_setopt (s->get.easyhandle, CURLOPT_HTTPPROXYTUNNEL,
1480           s->plugin->proxy_use_httpproxytunnel);
1481   }
1482
1483   if (GNUNET_YES == s->plugin->emulate_xhr)
1484   {
1485     char *url;
1486
1487     GNUNET_asprintf(&url, "%s,1", s->url);
1488     curl_easy_setopt (s->get.easyhandle, CURLOPT_URL, url);
1489     GNUNET_free(url);
1490   } else
1491     curl_easy_setopt (s->get.easyhandle, CURLOPT_URL, s->url);
1492   //curl_easy_setopt (s->get.easyhandle, CURLOPT_HEADERFUNCTION, &curl_get_header_cb);
1493   //curl_easy_setopt (s->get.easyhandle, CURLOPT_WRITEHEADER, ps);
1494   curl_easy_setopt (s->get.easyhandle, CURLOPT_READFUNCTION, client_send_cb);
1495   curl_easy_setopt (s->get.easyhandle, CURLOPT_READDATA, s);
1496   curl_easy_setopt (s->get.easyhandle, CURLOPT_WRITEFUNCTION, client_receive);
1497   curl_easy_setopt (s->get.easyhandle, CURLOPT_WRITEDATA, s);
1498   /* No timeout by default, timeout done with session timeout */
1499   curl_easy_setopt (s->get.easyhandle, CURLOPT_TIMEOUT, 0);
1500   curl_easy_setopt (s->get.easyhandle, CURLOPT_PRIVATE, s);
1501   curl_easy_setopt (s->get.easyhandle, CURLOPT_CONNECTTIMEOUT_MS,
1502                     (long) (HTTP_CLIENT_NOT_VALIDATED_TIMEOUT.rel_value_us / 1000LL));
1503   curl_easy_setopt (s->get.easyhandle, CURLOPT_BUFFERSIZE,
1504                     2 * GNUNET_SERVER_MAX_MESSAGE_SIZE);
1505 #if CURL_TCP_NODELAY
1506   curl_easy_setopt (ps->recv_endpoint, CURLOPT_TCP_NODELAY, 1);
1507 #endif
1508   curl_easy_setopt (s->get.easyhandle, CURLOPT_FOLLOWLOCATION, 0);
1509
1510   mret = curl_multi_add_handle (s->plugin->curl_multi_handle,
1511                                 s->get.easyhandle);
1512   if (CURLM_OK != mret)
1513   {
1514     LOG (GNUNET_ERROR_TYPE_ERROR,
1515          "Session %p : Failed to add GET handle to multihandle: `%s'\n",
1516          s,
1517          curl_multi_strerror (mret));
1518     curl_easy_cleanup (s->get.easyhandle);
1519     s->get.easyhandle = NULL;
1520     s->get.s = NULL;
1521     s->get.easyhandle = NULL;
1522     GNUNET_break (0);
1523     return GNUNET_SYSERR;
1524   }
1525   s->plugin->cur_requests++;
1526   LOG  (GNUNET_ERROR_TYPE_INFO,
1527       "GET request `%s' established, number of requests increased to %u\n",
1528       s->url, s->plugin->cur_requests);
1529   return GNUNET_OK;
1530 }
1531
1532
1533 /**
1534  * Connect a HTTP put request
1535  *
1536  * @param s the session to connect
1537  * @return #GNUNET_SYSERR for hard failure, #GNUNET_OK for ok
1538  */
1539 static int
1540 client_connect_put (struct Session *s)
1541 {
1542   CURLMcode mret;
1543
1544   /* create put request */
1545   LOG (GNUNET_ERROR_TYPE_DEBUG,
1546        "Session %p: Init PUT handle\n", s);
1547   s->put.easyhandle = curl_easy_init ();
1548   s->put.s = s;
1549 #if VERBOSE_CURL
1550   curl_easy_setopt (s->put.easyhandle, CURLOPT_VERBOSE, 1L);
1551   curl_easy_setopt (s->put.easyhandle, CURLOPT_DEBUGFUNCTION, &client_log);
1552   curl_easy_setopt (s->put.easyhandle, CURLOPT_DEBUGDATA, &s->put);
1553 #endif
1554 #if BUILD_HTTPS
1555   curl_easy_setopt (s->put.easyhandle, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
1556   {
1557     struct HttpAddress *ha;
1558     ha = (struct HttpAddress *) s->address->address;
1559
1560     if (HTTP_OPTIONS_VERIFY_CERTIFICATE ==
1561         (ntohl (ha->options) & HTTP_OPTIONS_VERIFY_CERTIFICATE))
1562     {
1563       curl_easy_setopt (s->put.easyhandle, CURLOPT_SSL_VERIFYPEER, 1L);
1564       curl_easy_setopt (s->put.easyhandle, CURLOPT_SSL_VERIFYHOST, 2L);
1565     }
1566     else
1567     {
1568       curl_easy_setopt (s->put.easyhandle, CURLOPT_SSL_VERIFYPEER, 0);
1569       curl_easy_setopt (s->put.easyhandle, CURLOPT_SSL_VERIFYHOST, 0);
1570     }
1571   }
1572   curl_easy_setopt (s->put.easyhandle, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS);
1573   curl_easy_setopt (s->put.easyhandle, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS);
1574 #else
1575   curl_easy_setopt (s->put.easyhandle, CURLOPT_PROTOCOLS, CURLPROTO_HTTP);
1576   curl_easy_setopt (s->put.easyhandle, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP);
1577 #endif
1578   if (s->plugin->proxy_hostname != NULL)
1579   {
1580     curl_easy_setopt (s->put.easyhandle, CURLOPT_PROXY, s->plugin->proxy_hostname);
1581     curl_easy_setopt (s->put.easyhandle, CURLOPT_PROXYTYPE, s->plugin->proxytype);
1582     if (NULL != s->plugin->proxy_username)
1583       curl_easy_setopt (s->put.easyhandle, CURLOPT_PROXYUSERNAME,
1584           s->plugin->proxy_username);
1585     if (NULL != s->plugin->proxy_password)
1586       curl_easy_setopt (s->put.easyhandle, CURLOPT_PROXYPASSWORD,
1587           s->plugin->proxy_password);
1588     if (GNUNET_YES == s->plugin->proxy_use_httpproxytunnel)
1589       curl_easy_setopt (s->put.easyhandle, CURLOPT_HTTPPROXYTUNNEL,
1590           s->plugin->proxy_use_httpproxytunnel);
1591   }
1592
1593   curl_easy_setopt (s->put.easyhandle, CURLOPT_URL, s->url);
1594   curl_easy_setopt (s->put.easyhandle, CURLOPT_UPLOAD, 1L);
1595   //curl_easy_setopt (s->put.easyhandle, CURLOPT_HEADERFUNCTION, &client_curl_header);
1596   //curl_easy_setopt (s->put.easyhandle, CURLOPT_WRITEHEADER, ps);
1597   curl_easy_setopt (s->put.easyhandle, CURLOPT_READFUNCTION, client_send_cb);
1598   curl_easy_setopt (s->put.easyhandle, CURLOPT_READDATA, s);
1599   curl_easy_setopt (s->put.easyhandle, CURLOPT_WRITEFUNCTION, client_receive_put);
1600   curl_easy_setopt (s->put.easyhandle, CURLOPT_WRITEDATA, s);
1601   /* No timeout by default, timeout done with session timeout */
1602   curl_easy_setopt (s->put.easyhandle, CURLOPT_TIMEOUT, 0);
1603   curl_easy_setopt (s->put.easyhandle, CURLOPT_PRIVATE, s);
1604   curl_easy_setopt (s->put.easyhandle, CURLOPT_CONNECTTIMEOUT_MS,
1605                     (long) (HTTP_CLIENT_NOT_VALIDATED_TIMEOUT.rel_value_us / 1000LL));
1606   curl_easy_setopt (s->put.easyhandle, CURLOPT_BUFFERSIZE,
1607                     2 * GNUNET_SERVER_MAX_MESSAGE_SIZE);
1608 #if CURL_TCP_NODELAY
1609   curl_easy_setopt (s->put.easyhandle, CURLOPT_TCP_NODELAY, 1);
1610 #endif
1611   mret = curl_multi_add_handle (s->plugin->curl_multi_handle,
1612                                 s->put.easyhandle);
1613   if (CURLM_OK != mret)
1614   {
1615     LOG (GNUNET_ERROR_TYPE_ERROR,
1616          "Session %p : Failed to add PUT handle to multihandle: `%s'\n",
1617          s, curl_multi_strerror (mret));
1618     curl_easy_cleanup (s->put.easyhandle);
1619     s->put.easyhandle = NULL;
1620     s->put.easyhandle = NULL;
1621     s->put.s = NULL;
1622     s->put.state = H_DISCONNECTED;
1623     return GNUNET_SYSERR;
1624   }
1625   s->put.state = H_CONNECTED;
1626   s->plugin->cur_requests++;
1627
1628   LOG  (GNUNET_ERROR_TYPE_INFO,
1629       "PUT request `%s' established, number of requests increased to %u\n",
1630       s->url, s->plugin->cur_requests);
1631
1632   return GNUNET_OK;
1633 }
1634
1635
1636 /**
1637  * Connect both PUT and GET request for a session
1638  *
1639  * @param s the session to connect
1640  * @return #GNUNET_OK on success, #GNUNET_SYSERR otherwise
1641  */
1642 static int
1643 client_connect (struct Session *s)
1644 {
1645   struct HTTP_Client_Plugin *plugin = s->plugin;
1646   int res = GNUNET_OK;
1647
1648   /* create url */
1649   if (NULL == http_common_plugin_address_to_string(plugin->protocol,
1650           s->address->address, s->address->address_length))
1651     {
1652       LOG(GNUNET_ERROR_TYPE_DEBUG, "Invalid address peer `%s'\n",
1653           GNUNET_i2s(&s->address->peer));
1654       return GNUNET_SYSERR;
1655     }
1656
1657   GNUNET_asprintf(&s->url, "%s/%s;%u",
1658       http_common_plugin_address_to_url(NULL, s->address->address,
1659           s->address->address_length),
1660       GNUNET_i2s_full(plugin->env->my_identity), plugin->last_tag);
1661
1662   plugin->last_tag++;
1663   LOG (GNUNET_ERROR_TYPE_DEBUG,
1664        "Initiating outbound session peer `%s' using address `%s'\n",
1665        GNUNET_i2s (&s->address->peer), s->url);
1666
1667   if (GNUNET_SYSERR == client_connect_get (s))
1668     return GNUNET_SYSERR;
1669   /* If we are emulating an XHR client then delay sending a PUT request until
1670    * there is something to send.
1671    */
1672   if (GNUNET_YES == plugin->emulate_xhr)
1673   {
1674     s->put.state = H_TMP_DISCONNECTED;
1675   }
1676   else if (GNUNET_SYSERR == client_connect_put (s))
1677     return GNUNET_SYSERR;
1678
1679   LOG (GNUNET_ERROR_TYPE_DEBUG,
1680        "Session %p: connected with GET %p and PUT %p\n",
1681        s, s->get.easyhandle, s->put.easyhandle);
1682   /* Perform connect */
1683   GNUNET_STATISTICS_set (plugin->env->stats,
1684                          HTTP_STAT_STR_CONNECTIONS,
1685                          plugin->cur_requests,
1686                          GNUNET_NO);
1687   /* Re-schedule since handles have changed */
1688   if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
1689   {
1690     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
1691     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
1692   }
1693
1694   /* Schedule task to run immediately */
1695   plugin->client_perform_task = GNUNET_SCHEDULER_add_now (client_run, plugin);
1696   return res;
1697 }
1698
1699
1700 /**
1701  * Function obtain the network type for a session
1702  *
1703  * @param cls closure (`struct Plugin*`)
1704  * @param session the session
1705  * @return the network type
1706  */
1707 static enum GNUNET_ATS_Network_Type
1708 http_client_plugin_get_network (void *cls,
1709                                 struct Session *session)
1710 {
1711   return ntohl (session->ats_address_network_type);
1712 }
1713
1714
1715 /**
1716  * Session was idle, so disconnect it
1717  *
1718  * @param cls the `struct Session` of the idle session
1719  * @param tc scheduler context
1720  */
1721 static void
1722 client_session_timeout (void *cls,
1723                         const struct GNUNET_SCHEDULER_TaskContext *tc)
1724 {
1725   struct Session *s = cls;
1726   struct GNUNET_TIME_Relative left;
1727
1728   s->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1729   left = GNUNET_TIME_absolute_get_remaining (s->timeout);
1730   if (0 != left.rel_value_us)
1731   {
1732     /* not actually our turn yet, but let's at least update
1733        the monitor, it may think we're about to die ... */
1734     notify_session_monitor (s->plugin,
1735                             s,
1736                             GNUNET_TRANSPORT_SS_UP);
1737     s->timeout_task = GNUNET_SCHEDULER_add_delayed (left,
1738                                                     &client_session_timeout,
1739                                                     s);
1740     return;
1741   }
1742   LOG (TIMEOUT_LOG,
1743        "Session %p was idle for %s, disconnecting\n",
1744        s,
1745        GNUNET_STRINGS_relative_time_to_string (HTTP_CLIENT_SESSION_TIMEOUT,
1746                                                GNUNET_YES));
1747   GNUNET_assert (GNUNET_OK ==
1748                  http_client_plugin_session_disconnect (s->plugin,
1749                                                  s));
1750 }
1751
1752
1753 /**
1754  * Creates a new outbound session the transport service will use to
1755  * send data to the peer
1756  *
1757  * @param cls the plugin
1758  * @param address the address
1759  * @return the session or NULL of max connections exceeded
1760  */
1761 static struct Session *
1762 http_client_plugin_get_session (void *cls,
1763                                 const struct GNUNET_HELLO_Address *address)
1764 {
1765   struct HTTP_Client_Plugin *plugin = cls;
1766   struct Session *s;
1767   struct sockaddr *sa;
1768   struct GNUNET_ATS_Information ats;
1769   size_t salen = 0;
1770   int res;
1771
1772   GNUNET_assert (NULL != address->address);
1773
1774   /* find existing session */
1775   s = client_lookup_session (plugin, address);
1776   if (NULL != s)
1777     return s;
1778
1779   /* create a new session */
1780   if (plugin->max_requests <= plugin->cur_requests)
1781   {
1782     LOG (GNUNET_ERROR_TYPE_WARNING,
1783          "Maximum number of requests (%u) reached: "
1784          "cannot connect to peer `%s'\n",
1785          plugin->max_requests,
1786          GNUNET_i2s (&address->peer));
1787     return NULL;
1788   }
1789
1790   /* Determine network location */
1791   ats.type = htonl (GNUNET_ATS_NETWORK_TYPE);
1792   ats.value = htonl (GNUNET_ATS_NET_UNSPECIFIED);
1793   sa = http_common_socket_from_address (address->address, address->address_length, &res);
1794   if (GNUNET_SYSERR == res)
1795     return NULL;
1796   if (GNUNET_YES == res)
1797   {
1798     GNUNET_assert (NULL != sa);
1799     if (AF_INET == sa->sa_family)
1800     {
1801       salen = sizeof (struct sockaddr_in);
1802     }
1803     else if (AF_INET6 == sa->sa_family)
1804     {
1805       salen = sizeof (struct sockaddr_in6);
1806     }
1807     ats = plugin->env->get_address_type (plugin->env->cls, sa, salen);
1808     GNUNET_free (sa);
1809   }
1810   else if (GNUNET_NO == res)
1811   {
1812     /* Cannot convert to sockaddr -> is external hostname */
1813     ats.value = htonl (GNUNET_ATS_NET_WAN);
1814   }
1815   if (GNUNET_ATS_NET_UNSPECIFIED == ntohl (ats.value))
1816   {
1817     GNUNET_break (0);
1818     return NULL;
1819   }
1820
1821   s = GNUNET_new (struct Session);
1822   s->plugin = plugin;
1823   s->address = GNUNET_HELLO_address_copy (address);
1824   s->ats_address_network_type = ats.value;
1825
1826   s->put.state = H_NOT_CONNECTED;
1827   s->timeout = GNUNET_TIME_relative_to_absolute (HTTP_CLIENT_SESSION_TIMEOUT);
1828   s->timeout_task =  GNUNET_SCHEDULER_add_delayed (HTTP_CLIENT_SESSION_TIMEOUT,
1829                                                    &client_session_timeout,
1830                                                    s);
1831   LOG (GNUNET_ERROR_TYPE_DEBUG,
1832        "Created new session %p for `%s' address `%s''\n",
1833        s,
1834        http_common_plugin_address_to_string (plugin->protocol,
1835                                              s->address->address,
1836                                              s->address->address_length),
1837        GNUNET_i2s (&s->address->peer));
1838
1839   /* add new session */
1840   (void) GNUNET_CONTAINER_multipeermap_put (plugin->sessions,
1841                                             &s->address->peer,
1842                                             s,
1843                                             GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
1844   /* initiate new connection */
1845   if (GNUNET_SYSERR == client_connect (s))
1846   {
1847     LOG (GNUNET_ERROR_TYPE_ERROR,
1848          "Cannot connect to peer `%s' address `%s''\n",
1849          http_common_plugin_address_to_string (plugin->protocol,
1850              s->address->address, s->address->address_length),
1851              GNUNET_i2s (&s->address->peer));
1852     client_delete_session (s);
1853     return NULL;
1854   }
1855   notify_session_monitor (plugin, s, GNUNET_TRANSPORT_SS_UP); /* or handshake? */
1856   return s;
1857 }
1858
1859
1860 /**
1861  * Setup http_client plugin
1862  *
1863  * @param plugin the plugin handle
1864  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
1865  */
1866 static int
1867 client_start (struct HTTP_Client_Plugin *plugin)
1868 {
1869   curl_global_init (CURL_GLOBAL_ALL);
1870   plugin->curl_multi_handle = curl_multi_init ();
1871
1872   if (NULL == plugin->curl_multi_handle)
1873   {
1874     LOG (GNUNET_ERROR_TYPE_ERROR,
1875          _("Could not initialize curl multi handle, failed to start %s plugin!\n"),
1876          plugin->name);
1877     return GNUNET_SYSERR;
1878   }
1879   return GNUNET_OK;
1880 }
1881
1882
1883 /**
1884  * Another peer has suggested an address for this
1885  * peer and transport plugin.  Check that this could be a valid
1886  * address.  If so, consider adding it to the list
1887  * of addresses.
1888  *
1889  * @param cls closure with the `struct Plugin`
1890  * @param addr pointer to the address
1891  * @param addrlen length of @a addr
1892  * @return #GNUNET_OK if this is a plausible address for this peer
1893  *         and transport; always returns #GNUNET_NO (this is the client!)
1894  */
1895 static int
1896 http_client_plugin_address_suggested (void *cls,
1897                                       const void *addr,
1898                                       size_t addrlen)
1899 {
1900   /* A HTTP/S client does not have any valid address so:*/
1901   return GNUNET_NO;
1902 }
1903
1904
1905 /**
1906  * Exit point from the plugin.
1907  *
1908  * @param cls api as closure
1909  * @return NULL
1910  */
1911 void *
1912 LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls)
1913 {
1914   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
1915   struct HTTP_Client_Plugin *plugin = api->cls;
1916
1917   if (NULL == api->cls)
1918   {
1919     /* Stub shutdown */
1920     GNUNET_free (api);
1921     return NULL;
1922   }
1923   LOG (GNUNET_ERROR_TYPE_DEBUG,
1924        _("Shutting down plugin `%s'\n"),
1925        plugin->name);
1926   GNUNET_CONTAINER_multipeermap_iterate (plugin->sessions,
1927                                          &destroy_session_cb,
1928                                          plugin);
1929   if (GNUNET_SCHEDULER_NO_TASK != plugin->client_perform_task)
1930   {
1931     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
1932     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
1933   }
1934   if (NULL != plugin->curl_multi_handle)
1935   {
1936     curl_multi_cleanup (plugin->curl_multi_handle);
1937     plugin->curl_multi_handle = NULL;
1938   }
1939   curl_global_cleanup ();
1940   LOG (GNUNET_ERROR_TYPE_DEBUG,
1941        _("Shutdown for plugin `%s' complete\n"),
1942        plugin->name);
1943   GNUNET_CONTAINER_multipeermap_destroy (plugin->sessions);
1944   GNUNET_free_non_null (plugin->proxy_hostname);
1945   GNUNET_free_non_null (plugin->proxy_username);
1946   GNUNET_free_non_null (plugin->proxy_password);
1947   GNUNET_free (plugin);
1948   GNUNET_free (api);
1949   return NULL;
1950 }
1951
1952
1953 /**
1954  * Configure plugin
1955  *
1956  * @param plugin the plugin handle
1957  * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
1958  */
1959 static int
1960 client_configure_plugin (struct HTTP_Client_Plugin *plugin)
1961 {
1962   unsigned long long max_requests;
1963   char *proxy_type;
1964
1965
1966   /* Optional parameters */
1967   if (GNUNET_OK !=
1968       GNUNET_CONFIGURATION_get_value_number (plugin->env->cfg,
1969                                              plugin->name,
1970                                              "MAX_CONNECTIONS",
1971                                              &max_requests))
1972     max_requests = 128;
1973   plugin->max_requests = max_requests;
1974
1975   LOG (GNUNET_ERROR_TYPE_DEBUG,
1976        _("Maximum number of requests is %u\n"),
1977        plugin->max_requests);
1978
1979   /* Read proxy configuration */
1980   if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
1981       plugin->name, "PROXY", &plugin->proxy_hostname))
1982   {
1983     LOG (GNUNET_ERROR_TYPE_DEBUG,
1984          "Found proxy host: `%s'\n",
1985          plugin->proxy_hostname);
1986     /* proxy username */
1987     if (GNUNET_OK ==
1988         GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
1989                                                plugin->name,
1990                                                "PROXY_USERNAME",
1991                                                &plugin->proxy_username))
1992     {
1993       LOG (GNUNET_ERROR_TYPE_DEBUG,
1994            "Found proxy username name: `%s'\n",
1995            plugin->proxy_username);
1996     }
1997
1998     /* proxy password */
1999     if (GNUNET_OK ==
2000         GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
2001                                                plugin->name,
2002                                                "PROXY_PASSWORD",
2003                                                &plugin->proxy_password))
2004     {
2005       LOG (GNUNET_ERROR_TYPE_DEBUG,
2006            "Found proxy password name: `%s'\n",
2007            plugin->proxy_password);
2008     }
2009
2010     /* proxy type */
2011     if (GNUNET_OK ==
2012         GNUNET_CONFIGURATION_get_value_string (plugin->env->cfg,
2013                                                plugin->name,
2014                                                "PROXY_TYPE",
2015                                                &proxy_type))
2016     {
2017       GNUNET_STRINGS_utf8_toupper (proxy_type, proxy_type);
2018
2019       if (0 == strcmp(proxy_type, "HTTP"))
2020         plugin->proxytype = CURLPROXY_HTTP;
2021       else if (0 == strcmp(proxy_type, "SOCKS4"))
2022         plugin->proxytype = CURLPROXY_SOCKS4;
2023       else if (0 == strcmp(proxy_type, "SOCKS5"))
2024         plugin->proxytype = CURLPROXY_SOCKS5;
2025       else if (0 == strcmp(proxy_type, "SOCKS4A"))
2026         plugin->proxytype = CURLPROXY_SOCKS4A;
2027       else if (0 == strcmp(proxy_type, "SOCKS5_HOSTNAME "))
2028         plugin->proxytype = CURLPROXY_SOCKS5_HOSTNAME ;
2029       else
2030       {
2031         LOG (GNUNET_ERROR_TYPE_ERROR,
2032              _("Invalid proxy type: `%s', disabling proxy! Check configuration!\n"),
2033              proxy_type);
2034
2035         GNUNET_free (proxy_type);
2036         GNUNET_free (plugin->proxy_hostname);
2037         plugin->proxy_hostname = NULL;
2038         GNUNET_free_non_null (plugin->proxy_username);
2039         plugin->proxy_username = NULL;
2040         GNUNET_free_non_null (plugin->proxy_password);
2041         plugin->proxy_password = NULL;
2042
2043         return GNUNET_SYSERR;
2044       }
2045
2046       LOG (GNUNET_ERROR_TYPE_DEBUG,
2047            "Found proxy type: `%s'\n",
2048            proxy_type);
2049     }
2050
2051     /* proxy http tunneling */
2052     plugin->proxy_use_httpproxytunnel
2053       = GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg,
2054                                               plugin->name,
2055                                               "PROXY_HTTP_TUNNELING");
2056     if (GNUNET_SYSERR == plugin->proxy_use_httpproxytunnel)
2057       plugin->proxy_use_httpproxytunnel = GNUNET_NO;
2058
2059     GNUNET_free_non_null (proxy_type);
2060   }
2061
2062   /* Should we emulate an XHR client for testing? */
2063   plugin->emulate_xhr
2064     = GNUNET_CONFIGURATION_get_value_yesno (plugin->env->cfg,
2065                                             plugin->name,
2066                                             "EMULATE_XHR");
2067   return GNUNET_OK;
2068 }
2069
2070
2071 /**
2072  * Function called by the pretty printer for the resolved address for
2073  * each human-readable address obtained.  The callback can be called
2074  * several times. The last invocation must be with a @a address of
2075  * NULL and a @a res of #GNUNET_OK.  Thus, to indicate conversion
2076  * errors, the callback might be called first with @a address NULL and
2077  * @a res being #GNUNET_SYSERR.  In that case, there must still be a
2078  * subsequent call later with @a address NULL and @a res #GNUNET_OK.
2079  *
2080  * @param cls closure
2081  * @param address one of the names for the host, NULL on last callback
2082  * @param res #GNUNET_OK if conversion was successful, #GNUNET_SYSERR on failure,
2083  *      #GNUNET_OK on last callback
2084  */
2085 static const char *
2086 http_client_plugin_address_to_string (void *cls,
2087                                       const void *addr,
2088                                       size_t addrlen)
2089 {
2090   return http_common_plugin_address_to_string (PLUGIN_NAME,
2091                                                addr,
2092                                                addrlen);
2093 }
2094
2095
2096 /**
2097  * Function that will be called whenever the transport service wants to
2098  * notify the plugin that a session is still active and in use and
2099  * therefore the session timeout for this session has to be updated
2100  *
2101  * @param cls closure
2102  * @param peer which peer was the session for
2103  * @param session which session is being updated
2104  */
2105 static void
2106 http_client_plugin_update_session_timeout (void *cls,
2107                                            const struct GNUNET_PeerIdentity *peer,
2108                                            struct Session *session)
2109 {
2110   client_reschedule_session_timeout (session);
2111 }
2112
2113
2114 /**
2115  * Function that will be called whenever the transport service wants to
2116  * notify the plugin that the inbound quota changed and that the plugin
2117  * should update it's delay for the next receive value
2118  *
2119  * @param cls closure
2120  * @param peer which peer was the session for
2121  * @param session which session is being updated
2122  * @param delay new delay to use for receiving
2123  */
2124 static void
2125 http_client_plugin_update_inbound_delay (void *cls,
2126                                          const struct GNUNET_PeerIdentity *peer,
2127                                          struct Session *s,
2128                                          struct GNUNET_TIME_Relative delay)
2129 {
2130   s->next_receive = GNUNET_TIME_relative_to_absolute (delay);
2131   LOG (GNUNET_ERROR_TYPE_DEBUG,
2132        "New inbound delay %s\n",
2133        GNUNET_STRINGS_relative_time_to_string (delay,
2134                                                GNUNET_NO));
2135   if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK)
2136   {
2137     GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
2138     s->recv_wakeup_task = GNUNET_SCHEDULER_add_delayed (delay,
2139         &client_wake_up, s);
2140   }
2141 }
2142
2143
2144 /**
2145  * Return information about the given session to the
2146  * monitor callback.
2147  *
2148  * @param cls the `struct Plugin` with the monitor callback (`sic`)
2149  * @param peer peer we send information about
2150  * @param value our `struct Session` to send information about
2151  * @return #GNUNET_OK (continue to iterate)
2152  */
2153 static int
2154 send_session_info_iter (void *cls,
2155                         const struct GNUNET_PeerIdentity *peer,
2156                         void *value)
2157 {
2158   struct HTTP_Client_Plugin *plugin = cls;
2159   struct Session *session = value;
2160
2161   notify_session_monitor (plugin,
2162                           session,
2163                           GNUNET_TRANSPORT_SS_UP);
2164   return GNUNET_OK;
2165 }
2166
2167
2168 /**
2169  * Begin monitoring sessions of a plugin.  There can only
2170  * be one active monitor per plugin (i.e. if there are
2171  * multiple monitors, the transport service needs to
2172  * multiplex the generated events over all of them).
2173  *
2174  * @param cls closure of the plugin
2175  * @param sic callback to invoke, NULL to disable monitor;
2176  *            plugin will being by iterating over all active
2177  *            sessions immediately and then enter monitor mode
2178  * @param sic_cls closure for @a sic
2179  */
2180 static void
2181 http_client_plugin_setup_monitor (void *cls,
2182                                   GNUNET_TRANSPORT_SessionInfoCallback sic,
2183                                   void *sic_cls)
2184 {
2185   struct HTTP_Client_Plugin *plugin = cls;
2186
2187   plugin->sic = sic;
2188   plugin->sic_cls = sic_cls;
2189   if (NULL != sic)
2190   {
2191     GNUNET_CONTAINER_multipeermap_iterate (plugin->sessions,
2192                                            &send_session_info_iter,
2193                                            plugin);
2194     /* signal end of first iteration */
2195     sic (sic_cls, NULL, NULL);
2196   }
2197 }
2198
2199
2200 /**
2201  * Entry point for the plugin.
2202  */
2203 void *
2204 LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls)
2205 {
2206   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
2207   struct GNUNET_TRANSPORT_PluginFunctions *api;
2208   struct HTTP_Client_Plugin *plugin;
2209
2210   if (NULL == env->receive)
2211   {
2212     /* run in 'stub' mode (i.e. as part of gnunet-peerinfo), don't fully
2213        initialze the plugin or the API */
2214     api = GNUNET_new (struct GNUNET_TRANSPORT_PluginFunctions);
2215     api->cls = NULL;
2216     api->address_to_string = &http_client_plugin_address_to_string;
2217     api->string_to_address = &http_common_plugin_string_to_address;
2218     api->address_pretty_printer = &http_common_plugin_address_pretty_printer;
2219     return api;
2220   }
2221
2222   plugin = GNUNET_new (struct HTTP_Client_Plugin);
2223   plugin->env = env;
2224   plugin->sessions = GNUNET_CONTAINER_multipeermap_create (128,
2225                                                            GNUNET_YES);
2226   api = GNUNET_new (struct GNUNET_TRANSPORT_PluginFunctions);
2227   api->cls = plugin;
2228   api->send = &http_client_plugin_send;
2229   api->disconnect_session = &http_client_plugin_session_disconnect;
2230   api->query_keepalive_factor = &http_client_query_keepalive_factor;
2231   api->disconnect_peer = &http_client_plugin_peer_disconnect;
2232   api->check_address = &http_client_plugin_address_suggested;
2233   api->get_session = &http_client_plugin_get_session;
2234   api->address_to_string = &http_client_plugin_address_to_string;
2235   api->string_to_address = &http_common_plugin_string_to_address;
2236   api->address_pretty_printer = &http_common_plugin_address_pretty_printer;
2237   api->get_network = &http_client_plugin_get_network;
2238   api->update_session_timeout = &http_client_plugin_update_session_timeout;
2239   api->update_inbound_delay = &http_client_plugin_update_inbound_delay;
2240   api->setup_monitor = &http_client_plugin_setup_monitor;
2241 #if BUILD_HTTPS
2242   plugin->name = "transport-https_client";
2243   plugin->protocol = "https";
2244 #else
2245   plugin->name = "transport-http_client";
2246   plugin->protocol = "http";
2247 #endif
2248   plugin->last_tag = 1;
2249
2250   if (GNUNET_SYSERR == client_configure_plugin (plugin))
2251   {
2252     LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
2253     return NULL;
2254   }
2255
2256   /* Start client */
2257   if (GNUNET_SYSERR == client_start (plugin))
2258   {
2259     LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
2260     return NULL;
2261   }
2262   return api;
2263 }
2264
2265 /* end of plugin_transport_http_client.c */