changes
[oweals/gnunet.git] / src / transport / plugin_transport_http_client.c
1 /*
2      This file is part of GNUnet
3      (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 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  */
26
27 #if BUILD_HTTPS
28 #define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_https_client_init
29 #define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_https_client_done
30 #else
31 #define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_http_client_init
32 #define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_http_client_done
33 #endif
34
35
36 #define VERBOSE_CURL GNUNET_YES
37
38 #include "platform.h"
39 #include "gnunet_protocols.h"
40 #include "gnunet_connection_lib.h"
41 #include "gnunet_server_lib.h"
42 #include "gnunet_service_lib.h"
43 #include "gnunet_statistics_service.h"
44 #include "gnunet_transport_service.h"
45 #include "gnunet_transport_plugin.h"
46 #include "plugin_transport_http_common.h"
47 #include <curl/curl.h>
48
49
50 /**
51  * Encapsulation of all of the state of the plugin.
52  */
53 struct HTTP_Client_Plugin;
54
55
56 /**
57  *  Message to send using http
58  */
59 struct HTTP_Message
60 {
61   /**
62    * next pointer for double linked list
63    */
64   struct HTTP_Message *next;
65
66   /**
67    * previous pointer for double linked list
68    */
69   struct HTTP_Message *prev;
70
71   /**
72    * buffer containing data to send
73    */
74   char *buf;
75
76   /**
77    * amount of data already sent
78    */
79   size_t pos;
80
81   /**
82    * buffer length
83    */
84   size_t size;
85
86   /**
87    * Continuation function to call once the transmission buffer
88    * has again space available.  NULL if there is no
89    * continuation to call.
90    */
91   GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
92
93   /**
94    * Closure for transmit_cont.
95    */
96   void *transmit_cont_cls;
97 };
98
99
100 /**
101  * Session handle for connections.
102  */
103 struct Session
104 {
105   /**
106    * To whom are we talking to (set to our identity
107    * if we are still waiting for the welcome message)
108    */
109   struct GNUNET_PeerIdentity target;
110
111   /**
112    * Stored in a linked list.
113    */
114   struct Session *next;
115
116   /**
117    * Stored in a linked list.
118    */
119   struct Session *prev;
120
121   /**
122    * Address
123    */
124   void *addr;
125
126   /**
127    * Address length
128    */
129   size_t addrlen;
130
131   /**
132    * ATS network type in NBO
133    */
134   uint32_t ats_address_network_type;
135
136   /**
137    * Pointer to the global plugin struct.
138    */
139   struct HTTP_Client_Plugin *plugin;
140
141   /**
142    * Is client send handle paused since there are no data to send?
143    * GNUNET_YES/NO
144    */
145   int client_put_paused;
146
147   /**
148    * Was session given to transport service?
149    */
150   int session_passed;
151
152   /**
153    * Client send handle
154    */
155   void *client_put;
156
157   /**
158    * Client receive handle
159    */
160   void *client_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 timeout_task;
181
182   /**
183    * Task to wake up client receive handle when receiving is allowed again
184    */
185   GNUNET_SCHEDULER_TaskIdentifier recv_wakeup_task;
186
187   /**
188   * Absolute time when to receive data again
189   * Used for receive throttling
190   */
191  struct GNUNET_TIME_Absolute next_receive;
192 };
193
194 /**
195  * Encapsulation of all of the state of the plugin.
196  */
197 struct HTTP_Client_Plugin
198 {
199   /**
200    * Our environment.
201    */
202   struct GNUNET_TRANSPORT_PluginEnvironment *env;
203
204   /**
205    * Linked list head of open sessions.
206    */
207   struct Session *head;
208
209   /**
210    * Linked list tail of open sessions.
211    */
212   struct Session *tail;
213
214   /**
215    * Plugin name
216    */
217   char *name;
218
219   /**
220    * Protocol
221    */
222   char *protocol;
223
224   /**
225    * Maximum number of sockets the plugin can use
226    * Each http inbound /outbound connections are two connections
227    */
228   unsigned int max_connections;
229
230   /**
231    * Current number of sockets the plugin can use
232    * Each http inbound /outbound connections are two connections
233    */
234   unsigned int cur_connections;
235
236   /**
237    * Last used unique HTTP connection tag
238    */
239   uint32_t last_tag;
240
241   /**
242    * use IPv6
243    */
244   uint16_t use_ipv6;
245
246   /**
247    * use IPv4
248    */
249   uint16_t use_ipv4;
250
251   /**
252    * cURL Multihandle
253    */
254   CURLM *curl_multi_handle;
255
256   /**
257    * curl perform task
258    */
259   GNUNET_SCHEDULER_TaskIdentifier client_perform_task;
260 };
261
262 /**
263  * Encapsulation of all of the state of the plugin.
264  */
265 struct HTTP_Client_Plugin *p;
266
267 /**
268  * Start session timeout
269  */
270 static void
271 client_start_session_timeout (struct Session *s);
272
273 /**
274  * Increment session timeout due to activity
275  */
276 static void
277 client_reschedule_session_timeout (struct Session *s);
278
279 /**
280  * Cancel timeout
281  */
282 static void
283 client_stop_session_timeout (struct Session *s);
284
285 /**
286  * Function setting up file descriptors and scheduling task to run
287  *
288  * @param  plugin plugin as closure
289  * @param now schedule task in 1ms, regardless of what curl may say
290  * @return GNUNET_SYSERR for hard failure, GNUNET_OK for ok
291  */
292 static int
293 client_schedule (struct HTTP_Client_Plugin *plugin, int now);
294
295
296 int
297 client_exist_session (struct HTTP_Client_Plugin *plugin, struct Session *s)
298 {
299   struct Session * head;
300
301   GNUNET_assert (NULL != plugin);
302   GNUNET_assert (NULL != s);
303
304   for (head = plugin->head; head != NULL; head = head->next)
305   {
306     if (head == s)
307       return GNUNET_YES;
308   }
309   return GNUNET_NO;
310 }
311
312 #if VERBOSE_CURL
313 /**
314  * Function to log curl debug messages with GNUNET_log
315  * @param curl handle
316  * @param type curl_infotype
317  * @param data data
318  * @param size size
319  * @param cls  closure
320  * @return 0
321  */
322 static int
323 client_log (CURL * curl, curl_infotype type, char *data, size_t size, void *cls)
324 {
325   char *ttype;
326   if ((type == CURLINFO_TEXT) || (type == CURLINFO_HEADER_IN) || (type == CURLINFO_HEADER_OUT))
327   {
328     char text[size + 2];
329
330     switch (type) {
331       case CURLINFO_TEXT:
332         ttype = "TEXT";
333         break;
334       case CURLINFO_HEADER_IN:
335         ttype = "HEADER_IN";
336         break;
337       case CURLINFO_HEADER_OUT:
338         ttype = "HEADER_OUT";
339         break;
340       default:
341         break;
342     }
343
344     memcpy (text, data, size);
345     if (text[size - 1] == '\n')
346       text[size] = '\0';
347     else
348     {
349       text[size] = '\n';
350       text[size + 1] = '\0';
351     }
352 #if BUILD_HTTPS
353     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-https_client",
354                      "Connection %p %s: %s", cls, ttype, text);
355 #else
356     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, "transport-http_client",
357                      "Connection %p %s: %s", cls, ttype, text);
358 #endif
359   }
360   return 0;
361 }
362 #endif
363
364 /**
365  * Function that can be used by the transport service to transmit
366  * a message using the plugin.   Note that in the case of a
367  * peer disconnecting, the continuation MUST be called
368  * prior to the disconnect notification itself.  This function
369  * will be called with this peer's HELLO message to initiate
370  * a fresh connection to another peer.
371  *
372  * @param cls closure
373  * @param session which session must be used
374  * @param msgbuf the message to transmit
375  * @param msgbuf_size number of bytes in 'msgbuf'
376  * @param priority how important is the message (most plugins will
377  *                 ignore message priority and just FIFO)
378  * @param to how long to wait at most for the transmission (does not
379  *                require plugins to discard the message after the timeout,
380  *                just advisory for the desired delay; most plugins will ignore
381  *                this as well)
382  * @param cont continuation to call once the message has
383  *        been transmitted (or if the transport is ready
384  *        for the next transmission call; or if the
385  *        peer disconnected...); can be NULL
386  * @param cont_cls closure for cont
387  * @return number of bytes used (on the physical network, with overheads);
388  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
389  *         and does NOT mean that the message was not transmitted (DV)
390  */
391 static ssize_t
392 http_client_plugin_send (void *cls,
393                   struct Session *session,
394                   const char *msgbuf, size_t msgbuf_size,
395                   unsigned int priority,
396                   struct GNUNET_TIME_Relative to,
397                   GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
398 {
399   struct HTTP_Client_Plugin *plugin = cls;
400   struct HTTP_Message *msg;
401
402   GNUNET_assert (plugin != NULL);
403   GNUNET_assert (session != NULL);
404
405   /* lookup if session is really existing */
406   if (GNUNET_YES != client_exist_session (plugin, session))
407   {
408     GNUNET_break (0);
409     return GNUNET_SYSERR;
410   }
411
412   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, session->plugin->name,
413                    "Session %p/connection %p: Sending message with %u to peer `%s' with \n",
414                    session, session->client_put,
415                    msgbuf_size, GNUNET_i2s (&session->target));
416
417   /* create new message and schedule */
418   msg = GNUNET_malloc (sizeof (struct HTTP_Message) + msgbuf_size);
419   msg->next = NULL;
420   msg->size = msgbuf_size;
421   msg->pos = 0;
422   msg->buf = (char *) &msg[1];
423   msg->transmit_cont = cont;
424   msg->transmit_cont_cls = cont_cls;
425   memcpy (msg->buf, msgbuf, msgbuf_size);
426   GNUNET_CONTAINER_DLL_insert_tail (session->msg_head, session->msg_tail, msg);
427
428   if (session->client_put_paused == GNUNET_YES)
429   {
430     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, session->plugin->name,
431                      "Session %p/connection %p: unpausing connection\n",
432                      session, session->client_put);
433     session->client_put_paused = GNUNET_NO;
434     curl_easy_pause (session->client_put, CURLPAUSE_CONT);
435   }
436   client_schedule (session->plugin, GNUNET_YES);
437   client_reschedule_session_timeout (session);
438   return msgbuf_size;
439 }
440
441
442 void
443 client_delete_session (struct Session *s)
444 {
445   struct HTTP_Client_Plugin *plugin = s->plugin;
446   struct HTTP_Message *pos;
447   struct HTTP_Message *next;
448
449   client_stop_session_timeout (s);
450
451   GNUNET_CONTAINER_DLL_remove (plugin->head, plugin->tail, s);
452
453   next = s->msg_head;
454   while (NULL != (pos = next))
455   {
456     next = pos->next;
457     GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, pos);
458     if (pos->transmit_cont != NULL)
459       pos->transmit_cont (pos->transmit_cont_cls, &s->target, GNUNET_SYSERR);
460     GNUNET_free (pos);
461   }
462
463   if (s->msg_tk != NULL)
464   {
465     GNUNET_SERVER_mst_destroy (s->msg_tk);
466     s->msg_tk = NULL;
467   }
468   GNUNET_free (s->addr);
469   GNUNET_free (s);
470 }
471
472
473
474 /**
475  * Disconnect a session
476  *
477  * @param s session
478  * @return GNUNET_OK on success, GNUNET_SYSERR on error
479  */
480 static int
481 client_disconnect (struct Session *s)
482 {
483   struct HTTP_Client_Plugin *plugin = s->plugin;
484   struct HTTP_Message *msg;
485   struct HTTP_Message *t;
486   int res = GNUNET_OK;
487   CURLMcode mret;
488
489   if (GNUNET_YES != client_exist_session (plugin, s))
490   {
491     GNUNET_break (0);
492     return GNUNET_SYSERR;
493   }
494
495   if (s->client_put != NULL)
496   {
497     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
498                      "Session %p/connection %p: disconnecting PUT connection to peer `%s'\n",
499                      s, s->client_put, GNUNET_i2s (&s->target));
500
501     /* remove curl handle from multi handle */
502     mret = curl_multi_remove_handle (plugin->curl_multi_handle, s->client_put);
503     if (mret != CURLM_OK)
504     {
505       /* clean up easy handle, handle is now invalid and free'd */
506       res = GNUNET_SYSERR;
507       GNUNET_break (0);
508     }
509     curl_easy_cleanup (s->client_put);
510     s->client_put = NULL;
511   }
512
513
514   if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK)
515   {
516     GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
517     s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK;
518   }
519
520   if (s->client_get != NULL)
521   {
522       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
523                        "Session %p/connection %p: disconnecting GET connection to peer `%s'\n",
524                        s, s->client_get, GNUNET_i2s (&s->target));
525
526     /* remove curl handle from multi handle */
527     mret = curl_multi_remove_handle (plugin->curl_multi_handle, s->client_get);
528     if (mret != CURLM_OK)
529     {
530       /* clean up easy handle, handle is now invalid and free'd */
531       res = GNUNET_SYSERR;
532       GNUNET_break (0);
533     }
534     curl_easy_cleanup (s->client_get);
535     s->client_get = NULL;
536   }
537
538   msg = s->msg_head;
539   while (msg != NULL)
540   {
541     t = msg->next;
542     if (NULL != msg->transmit_cont)
543       msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_SYSERR);
544     GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg);
545     GNUNET_free (msg);
546     msg = t;
547   }
548
549   GNUNET_assert (plugin->cur_connections >= 2);
550   plugin->cur_connections -= 2;
551   GNUNET_STATISTICS_set (plugin->env->stats,
552       "# HTTP client sessions",
553       plugin->cur_connections,
554       GNUNET_NO);
555
556   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
557                    "Session %p: notifying transport about ending session\n",s);
558
559   plugin->env->session_end (plugin->env->cls, &s->target, s);
560   client_delete_session (s);
561
562   /* Re-schedule since handles have changed */
563   if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
564   {
565     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
566     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
567   }
568   client_schedule (plugin, GNUNET_YES);
569
570   return res;
571 }
572
573
574 /**
575  * Function that can be used to force the plugin to disconnect
576  * from the given peer and cancel all previous transmissions
577  * (and their continuationc).
578  *
579  * @param cls closure
580  * @param target peer from which to disconnect
581  */
582 static void
583 http_client_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *target)
584 {
585   struct HTTP_Client_Plugin *plugin = cls;
586   struct Session *next = NULL;
587   struct Session *pos = NULL;
588
589   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
590                    "Transport tells me to disconnect `%s'\n",
591                    GNUNET_i2s (target));
592
593   next = plugin->head;
594   while (NULL != (pos = next))
595   {
596     next = pos->next;
597     if (0 == memcmp (target, &pos->target, sizeof (struct GNUNET_PeerIdentity)))
598     {
599       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
600                        "Disconnecting session %p to `%pos'\n",
601                        pos, GNUNET_i2s (target));
602       GNUNET_assert (GNUNET_OK == client_disconnect (pos));
603     }
604   }
605
606 }
607
608 static struct Session *
609 client_lookup_session (struct HTTP_Client_Plugin *plugin,
610                        const struct GNUNET_HELLO_Address *address)
611 {
612   struct Session *pos;
613
614   for (pos = plugin->head; NULL != pos; pos = pos->next)
615     if ((0 == memcmp (&address->peer, &pos->target, sizeof (struct GNUNET_PeerIdentity))) &&
616         (address->address_length == pos->addrlen) &&
617         (0 == memcmp (address->address, pos->addr, pos->addrlen)))
618       return pos;
619   return NULL;
620 }
621
622 /**
623  * Callback method used with libcurl
624  * Method is called when libcurl needs to read data during sending
625  *
626  * @param stream pointer where to write data
627  * @param size size of an individual element
628  * @param nmemb count of elements that can be written to the buffer
629  * @param cls source pointer, passed to the libcurl handle
630  * @return bytes written to stream, returning 0 will terminate connection!
631  */
632 static size_t
633 client_send_cb (void *stream, size_t size, size_t nmemb, void *cls)
634 {
635   struct Session *s = cls;
636   struct HTTP_Client_Plugin *plugin = s->plugin;
637   struct HTTP_Message *msg = s->msg_head;
638   size_t len;
639
640   if (GNUNET_YES != client_exist_session (plugin, s))
641   {
642     GNUNET_break (0);
643     return 0;
644   }
645   if (NULL == msg)
646   {
647     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
648                      "Session %p/connection %p: nothing to send, suspending\n",
649                      s, s->client_put);
650     s->client_put_paused = GNUNET_YES;
651     return CURL_READFUNC_PAUSE;
652   }
653   /* data to send */
654   GNUNET_assert (msg->pos < msg->size);
655   /* calculate how much fits in buffer */
656   len = GNUNET_MIN (msg->size - msg->pos,
657                     size * nmemb);
658   memcpy (stream, &msg->buf[msg->pos], len);
659   msg->pos += len;
660   if (msg->pos == msg->size)
661   {
662     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
663                      "Session %p/connection %p: sent message with %u bytes sent, removing message from queue\n",
664                      s, s->client_put, msg->size, msg->pos);
665     /* Calling transmit continuation  */
666     GNUNET_CONTAINER_DLL_remove (s->msg_head, s->msg_tail, msg);
667     if (NULL != msg->transmit_cont)
668       msg->transmit_cont (msg->transmit_cont_cls, &s->target, GNUNET_OK);
669     GNUNET_free (msg);
670   }
671   client_reschedule_session_timeout (s);
672   return len;
673 }
674
675
676 static void
677 client_wake_up (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
678 {
679   struct Session *s = cls;
680
681   if (GNUNET_YES != client_exist_session(p, s))
682   {
683     GNUNET_break (0);
684     return;
685   }
686   s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK;
687   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
688     return;
689   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, s->plugin->name,
690                    "Session %p/connection %p: Waking up GET handle\n", s, s->client_get);
691   if (s->client_get != NULL)
692     curl_easy_pause (s->client_get, CURLPAUSE_CONT);
693 }
694
695
696
697 static int
698 client_receive_mst_cb (void *cls, void *client,
699                        const struct GNUNET_MessageHeader *message)
700 {
701   struct Session *s = cls;
702   struct HTTP_Client_Plugin *plugin;
703   struct GNUNET_TIME_Relative delay;
704   struct GNUNET_ATS_Information atsi[2];
705
706   if (GNUNET_YES != client_exist_session(p, s))
707   {
708     GNUNET_break (0);
709     return GNUNET_OK;
710   }
711   plugin = s->plugin;
712
713
714   atsi[0].type = htonl (GNUNET_ATS_QUALITY_NET_DISTANCE);
715   atsi[0].value = htonl (1);
716   atsi[1].type = htonl (GNUNET_ATS_NETWORK_TYPE);
717   atsi[1].value = s->ats_address_network_type;
718   GNUNET_break (s->ats_address_network_type != ntohl (GNUNET_ATS_NET_UNSPECIFIED));
719
720   delay = s->plugin->env->receive (plugin->env->cls, &s->target, message,
721                                    (const struct GNUNET_ATS_Information *) &atsi, 2,
722                                    s, s->addr, s->addrlen);
723   s->next_receive =
724       GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get (), delay);
725
726   if (GNUNET_TIME_absolute_get ().abs_value < s->next_receive.abs_value)
727   {
728
729     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
730                      "Client: peer `%s' address `%s' next read delayed for %llu ms\n",
731                      GNUNET_i2s (&s->target), GNUNET_a2s (s->addr, s->addrlen),
732                      delay);
733   }
734   client_reschedule_session_timeout (s);
735   return GNUNET_OK;
736 }
737
738
739
740 /**
741  * Callback method used with libcurl
742  * Method is called when libcurl needs to write data during sending
743  *
744  * @param stream pointer where to write data
745  * @param size size of an individual element
746  * @param nmemb count of elements that can be written to the buffer
747  * @param cls destination pointer, passed to the libcurl handle
748  * @return bytes read from stream
749  */
750 static size_t
751 client_receive (void *stream, size_t size, size_t nmemb, void *cls)
752 {
753   struct Session *s = cls;
754   struct GNUNET_TIME_Absolute now;
755   size_t len = size * nmemb;
756   struct HTTP_Client_Plugin *plugin = s->plugin;
757
758   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
759                    "Received %u bytes from peer `%s'\n", len,
760                    GNUNET_i2s (&s->target));
761   now = GNUNET_TIME_absolute_get ();
762   if (now.abs_value < s->next_receive.abs_value)
763   {
764     struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
765     struct GNUNET_TIME_Relative delta =
766         GNUNET_TIME_absolute_get_difference (now, s->next_receive);
767     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
768                      "No inbound bandwidth for session %p available! Next read was delayed for %llu ms\n",
769                      s, delta.rel_value);
770     if (s->recv_wakeup_task != GNUNET_SCHEDULER_NO_TASK)
771     {
772       GNUNET_SCHEDULER_cancel (s->recv_wakeup_task);
773       s->recv_wakeup_task = GNUNET_SCHEDULER_NO_TASK;
774     }
775     s->recv_wakeup_task =
776         GNUNET_SCHEDULER_add_delayed (delta, &client_wake_up, s);
777     return CURLPAUSE_ALL;
778   }
779   if (NULL == s->msg_tk)
780     s->msg_tk = GNUNET_SERVER_mst_create (&client_receive_mst_cb, s);
781   GNUNET_SERVER_mst_receive (s->msg_tk, s, stream, len, GNUNET_NO, GNUNET_NO);
782   return len;
783
784 }
785
786 /**
787  * Task performing curl operations
788  *
789  * @param cls plugin as closure
790  * @param tc gnunet scheduler task context
791  */
792 static void
793 client_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
794
795 /**
796  * Function setting up file descriptors and scheduling task to run
797  *
798  * @param  plugin plugin as closure
799  * @param now schedule task in 1ms, regardless of what curl may say
800  * @return GNUNET_SYSERR for hard failure, GNUNET_OK for ok
801  */
802 static int
803 client_schedule (struct HTTP_Client_Plugin *plugin, int now)
804 {
805   fd_set rs;
806   fd_set ws;
807   fd_set es;
808   int max;
809   struct GNUNET_NETWORK_FDSet *grs;
810   struct GNUNET_NETWORK_FDSet *gws;
811   long to;
812   CURLMcode mret;
813   struct GNUNET_TIME_Relative timeout;
814
815   /* Cancel previous scheduled task */
816   if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
817   {
818     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
819     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
820   }
821   max = -1;
822   FD_ZERO (&rs);
823   FD_ZERO (&ws);
824   FD_ZERO (&es);
825   mret = curl_multi_fdset (plugin->curl_multi_handle, &rs, &ws, &es, &max);
826   if (mret != CURLM_OK)
827   {
828     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
829                 "curl_multi_fdset", __FILE__, __LINE__,
830                 curl_multi_strerror (mret));
831     return GNUNET_SYSERR;
832   }
833   mret = curl_multi_timeout (plugin->curl_multi_handle, &to);
834   if (to == -1)
835     timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1);
836   else
837     timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, to);
838   if (now == GNUNET_YES)
839     timeout = GNUNET_TIME_UNIT_MILLISECONDS;
840
841   if (mret != CURLM_OK)
842   {
843     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("%s failed at %s:%d: `%s'\n"),
844                 "curl_multi_timeout", __FILE__, __LINE__,
845                 curl_multi_strerror (mret));
846     return GNUNET_SYSERR;
847   }
848
849   grs = GNUNET_NETWORK_fdset_create ();
850   gws = GNUNET_NETWORK_fdset_create ();
851   GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
852   GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
853
854   plugin->client_perform_task =
855       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
856                                    timeout, grs, gws,
857                                    &client_run, plugin);
858   GNUNET_NETWORK_fdset_destroy (gws);
859   GNUNET_NETWORK_fdset_destroy (grs);
860   return GNUNET_OK;
861 }
862
863
864
865 /**
866  * Task performing curl operations
867  *
868  * @param cls plugin as closure
869  * @param tc gnunet scheduler task context
870  */
871 static void
872 client_run (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
873 {
874   struct HTTP_Client_Plugin *plugin = cls;
875   int running;
876   CURLMcode mret;
877
878   GNUNET_assert (cls != NULL);
879
880   plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
881   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
882     return;
883
884   do
885   {
886     running = 0;
887     mret = curl_multi_perform (plugin->curl_multi_handle, &running);
888
889     CURLMsg *msg;
890     int msgs_left;
891
892     while ((msg = curl_multi_info_read (plugin->curl_multi_handle, &msgs_left)))
893     {
894       CURL *easy_h = msg->easy_handle;
895       struct Session *s = NULL;
896       char *d = (char *) s;
897
898       if (easy_h == NULL)
899       {
900         GNUNET_break (0);
901         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
902                          "Client: connection to ended with reason %i: `%s', %i handles running\n",
903                          msg->data.result,
904                          curl_easy_strerror (msg->data.result), running);
905         continue;
906       }
907
908       GNUNET_assert (CURLE_OK ==
909                      curl_easy_getinfo (easy_h, CURLINFO_PRIVATE, &d));
910       s = (struct Session *) d;
911
912       if (GNUNET_YES != client_exist_session(plugin, s))
913       {
914         GNUNET_break (0);
915         return;
916       }
917
918       GNUNET_assert (s != NULL);
919       if (msg->msg == CURLMSG_DONE)
920       {
921         GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
922                          "Session %p/connection %p: connection to `%s' ended with reason %i: `%s'\n",
923                          s, msg->easy_handle, GNUNET_i2s (&s->target),
924                          msg->data.result,
925                          curl_easy_strerror (msg->data.result));
926
927         /* Disconnect other transmission direction and tell transport */
928         client_disconnect (s);
929       }
930     }
931   }
932   while (mret == CURLM_CALL_MULTI_PERFORM);
933   client_schedule (plugin, GNUNET_NO);
934 }
935
936 static int
937 client_connect (struct Session *s)
938 {
939
940   struct HTTP_Client_Plugin *plugin = s->plugin;
941   int res = GNUNET_OK;
942   char *url;
943   CURLMcode mret;
944
945   /* create url */
946   if (NULL == http_common_plugin_address_to_string (NULL, s->addr, s->addrlen))
947   {
948     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
949                      "Invalid address peer `%s'\n",
950                      GNUNET_i2s (&s->target));
951     return GNUNET_SYSERR;
952   }
953
954   GNUNET_asprintf (&url, "%s/%s;%u",
955       http_common_plugin_address_to_string (plugin, s->addr, s->addrlen),
956                    GNUNET_h2s_full (&plugin->env->my_identity->hashPubKey),
957                    plugin->last_tag);
958   plugin->last_tag++;
959   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
960                    "Initiating outbound session peer `%s' using address `%s'\n",
961                    GNUNET_i2s (&s->target), url);
962
963   /* create get connection */
964   s->client_get = curl_easy_init ();
965 #if VERBOSE_CURL
966   curl_easy_setopt (s->client_get, CURLOPT_VERBOSE, 1L);
967   curl_easy_setopt (s->client_get, CURLOPT_DEBUGFUNCTION, &client_log);
968   curl_easy_setopt (s->client_get, CURLOPT_DEBUGDATA, s->client_get);
969 #endif
970 #if BUILD_HTTPS
971   curl_easy_setopt (s->client_get, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
972   curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYPEER, 0);
973   curl_easy_setopt (s->client_get, CURLOPT_SSL_VERIFYHOST, 0);
974 #endif
975   curl_easy_setopt (s->client_get, CURLOPT_URL, url);
976   //curl_easy_setopt (s->client_get, CURLOPT_HEADERFUNCTION, &curl_get_header_cb);
977   //curl_easy_setopt (s->client_get, CURLOPT_WRITEHEADER, ps);
978   curl_easy_setopt (s->client_get, CURLOPT_READFUNCTION, client_send_cb);
979   curl_easy_setopt (s->client_get, CURLOPT_READDATA, s);
980   curl_easy_setopt (s->client_get, CURLOPT_WRITEFUNCTION, client_receive);
981   curl_easy_setopt (s->client_get, CURLOPT_WRITEDATA, s);
982   /* No timeout by default, timeout done with session timeout */
983   curl_easy_setopt (s->client_get, CURLOPT_TIMEOUT, 0);
984   curl_easy_setopt (s->client_get, CURLOPT_PRIVATE, s);
985   curl_easy_setopt (s->client_get, CURLOPT_CONNECTTIMEOUT_MS,
986                     (long) HTTP_CLIENT_NOT_VALIDATED_TIMEOUT.rel_value);
987   curl_easy_setopt (s->client_get, CURLOPT_BUFFERSIZE,
988                     2 * GNUNET_SERVER_MAX_MESSAGE_SIZE);
989 #if CURL_TCP_NODELAY
990   curl_easy_setopt (ps->recv_endpoint, CURLOPT_TCP_NODELAY, 1);
991 #endif
992
993   /* create put connection */
994   s->client_put = curl_easy_init ();
995 #if VERBOSE_CURL
996   curl_easy_setopt (s->client_put, CURLOPT_VERBOSE, 1L);
997   curl_easy_setopt (s->client_put, CURLOPT_DEBUGFUNCTION, &client_log);
998   curl_easy_setopt (s->client_put, CURLOPT_DEBUGDATA, s->client_put);
999 #endif
1000 #if BUILD_HTTPS
1001   curl_easy_setopt (s->client_put, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
1002   curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYPEER, 0);
1003   curl_easy_setopt (s->client_put, CURLOPT_SSL_VERIFYHOST, 0);
1004 #endif
1005   curl_easy_setopt (s->client_put, CURLOPT_URL, url);
1006   curl_easy_setopt (s->client_put, CURLOPT_UPLOAD, 1L);
1007   /*
1008   struct curl_slist *m_headerlist;
1009   m_headerlist = NULL;
1010   m_headerlist = curl_slist_append(m_headerlist, "Transfer-Encoding: chunked");
1011   curl_easy_setopt(s->client_put, CURLOPT_HTTPHEADER, m_headerlist);*/
1012   //curl_easy_setopt (s->client_put, CURLOPT_HEADERFUNCTION, &client_curl_header);
1013   //curl_easy_setopt (s->client_put, CURLOPT_WRITEHEADER, ps);
1014   curl_easy_setopt (s->client_put, CURLOPT_READFUNCTION, client_send_cb);
1015   curl_easy_setopt (s->client_put, CURLOPT_READDATA, s);
1016   curl_easy_setopt (s->client_put, CURLOPT_WRITEFUNCTION, client_receive);
1017   curl_easy_setopt (s->client_put, CURLOPT_WRITEDATA, s);
1018   /* No timeout by default, timeout done with session timeout */
1019   curl_easy_setopt (s->client_put, CURLOPT_TIMEOUT, 0);
1020   curl_easy_setopt (s->client_put, CURLOPT_PRIVATE, s);
1021   curl_easy_setopt (s->client_put, CURLOPT_CONNECTTIMEOUT_MS,
1022                     (long) HTTP_CLIENT_NOT_VALIDATED_TIMEOUT.rel_value);
1023   curl_easy_setopt (s->client_put, CURLOPT_BUFFERSIZE,
1024                     2 * GNUNET_SERVER_MAX_MESSAGE_SIZE);
1025 #if CURL_TCP_NODELAY
1026   curl_easy_setopt (s->client_put, CURLOPT_TCP_NODELAY, 1);
1027 #endif
1028   GNUNET_free (url);
1029
1030   mret = curl_multi_add_handle (plugin->curl_multi_handle, s->client_get);
1031   if (mret != CURLM_OK)
1032   {
1033     curl_easy_cleanup (s->client_get);
1034     GNUNET_break (0);
1035     return GNUNET_SYSERR;
1036   }
1037
1038   mret = curl_multi_add_handle (plugin->curl_multi_handle, s->client_put);
1039   if (mret != CURLM_OK)
1040   {
1041     curl_multi_remove_handle (plugin->curl_multi_handle, s->client_get);
1042     curl_easy_cleanup (s->client_get);
1043     curl_easy_cleanup (s->client_put);
1044     GNUNET_break (0);
1045     return GNUNET_SYSERR;
1046   }
1047
1048   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1049                    "Session %p: connected with connections GET %p and PUT %p\n",
1050                    s, s->client_get, s->client_put);
1051
1052   /* Perform connect */
1053   plugin->cur_connections += 2;
1054   GNUNET_STATISTICS_set (plugin->env->stats,
1055       "# HTTP client connections",
1056       plugin->cur_connections,
1057       GNUNET_NO);
1058
1059   /* Re-schedule since handles have changed */
1060   if (plugin->client_perform_task != GNUNET_SCHEDULER_NO_TASK)
1061   {
1062     GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
1063     plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
1064   }
1065   plugin->client_perform_task = GNUNET_SCHEDULER_add_now (client_run, plugin);
1066   return res;
1067 }
1068
1069
1070 /**
1071  * Creates a new outbound session the transport service will use to send data to the
1072  * peer
1073  *
1074  * @param cls the plugin
1075  * @param address the address
1076  * @return the session or NULL of max connections exceeded
1077  */
1078 static struct Session *
1079 http_client_plugin_get_session (void *cls,
1080                   const struct GNUNET_HELLO_Address *address)
1081 {
1082   struct HTTP_Client_Plugin *plugin = cls;
1083   struct Session * s = NULL;
1084   struct sockaddr *sa;
1085   struct GNUNET_ATS_Information ats;
1086   size_t salen = 0;
1087   int res;
1088
1089   GNUNET_assert (plugin != NULL);
1090   GNUNET_assert (address != NULL);
1091   GNUNET_assert (address->address != NULL);
1092
1093
1094   /* find existing session */
1095   s = client_lookup_session (plugin, address);
1096   if (s != NULL)
1097     return s;
1098
1099   if (plugin->max_connections <= plugin->cur_connections)
1100   {
1101     GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name,
1102                      "Maximum number of connections (%u) reached: "
1103                      "cannot connect to peer `%s'\n",
1104                      plugin->max_connections,
1105                      GNUNET_i2s (&address->peer));
1106     return NULL;
1107   }
1108
1109   ats.type = htonl (GNUNET_ATS_NETWORK_TYPE);
1110   ats.value = htonl (GNUNET_ATS_NET_UNSPECIFIED);
1111   sa = http_common_socket_from_address (address->address, address->address_length, &res);
1112
1113   if (GNUNET_SYSERR == res)
1114   {
1115       return NULL;
1116   }
1117   else if (GNUNET_YES == res)
1118   {
1119       GNUNET_assert (NULL != sa);
1120       if (AF_INET == sa->sa_family)
1121       {
1122           salen = sizeof (struct sockaddr_in);
1123       }
1124       else if (AF_INET == sa->sa_family)
1125       {
1126           salen = sizeof (struct sockaddr_in6);
1127       }
1128       ats = plugin->env->get_address_type (plugin->env->cls, sa, salen);
1129       GNUNET_free (sa);
1130   }
1131   else if (GNUNET_NO == res)
1132   {
1133       ats.value = htonl (GNUNET_ATS_COST_WAN);
1134   }
1135
1136   if (GNUNET_ATS_NET_UNSPECIFIED == ntohl(ats.value))
1137   {
1138       GNUNET_break (0);
1139       return NULL;
1140   }
1141
1142   s = GNUNET_malloc (sizeof (struct Session));
1143   memcpy (&s->target, &address->peer, sizeof (struct GNUNET_PeerIdentity));
1144   s->plugin = plugin;
1145   s->addr = GNUNET_malloc (address->address_length);
1146   memcpy (s->addr, address->address, address->address_length);
1147   s->addrlen = address->address_length;
1148   s->ats_address_network_type = ats.value;
1149
1150   client_start_session_timeout (s);
1151
1152   /* add new session */
1153   GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s);
1154
1155   /* initiate new connection */
1156   if (GNUNET_SYSERR == client_connect (s))
1157   {
1158     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1159                      "Cannot connect to peer `%s' address `%s''\n",
1160                      http_common_plugin_address_to_string (NULL, s->addr, s->addrlen),
1161                      GNUNET_i2s (&s->target));
1162     client_delete_session (s);
1163     return NULL;
1164   }
1165   return s;
1166 }
1167
1168 static int
1169 client_start (struct HTTP_Client_Plugin *plugin)
1170 {
1171   curl_global_init (CURL_GLOBAL_ALL);
1172   plugin->curl_multi_handle = curl_multi_init ();
1173
1174   if (NULL == plugin->curl_multi_handle)
1175   {
1176     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
1177                      _("Could not initialize curl multi handle, failed to start %s plugin!\n"),
1178                      plugin->name);
1179     return GNUNET_SYSERR;
1180   }
1181   return GNUNET_OK;
1182 }
1183
1184 /**
1185  * Session was idle, so disconnect it
1186  */
1187 static void
1188 client_session_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1189 {
1190   GNUNET_assert (NULL != cls);
1191   struct Session *s = cls;
1192
1193   s->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1194   GNUNET_log (TIMEOUT_LOG,
1195               "Session %p was idle for %llu ms, disconnecting\n",
1196               s, (unsigned long long) CLIENT_SESSION_TIMEOUT.rel_value);
1197
1198   /* call session destroy function */
1199   GNUNET_assert (GNUNET_OK == client_disconnect (s));
1200 }
1201
1202 /**
1203 * Start session timeout
1204 */
1205 static void
1206 client_start_session_timeout (struct Session *s)
1207 {
1208
1209  GNUNET_assert (NULL != s);
1210  GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == s->timeout_task);
1211  s->timeout_task =  GNUNET_SCHEDULER_add_delayed (CLIENT_SESSION_TIMEOUT,
1212                                                   &client_session_timeout,
1213                                                   s);
1214  GNUNET_log (TIMEOUT_LOG,
1215              "Timeout for session %p set to %llu ms\n",
1216              s,  (unsigned long long) CLIENT_SESSION_TIMEOUT.rel_value);
1217 }
1218
1219 /**
1220 * Increment session timeout due to activity
1221 */
1222 static void
1223 client_reschedule_session_timeout (struct Session *s)
1224 {
1225
1226  GNUNET_assert (NULL != s);
1227  GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != s->timeout_task);
1228
1229  GNUNET_SCHEDULER_cancel (s->timeout_task);
1230  s->timeout_task =  GNUNET_SCHEDULER_add_delayed (CLIENT_SESSION_TIMEOUT,
1231                                                   &client_session_timeout,
1232                                                   s);
1233  GNUNET_log (TIMEOUT_LOG,
1234              "Timeout rescheduled for session %p set to %llu ms\n",
1235              s, (unsigned long long) CLIENT_SESSION_TIMEOUT.rel_value);
1236 }
1237
1238 /**
1239 * Cancel timeout
1240 */
1241 static void
1242 client_stop_session_timeout (struct Session *s)
1243 {
1244  GNUNET_assert (NULL != s);
1245
1246  if (GNUNET_SCHEDULER_NO_TASK != s->timeout_task)
1247  {
1248    GNUNET_SCHEDULER_cancel (s->timeout_task);
1249    s->timeout_task = GNUNET_SCHEDULER_NO_TASK;
1250    GNUNET_log (TIMEOUT_LOG, "Timeout stopped for session %p\n", s);
1251  }
1252 }
1253
1254
1255 /**
1256  * Another peer has suggested an address for this
1257  * peer and transport plugin.  Check that this could be a valid
1258  * address.  If so, consider adding it to the list
1259  * of addresses.
1260  *
1261  * @param cls closure
1262  * @param addr pointer to the address
1263  * @param addrlen length of addr
1264  * @return GNUNET_OK if this is a plausible address for this peer
1265  *         and transport
1266  */
1267 static int
1268 http_client_plugin_address_suggested (void *cls, const void *addr, size_t addrlen)
1269 {
1270   /* struct Plugin *plugin = cls; */
1271
1272   /* A HTTP/S client does not have any valid address so:*/
1273   return GNUNET_NO;
1274 }
1275
1276 /**
1277  * Exit point from the plugin.
1278  */
1279 void *
1280 LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls)
1281 {
1282   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
1283   struct HTTP_Client_Plugin *plugin = api->cls;
1284   struct Session *pos;
1285   struct Session *next;
1286
1287   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1288                    _("Shutting down plugin `%s'\n"),
1289                    plugin->name);
1290
1291   if (NULL == api->cls)
1292   {
1293     /* Stub shutdown */
1294     GNUNET_free (api);
1295     return NULL;
1296   }
1297
1298   next = plugin->head;
1299   while (NULL != (pos = next))
1300   {
1301       next = pos->next;
1302       client_disconnect (pos);
1303   }
1304   if (GNUNET_SCHEDULER_NO_TASK != plugin->client_perform_task)
1305   {
1306       GNUNET_SCHEDULER_cancel (plugin->client_perform_task);
1307       plugin->client_perform_task = GNUNET_SCHEDULER_NO_TASK;
1308   }
1309
1310
1311   if (NULL != plugin->curl_multi_handle)
1312   {
1313     curl_multi_cleanup (plugin->curl_multi_handle);
1314     plugin->curl_multi_handle = NULL;
1315   }
1316   curl_global_cleanup ();
1317
1318   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1319                    _("Shutdown for plugin `%s' complete\n"),
1320                    plugin->name);
1321
1322   GNUNET_free (plugin);
1323   GNUNET_free (api);
1324   return NULL;
1325 }
1326
1327
1328 static int
1329 client_configure_plugin (struct HTTP_Client_Plugin *plugin)
1330 {
1331   unsigned long long max_connections;
1332
1333   /* Optional parameters */
1334   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (plugin->env->cfg,
1335                       plugin->name,
1336                       "MAX_CONNECTIONS", &max_connections))
1337     max_connections = 128;
1338   plugin->max_connections = max_connections;
1339
1340   GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG, plugin->name,
1341                    _("Maximum number of connections is %u\n"),
1342                    plugin->max_connections);
1343   return GNUNET_OK;
1344 }
1345
1346 /**
1347  * Entry point for the plugin.
1348  */
1349 void *
1350 LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls)
1351 {
1352   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
1353   struct GNUNET_TRANSPORT_PluginFunctions *api;
1354   struct HTTP_Client_Plugin *plugin;
1355
1356   if (NULL == env->receive)
1357   {
1358     /* run in 'stub' mode (i.e. as part of gnunet-peerinfo), don't fully
1359        initialze the plugin or the API */
1360     api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
1361     api->cls = NULL;
1362     api->address_to_string = &http_common_plugin_address_to_string;
1363     api->string_to_address = &http_common_plugin_string_to_address;
1364     api->address_pretty_printer = &http_common_plugin_address_pretty_printer;
1365     return api;
1366   }
1367
1368   plugin = GNUNET_malloc (sizeof (struct HTTP_Client_Plugin));
1369   p = plugin;
1370   plugin->env = env;
1371   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
1372   api->cls = plugin;
1373   api->send = &http_client_plugin_send;
1374   api->disconnect = &http_client_plugin_disconnect;
1375   api->check_address = &http_client_plugin_address_suggested;
1376   api->get_session = &http_client_plugin_get_session;
1377   api->address_to_string = &http_common_plugin_address_to_string;
1378   api->string_to_address = &http_common_plugin_string_to_address;
1379   api->address_pretty_printer = &http_common_plugin_address_pretty_printer;
1380
1381
1382 #if BUILD_HTTPS
1383   plugin->name = "transport-https_client";
1384   plugin->protocol = "https";
1385 #else
1386   plugin->name = "transport-http_client";
1387   plugin->protocol = "http";
1388 #endif
1389   plugin->last_tag = 1;
1390
1391   if (GNUNET_SYSERR == client_configure_plugin (plugin))
1392   {
1393       LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
1394       return NULL;
1395   }
1396
1397   /* Start client */
1398   if (GNUNET_SYSERR == client_start (plugin))
1399   {
1400       LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
1401       return NULL;
1402   }
1403   return api;
1404 }
1405
1406 /* end of plugin_transport_http_client.c */