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