changes
[oweals/gnunet.git] / src / transport / plugin_transport_http_client.c
1 /*
2      This file is part of GNUnet
3      (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file transport/plugin_transport_http_client.c
23  * @brief HTTP/S client transport plugin
24  * @author Matthias Wachs
25  */
26
27 #if BUILD_HTTPS
28 #define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_https_client_init
29 #define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_https_client_done
30 #else
31 #define LIBGNUNET_PLUGIN_TRANSPORT_INIT libgnunet_plugin_transport_http_client_init
32 #define LIBGNUNET_PLUGIN_TRANSPORT_DONE libgnunet_plugin_transport_http_client_done
33 #endif
34
35
36 #include "platform.h"
37 #include "gnunet_protocols.h"
38 #include "gnunet_connection_lib.h"
39 #include "gnunet_server_lib.h"
40 #include "gnunet_service_lib.h"
41 #include "gnunet_statistics_service.h"
42 #include "gnunet_transport_service.h"
43 #include "gnunet_transport_plugin.h"
44 #include "plugin_transport_http_common.h"
45 #include <curl/curl.h>
46
47
48 #define DEBUG_TEMPLATE GNUNET_EXTRA_LOGGING
49
50 /**
51  * After how long do we expire an address that we
52  * learned from another peer if it is not reconfirmed
53  * by anyone?
54  */
55 #define LEARNED_ADDRESS_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 6)
56
57
58 /**
59  * Encapsulation of all of the state of the plugin.
60  */
61 struct HTTP_Client_Plugin;
62
63
64 /**
65  * Session handle for connections.
66  */
67 struct Session
68 {
69   /**
70    * To whom are we talking to (set to our identity
71    * if we are still waiting for the welcome message)
72    */
73   struct GNUNET_PeerIdentity sender;
74
75   /**
76    * Stored in a linked list.
77    */
78   struct Session *next;
79
80   /**
81    * Pointer to the global plugin struct.
82    */
83   struct HTTP_Client_Plugin *plugin;
84
85   /**
86    * The client (used to identify this connection)
87    */
88   /* void *client; */
89
90   /**
91    * Continuation function to call once the transmission buffer
92    * has again space available.  NULL if there is no
93    * continuation to call.
94    */
95   GNUNET_TRANSPORT_TransmitContinuation transmit_cont;
96
97   /**
98    * Closure for transmit_cont.
99    */
100   void *transmit_cont_cls;
101
102   /**
103    * At what time did we reset last_received last?
104    */
105   struct GNUNET_TIME_Absolute last_quota_update;
106
107   /**
108    * How many bytes have we received since the "last_quota_update"
109    * timestamp?
110    */
111   uint64_t last_received;
112
113   /**
114    * Number of bytes per ms that this peer is allowed
115    * to send to us.
116    */
117   uint32_t quota;
118
119 };
120
121 /**
122  * Encapsulation of all of the state of the plugin.
123  */
124 struct HTTP_Client_Plugin
125 {
126   /**
127    * Our environment.
128    */
129   struct GNUNET_TRANSPORT_PluginEnvironment *env;
130
131   /**
132    * Linked list head of open sessions.
133    */
134   struct Session *head;
135
136   /**
137    * Linked list tail of open sessions.
138    */
139   struct Session *tail;
140
141   /**
142    * Plugin name
143    */
144   char *name;
145
146   /**
147    * Protocol
148    */
149   char *protocol;
150
151   /**
152    * use IPv6
153    */
154   uint16_t use_ipv6;
155
156   /**
157    * use IPv4
158    */
159   uint16_t use_ipv4;
160
161   /**
162    * cURL Multihandle
163    */
164   CURLM *curl_multi_handle;
165 };
166
167
168 /**
169  * Function that can be used by the transport service to transmit
170  * a message using the plugin.   Note that in the case of a
171  * peer disconnecting, the continuation MUST be called
172  * prior to the disconnect notification itself.  This function
173  * will be called with this peer's HELLO message to initiate
174  * a fresh connection to another peer.
175  *
176  * @param cls closure
177  * @param session which session must be used
178  * @param msgbuf the message to transmit
179  * @param msgbuf_size number of bytes in 'msgbuf'
180  * @param priority how important is the message (most plugins will
181  *                 ignore message priority and just FIFO)
182  * @param to how long to wait at most for the transmission (does not
183  *                require plugins to discard the message after the timeout,
184  *                just advisory for the desired delay; most plugins will ignore
185  *                this as well)
186  * @param cont continuation to call once the message has
187  *        been transmitted (or if the transport is ready
188  *        for the next transmission call; or if the
189  *        peer disconnected...); can be NULL
190  * @param cont_cls closure for cont
191  * @return number of bytes used (on the physical network, with overheads);
192  *         -1 on hard errors (i.e. address invalid); 0 is a legal value
193  *         and does NOT mean that the message was not transmitted (DV)
194  */
195 static ssize_t
196 http_client_plugin_send (void *cls,
197                   struct Session *session,
198                   const char *msgbuf, size_t msgbuf_size,
199                   unsigned int priority,
200                   struct GNUNET_TIME_Relative to,
201                   GNUNET_TRANSPORT_TransmitContinuation cont, void *cont_cls)
202 {
203   struct HTTP_Client_Plugin *plugin = cls;
204   int bytes_sent = 0;
205
206   GNUNET_assert (plugin != NULL);
207   GNUNET_assert (session != NULL);
208
209   /*  struct Plugin *plugin = cls; */
210   return bytes_sent;
211 }
212
213
214
215 /**
216  * Function that can be used to force the plugin to disconnect
217  * from the given peer and cancel all previous transmissions
218  * (and their continuationc).
219  *
220  * @param cls closure
221  * @param target peer from which to disconnect
222  */
223 static void
224 http_client_plugin_disconnect (void *cls, const struct GNUNET_PeerIdentity *target)
225 {
226   // struct Plugin *plugin = cls;
227   // FIXME
228 }
229
230 static void
231 client_stop (struct HTTP_Client_Plugin *plugin)
232 {
233   if (NULL != plugin->curl_multi_handle)
234   {
235     curl_multi_cleanup (plugin->curl_multi_handle);
236     plugin->curl_multi_handle = NULL;
237   }
238   curl_global_cleanup ();
239 }
240
241 /**
242  * Creates a new outbound session the transport service will use to send data to the
243  * peer
244  *
245  * @param cls the plugin
246  * @param address the address
247  * @return the session or NULL of max connections exceeded
248  */
249 static struct Session *
250 http_client_plugin_get_session (void *cls,
251                   const struct GNUNET_HELLO_Address *address)
252 {
253   struct HTTP_Client_Plugin *plugin = cls;
254   struct Session * s = NULL;
255 //  size_t addrlen;
256
257   GNUNET_assert (plugin != NULL);
258   GNUNET_assert (address != NULL);
259   GNUNET_assert (address->address != NULL);
260
261   GNUNET_break (0);
262
263   /* find existing session */
264 #if 0
265   s = lookup_session (plugin, address);
266   if (s != NULL)
267     return s;
268
269   if (plugin->max_connections <= plugin->cur_connections)
270   {
271     GNUNET_log_from (GNUNET_ERROR_TYPE_WARNING, plugin->name,
272                      "Maximum number of connections reached, "
273                      "cannot connect to peer `%s'\n", GNUNET_i2s (&address->peer));
274     return NULL;
275   }
276
277   /* create new session */
278   addrlen = address->address_length;
279
280   GNUNET_assert (addrlen > sizeof (struct HttpAddress));
281
282   s = create_session (plugin, &address->peer, address->address, address->address_length);
283
284   /* add new session */
285   GNUNET_CONTAINER_DLL_insert (plugin->head, plugin->tail, s);
286   /* initiate new connection */
287   if (GNUNET_SYSERR == client_connect (s))
288   {
289     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
290                      "Cannot connect to peer `%s' address `%s''\n",
291                      http_plugin_address_to_string(NULL, s->addr, s->addrlen),
292                      GNUNET_i2s (&s->target));
293     GNUNET_CONTAINER_DLL_remove (plugin->head, plugin->tail, s);
294     delete_session (s);
295     return NULL;
296   }
297 #endif
298   return s;
299 }
300
301 static int
302 client_start (struct HTTP_Client_Plugin *plugin)
303 {
304   curl_global_init (CURL_GLOBAL_ALL);
305   plugin->curl_multi_handle = curl_multi_init ();
306
307   if (NULL == plugin->curl_multi_handle)
308   {
309     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, plugin->name,
310                      _("Could not initialize curl multi handle, failed to start %s plugin!\n"),
311                      plugin->name);
312     return GNUNET_SYSERR;
313   }
314   return GNUNET_OK;
315 }
316
317 /**
318  * Another peer has suggested an address for this
319  * peer and transport plugin.  Check that this could be a valid
320  * address.  If so, consider adding it to the list
321  * of addresses.
322  *
323  * @param cls closure
324  * @param addr pointer to the address
325  * @param addrlen length of addr
326  * @return GNUNET_OK if this is a plausible address for this peer
327  *         and transport
328  */
329 static int
330 http_client_plugin_address_suggested (void *cls, const void *addr, size_t addrlen)
331 {
332   /* struct Plugin *plugin = cls; */
333
334   /* A HTTP/S client does not have any valid address so:*/
335   return GNUNET_NO;
336 }
337
338 /**
339  * Exit point from the plugin.
340  */
341 void *
342 LIBGNUNET_PLUGIN_TRANSPORT_DONE (void *cls)
343 {
344   struct GNUNET_TRANSPORT_PluginFunctions *api = cls;
345   struct HTTP_Client_Plugin *plugin = api->cls;
346
347   client_stop (plugin);
348
349   GNUNET_free (plugin);
350   GNUNET_free (api);
351   return NULL;
352 }
353
354
355 /**
356  * Entry point for the plugin.
357  */
358 void *
359 LIBGNUNET_PLUGIN_TRANSPORT_INIT (void *cls)
360 {
361   struct GNUNET_TRANSPORT_PluginEnvironment *env = cls;
362   struct GNUNET_TRANSPORT_PluginFunctions *api;
363   struct HTTP_Client_Plugin *plugin;
364
365   plugin = GNUNET_malloc (sizeof (struct HTTP_Client_Plugin));
366   plugin->env = env;
367   api = GNUNET_malloc (sizeof (struct GNUNET_TRANSPORT_PluginFunctions));
368   api->cls = plugin;
369   api->send = &http_client_plugin_send;
370   api->disconnect = &http_client_plugin_disconnect;
371   api->check_address = &http_client_plugin_address_suggested;
372   api->get_session = &http_client_plugin_get_session;
373
374   api->address_to_string = &http_common_plugin_address_to_string;
375   api->string_to_address = &http_common_plugin_string_to_address;
376   api->address_pretty_printer = &http_common_plugin_address_pretty_printer;
377
378 #if BUILD_HTTPS
379   plugin->name = "transport-https_client";
380   plugin->protocol = "https";
381 #else
382   plugin->name = "transport-http_client";
383   plugin->protocol = "http";
384 #endif
385
386   /* Start client */
387   if (GNUNET_SYSERR == client_start (plugin))
388   {
389       LIBGNUNET_PLUGIN_TRANSPORT_DONE (api);
390       return NULL;
391   }
392   return api;
393 }
394
395 /* end of plugin_transport_http_client.c */