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