9410d7df38835856e3fc793555b8181bbaf4ec0d
[oweals/gnunet.git] / src / stream / test_stream_local.c
1 /*
2      This file is part of GNUnet.
3      (C) 2011, 2012 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 stream/test_stream_local.c
23  * @brief Stream API testing between local peers
24  * @author Sree Harsha Totakura
25  */
26
27 #include <string.h>
28
29 #include "platform.h"
30 #include "gnunet_util_lib.h"
31 #include "gnunet_mesh_service.h"
32 #include "gnunet_stream_lib.h"
33
34 #define VERBOSE 1
35
36 static struct GNUNET_OS_Process *arm_pid;
37 static struct GNUNET_STREAM_Socket *peer1_socket;
38 static struct GNUNET_STREAM_ListenSocket *peer2_listen_socket;
39 static struct GNUNET_STREAM_Socket *peer2_socket;
40
41 static GNUNET_SCHEDULER_TaskIdentifier abort_task;
42 static GNUNET_SCHEDULER_TaskIdentifier test_task;
43 static GNUNET_SCHEDULER_TaskIdentifier read_task;
44
45 static GNUNET_STREAM_IOHandle *peer1_IOHandle;
46 static GNUNET_STREAM_IOHandle *peer2_IOHandle;
47
48 static char *data = "ABCD";
49 static unsigned int *data_pointer;
50 static unsigned int *read_pointer;
51 static int result
52
53 static int test1_success_counter;
54
55
56 /**
57  * Shutdown nicely
58  */
59 static void
60 do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
61 {
62   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: shutdown\n");
63   if (0 != abort_task)
64   {
65     GNUNET_SCHEDULER_cancel (abort_task);
66   }
67   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: arm\n");
68   if (0 != GNUNET_OS_process_kill (arm_pid, SIGTERM))
69   {
70     GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
71   }
72   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Wait\n");
73   GNUNET_assert (GNUNET_OK == GNUNET_OS_process_wait (arm_pid));
74   GNUNET_OS_process_close (arm_pid);
75 }
76
77
78 /**
79  * Something went wrong and timed out. Kill everything and set error flag
80  */
81 static void
82 do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
83 {
84   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: ABORT\n");
85   if (0 != test_task)
86   {
87     GNUNET_SCHEDULER_cancel (test_task);
88   }
89   if (0 != read_task)
90     {
91       GNUNET_SCHEDULER_cancel (read_task);
92     }
93   result = GNUNET_SYSERR;
94   abort_task = 0;
95   do_shutdown (cls, tc);
96 }
97
98
99 /**
100  * The write completion function; called upon writing some data to stream or
101  * upon error
102  *
103  * @param cls the closure from GNUNET_STREAM_write/read
104  * @param status the status of the stream at the time this function is called
105  * @param size the number of bytes read or written
106  */
107 void write_completion (void *cls,
108                        enum GNUNET_STREAM_Status status,
109                        size_t size)
110 {
111   GNUNET_assert (GNUNET_STREAM_OK == status);
112   if (data_pointer + size != strlen(data)) /* Have more data to send */
113     {
114       data_pointer += size;
115       peer1_IOHandle = GNUNET_STREAM_write (peer1_socket,
116                                             (void *) data_pointer,
117                                             strlen(data) - data_pointer,
118                                             GNUNET_TIME_relative_multiply
119                                             (GNUNET_TIME_UNIT_SECONDS, 5),
120                                             &write_completion,
121                                             NULL);
122       GNUNET_assert (NULL != peer1_IOHandle);
123     }
124   else{                         /* Close peer1 socket */
125     GNUNET_STREAM_close (peer1_socket);
126     test1_success_counter++;
127     if (2 == test1_success_counter)
128       {
129         GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
130       }
131   }
132 }
133
134
135 /**
136  * Function executed after stream has been established
137  *
138  * @param cls the closure from GNUNET_STREAM_open
139  * @param socket socket to use to communicate with the other side (read/write)
140  */
141 static void 
142 stream_open_cb (void *cls,
143                 struct GNUNET_STREAM_Socket
144                 *socket)
145 {
146   data_pointer = 0;
147   GNUNET_assert (socket == peer1_socket);
148   peer1_IOHandle = GNUNET_STREAM_write (socket, /* socket */
149                                         (void *) data, /* data */
150                                         strlen(data),
151                                         GNUNET_TIME_relative_multiply
152                                         (GNUNET_TIME_UNIT_SECONDS, 5),
153                                         &write_completion,
154                                         NULL);
155   GNUNET_assert (NULL != peer1_IOHandle);     
156   
157 }
158
159
160 /**
161  * Input processor
162  *
163  * @param cls the closure from GNUNET_STREAM_write/read
164  * @param status the status of the stream at the time this function is called
165  * @param data traffic from the other side
166  * @param size the number of bytes available in data read 
167  * @return number of bytes of processed from 'data' (any data remaining should be
168  *         given to the next time the read processor is called).
169  */
170 static size_t
171 input_processor (void *cls,
172                  enum GNUNET_STREAM_Status status,
173                  const void *input_data,
174                  size_t size)
175 {
176   GNUNET_assert (size < strlen (data));
177   GNUNET_assert (strncmp ((const char *) data, 
178                           (const char *) input_data,
179                           size));
180   read_pointer += size;
181
182   if (read_pointer < strlen (data))
183     {
184       peer2_IOHandle = GNUNET_STREAM_read ((struct GNUNET_STREAM_Socket) cls,
185                                        GNUNET_TIME_relative_multiply
186                                        (GNUNET_TIME_UNIT_SECONDS, 5),
187                                        &input_processor,
188                                        NULL);
189       GNUNET_assert (NULL != peer2_IOHandle);
190     }
191   else {                        /* Close peer2 socket */
192     GNUNET_STREAM_close (peer2_socket);
193     test1_success_counter ++;
194     if (2 == test1_success_counter)
195       {
196         GNUNET_SCHEDULER_add_now (&do_shutdown, NULL);
197       }
198   }
199         
200   return size;
201 }
202
203
204 /**
205  * Scheduler call back; to be executed when a new stream is connected
206  */
207 static void
208 stream_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
209 {
210   read_task = GNUNET_SCHEDULER_NO_TASK;
211   GNUNET_assert (NULL != cls);
212   read_pointer = 0;
213   GNUNET_STREAM_listen_close (peer2_listen_socket); /* Close listen socket */
214   peer2_IOHandle = GNUNET_STREAM_read ((struct GNUNET_STREAM_Socket) cls,
215                                        GNUNET_TIME_relative_multiply
216                                        (GNUNET_TIME_UNIT_SECONDS, 5),
217                                        &input_processor,
218                                        NULL);
219   GNUNET_assert (NULL != peer2_IOHandle);
220 }
221
222
223 /**
224  * Functions of this type are called upon new stream connection from other peers
225  *
226  * @param cls the closure from GNUNET_STREAM_listen
227  * @param socket the socket representing the stream
228  * @param initiator the identity of the peer who wants to establish a stream
229  *            with us
230  * @return GNUNET_OK to keep the socket open, GNUNET_SYSERR to close the
231  *             stream (the socket will be invalid after the call)
232  */
233 static int
234 stream_listen_cb (void *cls,
235            struct GNUNET_STREAM_Socket *socket,
236            const struct GNUNET_PeerIdentity *initiator)
237 {
238   GNUNET_assert (NULL != socket);
239   GNUNET_assert (NULL == initiator);   /* Local peer=NULL? */
240   GNUNET_assert (socket != peer1_socket);
241
242   peer2_socket = socket;
243   read_task = GNUNET_SCHEDULER_add_now (&stream_read, (void *) socket);
244   return GNUNET_OK;
245 }
246
247
248 /**
249  * Testing function
250  */
251 static void
252 test (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
253 {
254   test_task = GNUNET_SCHEDULER_NO_TASK;
255   test1_success_counter = 0;
256   /* Connect to stream library */
257   peer1_socket = GNUNET_STREAM_open (NULL,         /* Null for local peer? */
258                                      10,           /* App port */
259                                      open_cb);
260   GNUNET_assert (NULL != peer1_socket);
261   peer2_listen_socket = GNUNET_STREAM_listen (10 /* App port */
262                                               &stream_listen_cb,
263                                               NULL);
264   GNUNET_assert (NULL != peer2_listen_socket);
265                   
266 }
267
268 /**
269  * Initialize framework and start test
270  */
271 static void
272 run (void *cls, char *const *args, const char *cfgfile,
273      const struct GNUNET_CONFIGURATION_Handle *cfg)
274 {
275    GNUNET_log_setup ("test_stream_local",
276 #if VERBOSE
277                     "DEBUG",
278 #else
279                     "WARNING",
280 #endif
281                     NULL);
282    arm_pid =
283      GNUNET_OS_start_process (NULL, NULL, "gnunet-service-arm",
284                               "gnunet-service-arm",
285 #if VERBOSE_ARM
286                               "-L", "DEBUG",
287 #endif
288                               "-c", "test_stream_local.conf", NULL);
289
290    abort_task =
291      GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
292                                    (GNUNET_TIME_UNIT_SECONDS, 20), &do_abort,
293                                     NULL);
294
295    test_task = GNUNET_SCHEDULER_add_now (&test, (void *) cfg);
296
297 }
298
299 /**
300  * Main function
301  */
302 int main (int argc, char **argv)
303 {
304   int ret;
305
306   char *const argv2[] = { "test-stream-local",
307                           "-c", "test_stream.conf",
308 #if VERBOSE
309                           "-L", "DEBUG",
310 #endif
311                           NULL
312   };
313   
314   struct GNUNET_GETOPT_CommandLineOption options[] = {
315     GNUNET_GETOPT_OPTION_END
316   };
317   
318   ret =
319       GNUNET_PROGRAM_run ((sizeof (argv2) / sizeof (char *)) - 1, argv2,
320                           "test-stream-local", "nohelp", options, &run, NULL);
321
322   if (GNUNET_OK != ret)
323   {
324     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "run failed with error code %d\n",
325                 ret);
326     return 1;
327   }
328   if (GNUNET_SYSERR == result)
329   {
330     GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "test failed\n");
331     return 1;
332   }
333   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test ok\n");
334   return 0;
335 }