(no commit message)
[oweals/gnunet.git] / src / transport / test_plugin_transport_http.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010 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 2, 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  * @file transport/test_plugin_transport_http.c
22  * @brief testcase for plugin_transport_http.c
23  * @author Matthias Wachs
24  */
25
26 #include "platform.h"
27 #include "gnunet_constants.h"
28 #include "gnunet_getopt_lib.h"
29 #include "gnunet_hello_lib.h"
30 #include "gnunet_os_lib.h"
31 #include "gnunet_peerinfo_service.h"
32 #include "gnunet_plugin_lib.h"
33 #include "gnunet_protocols.h"
34 #include "gnunet_program_lib.h"
35 #include "gnunet_signatures.h"
36 #include "gnunet_service_lib.h"
37 #include "plugin_transport.h"
38 #include "gnunet_statistics_service.h"
39 #include "transport.h"
40 #include <curl/curl.h>
41
42 #define VERBOSE GNUNET_YES
43 #define DEBUG GNUNET_YES
44
45 #define PLUGIN libgnunet_plugin_transport_template
46
47 /**
48  * How long until we give up on transmitting the message?
49  */
50 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 90)
51
52 /**
53  * How long until we give up on transmitting the message?
54  */
55 #define STAT_INTERVALL GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 3)
56
57 /**
58  * Our public key.
59  */
60 /* static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key; */
61
62 /**
63  * Our public key.
64  */
65 static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key;
66
67 /**
68  * Our identity.
69  */
70 static struct GNUNET_PeerIdentity my_identity;
71
72 /**
73  * Our private key.
74  */
75 static struct GNUNET_CRYPTO_RsaPrivateKey *my_private_key;
76
77
78 /**
79  * Our scheduler.
80  */
81 struct GNUNET_SCHEDULER_Handle *sched;
82
83 /**
84  * Our statistics handle.
85  */
86 struct GNUNET_STATISTICS_Handle *stats;
87
88
89 /**
90  * Our configuration.
91  */
92 const struct GNUNET_CONFIGURATION_Handle *cfg;
93
94 /**
95  * Number of neighbours we'd like to have.
96  */
97 static uint32_t max_connect_per_transport;
98
99 /**
100  * Environment for this plugin.
101  */
102 static struct GNUNET_TRANSPORT_PluginEnvironment env;
103
104 /**
105  *handle for the api provided by this plugin
106  */
107 static struct GNUNET_TRANSPORT_PluginFunctions *api;
108
109 /**
110  * ID of the task controlling the testcase timeout
111  */
112 static GNUNET_SCHEDULER_TaskIdentifier ti_timeout;
113
114 #if 0
115 static GNUNET_SCHEDULER_TaskIdentifier ti_download;
116 #endif
117
118 static unsigned int timeout_count;
119
120 /**
121  * Did the test pass or fail?
122  */
123 static int fail;
124
125 //static int done;
126
127 pid_t pid;
128
129 /**
130  * Initialize Environment for this plugin
131  */
132 static struct GNUNET_TIME_Relative
133 receive (void *cls,
134          const struct GNUNET_PeerIdentity * peer,
135          const struct GNUNET_MessageHeader * message,
136          uint32_t distance,
137          struct Session *session,
138          const char *sender_address,
139          uint16_t sender_address_len)
140 {
141   /* do nothing */
142   return GNUNET_TIME_UNIT_ZERO;
143 }
144
145 void
146 notify_address (void *cls,
147                 const char *name,
148                 const void *addr,
149                 uint16_t addrlen,
150                 struct GNUNET_TIME_Relative expires)
151 {
152
153 }
154
155 /**
156  * Simple example test that invokes
157  * the check_address function of the plugin.
158  */
159 /* FIXME: won't work on IPv6 enabled systems where IPv4 mapping
160  * isn't enabled (eg. FreeBSD > 4)[TESTING]
161 WEAKRANDOM = YES
162  */
163 static void
164 shutdown_clean ()
165 {
166 /*  if (stat_get_handle != NULL)
167   {
168     GNUNET_STATISTICS_get_cancel(stat_get_handle);
169   }*/
170
171   /* if ( NULL!=stats )GNUNET_STATISTICS_destroy (stats, GNUNET_YES); */
172   if (ti_timeout != GNUNET_SCHEDULER_NO_TASK)
173     GNUNET_SCHEDULER_cancel(sched,ti_timeout);
174   ti_timeout = GNUNET_SCHEDULER_NO_TASK;
175
176   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Unloading http plugin\n");
177   GNUNET_assert (NULL == GNUNET_PLUGIN_unload ("libgnunet_plugin_transport_http", api));
178   
179   GNUNET_SCHEDULER_shutdown(sched);
180   /* FIXME: */ fail = GNUNET_NO;
181   return;
182 }
183
184 static void
185 setup_plugin_environment ()
186 {
187   env.cfg = cfg;
188   env.sched = sched;
189   env.stats = stats;
190   env.my_identity = &my_identity;
191   env.cls = &env;
192   env.receive = &receive;
193   env.notify_address = &notify_address;
194   env.max_connections = max_connect_per_transport;
195 }
196
197 #if 0
198 static int
199 process_stat (void *cls,
200               const char *subsystem,
201               const char *name,
202               uint64_t value,
203               int is_persistent)
204 {
205   stat_get_handle = NULL;
206   if (value==1)
207     {
208     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutdown, plugin failed \n");
209     fail = GNUNET_YES;
210     shutdown_clean();
211     return GNUNET_YES;
212     }
213   if (value==2)
214     {
215     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Shutdown, plugin not failed \n");
216     shutdown_clean();
217     return GNUNET_YES;
218     }
219   return GNUNET_YES;
220 }
221 #endif
222
223 #if 0
224 static void
225 cont_func (void *cls, int success)
226 {
227   stat_get_handle = NULL;
228 }
229 #endif
230
231 /**
232  * Task that checks if we should try to download a hostlist.
233  * If so, we initiate the download, otherwise we schedule
234  * this task again for a later time.
235  */
236 static void
237 task_timeout (void *cls,
238             const struct GNUNET_SCHEDULER_TaskContext *tc)
239 {
240   ti_timeout = GNUNET_SCHEDULER_NO_TASK;
241   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
242     return;
243
244   if ( timeout_count > 15 )
245   {
246     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Testcase timeout\n",  timeout_count);
247     fail = GNUNET_YES;
248     shutdown_clean();
249     return;
250   }
251   timeout_count++;
252
253 /*  stat_get_handle = GNUNET_STATISTICS_get (stats,
254                                            "http-transport",
255                                            gettext_noop("shutdown"),
256                                            GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 2),
257                                            &cont_func,
258                                            &process_stat,
259                                            NULL);*/
260
261   ti_timeout = GNUNET_SCHEDULER_add_delayed (sched, STAT_INTERVALL, &task_timeout, NULL);
262   return;
263 }
264
265 #if 0
266 static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream)
267 {
268   size_t retcode;
269
270   /* in real-world cases, this would probably get this data differently
271      as this fread() stuff is exactly what the library already would do
272      by default internally */
273   retcode = fread(ptr, size, nmemb, stream);
274
275   fprintf(stderr, "*** We read %d bytes from file\n", (int) retcode);
276
277   return retcode;
278 }
279 #endif
280
281 struct CBC
282 {
283   char *buf;
284   size_t pos;
285   size_t size;
286 };
287
288 #if 0
289 static size_t
290 putBuffer (void *stream, size_t size, size_t nmemb, void *ptr)
291 {
292   size_t len;
293
294   struct CBC  * cbc = ptr;
295
296   len = strlen(cbc->buf);
297
298   if (( cbc->pos == len) && (len < (size * nmemb)))
299     return 0;
300   memcpy(stream, cbc->buf, len+1);
301   cbc->pos = len;
302   return len;
303 }
304 #endif
305
306 #if 0
307 static int execute (char * url)
308 {
309   done = 0;
310   CURLM *multi_handle;
311   CURL *curl_handle;
312   CURLMsg *msg;
313   int msgs_left;
314   FILE * hd_src ;
315   int hd ;
316   struct stat file_info;
317   int still_running;
318   char buf[2048];
319   struct CBC cbc;
320
321   char *file = "curl.c";
322
323   cbc.buf = buf;
324   cbc.size = 2048;
325   cbc.pos = 0;
326
327   const char * txt = "Hello World!";
328   memcpy(cbc.buf,txt,strlen(txt)+1);
329   //fprintf(stderr,"%s %u\n",src,strlen(src));
330
331   /* get the file size of the local file */
332   hd = open(file, O_RDONLY) ;
333   fstat(hd, &file_info);
334   close(hd) ;
335
336   /* get a FILE * of the same file, could also be made with
337      fdopen() from the previous descriptor, but hey this is just
338      an example! */
339   hd_src = fopen(file, "rb");
340   //printf("size: %u \n", (curl_off_t) file_info.st_size);
341
342   /* get a curl handle */
343   curl_handle = curl_easy_init();
344   if( NULL == curl_handle)
345   {
346     printf("easy_init failed \n");
347     return 0;
348   }
349   curl_easy_setopt(curl_handle, CURLOPT_VERBOSE, 1L);
350   //curl_easy_setopt (curl_handle, CURLOPT_WRITEFUNCTION, &copyBuffer);
351   //curl_easy_setopt (curl_handle, CURLOPT_WRITEDATA, &cbc);
352   curl_easy_setopt (curl_handle, CURLOPT_READFUNCTION, &putBuffer);
353   curl_easy_setopt (curl_handle, CURLOPT_READDATA, &cbc);
354   curl_easy_setopt(curl_handle, CURLOPT_UPLOAD, 1L);
355   curl_easy_setopt(curl_handle, CURLOPT_PUT, 1L);
356   curl_easy_setopt(curl_handle, CURLOPT_URL, url);
357   curl_easy_setopt(curl_handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t) strlen(txt));
358
359   //curl_easy_setopt(curl_handle, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size);
360
361   multi_handle = curl_multi_init();
362   curl_multi_add_handle(multi_handle, curl_handle);
363
364   while(CURLM_CALL_MULTI_PERFORM ==
365         curl_multi_perform(multi_handle, &still_running));
366
367   while(still_running)
368   {
369       struct timeval timeout;
370       int rc; /* select() return code */
371
372       fd_set fdread;
373       fd_set fdwrite;
374       fd_set fdexcep;
375       int maxfd = -1;
376
377       FD_ZERO(&fdread);
378       FD_ZERO(&fdwrite);
379       FD_ZERO(&fdexcep);
380
381       /* set a suitable timeout to play around with */
382       timeout.tv_sec = 1;
383       timeout.tv_usec = 0;
384
385       /* get file descriptors from the transfers */
386       curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
387
388       /* In a real-world program you OF COURSE check the return code of the
389          function calls.  On success, the value of maxfd is guaranteed to be
390          greater or equal than -1.  We call select(maxfd + 1, ...), specially in
391          case of (maxfd == -1), we call select(0, ...), which is basically equal
392          to sleep. */
393
394       rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
395
396       switch(rc)
397       {
398       case -1:
399         /* select error */
400         break;
401       case 0:
402         /* timeout, do something else */
403         break;
404       default:
405         /* one or more of curl's file descriptors say there's data to read
406            or write */
407         while(CURLM_CALL_MULTI_PERFORM ==
408               curl_multi_perform(multi_handle, &still_running));
409         break;
410       }
411   }
412
413   /* See how the transfers went */
414   while ((msg = curl_multi_info_read(multi_handle, &msgs_left))) {
415     if (msg->msg == CURLMSG_DONE) {
416       int idx, found = 0;
417
418       /* Find out which handle this message is about */
419       for (idx=0; idx<1; idx++) {
420         found = (msg->easy_handle == curl_handle);
421         if(found)
422           break;
423       }
424
425       switch (idx) {
426       case 0:
427         printf("HTTP transfer completed with status %d\n", msg->data.result);
428         break;
429       }
430     }
431   }
432
433   curl_multi_cleanup(multi_handle);
434
435   curl_easy_cleanup(curl_handle);
436
437   fclose(hd_src); /* close the local file */
438   return 0;
439 }
440 #endif
441
442 #if 0
443 /**
444  * Task that checks if we should try to download a hostlist.
445  * If so, we initiate the download, otherwise we schedule
446  * this task again for a later time.
447  */
448 static void
449 task_download (void *cls,
450             const struct GNUNET_SCHEDULER_TaskContext *tc)
451 {
452   ti_download = GNUNET_SCHEDULER_NO_TASK;
453   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
454     return;
455
456   execute ("http://localhost:12389/Q7BO0ELEHAT8JNENQ90112G0TACH2H2HIR4IJ2JQ28U6FV14CK44EVP26FVAEALO7HIRJFLFE6709RP6IITM64B0FU7J4RA8KPNDKN8");
457
458   return;
459 }
460 #endif
461
462 /**
463  * Runs the test.
464  *
465  * @param cls closure
466  * @param s scheduler to use
467  * @param c configuration to use
468  */
469 static void
470 run (void *cls,
471      struct GNUNET_SCHEDULER_Handle *s,
472      char *const *args,
473      const char *cfgfile, const struct GNUNET_CONFIGURATION_Handle *c)
474 {
475   char * libname;
476   sched = s;
477   cfg = c;
478   char *keyfile;
479   unsigned long long tneigh;
480
481   /* settings up statistics */
482 /*  stats = GNUNET_STATISTICS_create (sched, "http-transport", cfg);
483   if (NULL == stats)
484   {
485     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
486                 _("Failed to retrieve statistics handle\n"));
487     fail = GNUNET_YES;
488     shutdown_clean();
489     return ;
490   }*/
491
492   /* parse configuration */
493   if ((GNUNET_OK !=
494        GNUNET_CONFIGURATION_get_value_number (c,
495                                               "TRANSPORT",
496                                               "NEIGHBOUR_LIMIT",
497                                               &tneigh)) ||
498       (GNUNET_OK !=
499        GNUNET_CONFIGURATION_get_value_filename (c,
500                                                 "GNUNETD",
501                                                 "HOSTKEY", &keyfile)))
502     {
503       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
504                   _
505                   ("Transport service is lacking key configuration settings.  Exiting.\n"));
506       GNUNET_SCHEDULER_shutdown (s);
507       return;
508     }
509   max_connect_per_transport = (uint32_t) tneigh;
510   my_private_key = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
511   GNUNET_free (keyfile);
512   if (my_private_key == NULL)
513     {
514       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
515                   _
516                   ("Transport service could not access hostkey.  Exiting.\n"));
517       GNUNET_SCHEDULER_shutdown (s);
518       return;
519     }
520   GNUNET_CRYPTO_rsa_key_get_public (my_private_key, &my_public_key);
521   GNUNET_CRYPTO_hash (&my_public_key,
522                       sizeof (my_public_key), &my_identity.hashPubKey);
523
524   /* load plugins... */
525   setup_plugin_environment ();
526   GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Loading HTTP transport plugin `%s'\n"),"libgnunet_plugin_transport_http");
527   GNUNET_asprintf (&libname, "libgnunet_plugin_transport_http");
528   api = GNUNET_PLUGIN_load (libname, &env);
529   GNUNET_free (libname);
530   if (api == NULL)
531   {
532     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
533                 _("Failed to load transport plugin for http\n"));
534     fail = GNUNET_YES;
535     return;
536   }
537
538   ti_timeout = GNUNET_SCHEDULER_add_now (sched, &task_timeout, NULL);
539   //ti_download = GNUNET_SCHEDULER_add_now (sched, &task_download, NULL);
540
541   return;
542
543 }
544
545
546 /**
547  * The main function for the transport service.
548  *
549  * @param argc number of arguments from the command line
550  * @param argv command line arguments
551  * @return 0 ok, 1 on error
552  */
553 int
554 main (int argc, char *const *argv)
555 {
556
557   static struct GNUNET_GETOPT_CommandLineOption options[] = {
558     GNUNET_GETOPT_OPTION_END
559   };
560   int ret;
561   char *const argv_prog[] = {
562     "test_plugin_transport_http",
563     "-c",
564     "test_plugin_transport_data_http.conf",
565     "-L",
566 #if VERBOSE
567     "DEBUG",
568 #else
569     "WARNING",
570 #endif
571     NULL
572   };
573   GNUNET_log_setup ("test_plugin_transport_http",
574 #if VERBOSE
575                     "DEBUG",
576 #else
577                     "WARNING",
578 #endif
579                     NULL);
580 /*  GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Starting statistics service\n");
581   pid = GNUNET_OS_start_process (NULL, NULL, "gnunet-service-statistics",
582                                  "gnunet-service-statistics",
583                                  "-L", "DEBUG",
584                                  "-c", "test_plugin_transport_data_http.conf", NULL);
585
586   fail = GNUNET_NO;
587   if (pid==-1 )
588   {
589     GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Failed to start statistics service\n");
590     fail = GNUNET_YES;
591     return fail;
592   }*/
593
594   ret = (GNUNET_OK ==
595          GNUNET_PROGRAM_run (5,
596                              argv_prog,
597                              "test_plugin_transport_http",
598                              "testcase", options, &run, NULL)) ? GNUNET_NO : GNUNET_YES;
599
600     GNUNET_DISK_directory_remove ("/tmp/test_plugin_transport_http");
601
602
603 /*  if (0 != PLIBC_KILL (pid, SIGTERM))
604   {
605     GNUNET_log_strerror (GNUNET_ERROR_TYPE_DEBUG, "Failed to kill statistics service");
606     fail = GNUNET_YES;
607   }
608   if (GNUNET_OS_process_wait(pid) != GNUNET_OK)
609     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "waitpid");
610   GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Killed statistics service\n");*/
611   return fail;
612 }
613
614 /* end of test_plugin_transport_http.c */