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