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