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