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