2 This file is part of GNUnet.
3 (C) 2011, 2012 Christian Grothoff (and other contributing authors)
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.
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.
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.
22 * @file stream/test_stream_2peers_halfclose.c
23 * @brief Testcases for Stream API halfclosed connections between 2 peers
24 * @author Sree Harsha Totakura
30 #include "gnunet_util_lib.h"
31 #include "gnunet_mesh_service.h"
32 #include "gnunet_stream_lib.h"
33 #include "gnunet_testing_lib.h"
34 #include "gnunet_scheduler_lib.h"
43 #define TIME_REL_SECS(sec) \
44 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, sec)
47 * Structure for holding peer's sockets and IO Handles
52 * Peer's stream socket
54 struct GNUNET_STREAM_Socket *socket;
57 * Peer's io write handle
59 struct GNUNET_STREAM_IOWriteHandle *io_write_handle;
62 * Peer's io read handle
64 struct GNUNET_STREAM_IOReadHandle *io_read_handle;
67 * Peer's shutdown handle
69 struct GNUNET_STREAM_ShutdownHandle *shutdown_handle;
74 struct GNUNET_PeerIdentity our_id;
77 * Bytes the peer has written
79 unsigned int bytes_wrote;
82 * Byte the peer has read
84 unsigned int bytes_read;
87 * GNUNET_YES if the peer has successfully completed the current test
92 * The shutdown operation that has to be used by the stream_shutdown_task
94 int shutdown_operation;
98 * The current peer group
100 static struct GNUNET_TESTING_PeerGroup *pg;
105 static struct GNUNET_TESTING_Daemon *d1;
110 static struct GNUNET_TESTING_Daemon *d2;
114 * Peer1 writes first and then calls for SHUT_WR
115 * Peer2 reads first and then calls for SHUT_RD
116 * Attempt to write again by Peer1 should be rejected
117 * Attempt to read again by Peer2 should be rejected
118 * Peer1 then reads from Peer2 which writes
120 static struct PeerData peer1;
121 static struct PeerData peer2;
122 static struct GNUNET_STREAM_ListenSocket *peer2_listen_socket;
123 static struct GNUNET_CONFIGURATION_Handle *config;
125 static GNUNET_SCHEDULER_TaskIdentifier abort_task;
126 static GNUNET_SCHEDULER_TaskIdentifier read_task;
128 static char *data = "ABCD";
132 * Enumeration for various tests that are to be passed in the same order as
138 * Peer1 writing; Peer2 reading
143 * Peer1 write shutdown; Peer2 should get an error when it tries to read;
145 PEER1_WRITE_SHUTDOWN,
148 * Peer1 reads; Peer2 writes (connection is halfclosed)
150 PEER1_HALFCLOSE_READ,
153 * Peer1 attempts to write; Should fail with stream already shutdown error
155 PEER1_HALFCLOSE_WRITE_FAIL,
158 * Peer1 read shutdown; Peer2 should get stream shutdown error during write
163 * All tests successfully finished
169 * Current running test
171 enum Test current_test;
176 * @param cls the closure from GNUNET_STREAM_write/read
177 * @param status the status of the stream at the time this function is called
178 * @param data traffic from the other side
179 * @param size the number of bytes available in data read
180 * @return number of bytes of processed from 'data' (any data remaining should be
181 * given to the next time the read processor is called).
184 input_processor (void *cls,
185 enum GNUNET_STREAM_Status status,
186 const void *input_data,
191 * The transition function; responsible for the transitions among tests
198 * Task for calling STREAM_read
200 * @param cls the peer data entity
201 * @param tc the task context
204 stream_read_task (void *cls,
205 const struct GNUNET_SCHEDULER_TaskContext *tc)
207 struct PeerData *peer = cls;
209 peer->io_read_handle = GNUNET_STREAM_read (peer->socket,
210 GNUNET_TIME_relative_multiply
211 (GNUNET_TIME_UNIT_SECONDS, 5),
214 switch (current_test)
216 case PEER1_WRITE_SHUTDOWN:
217 GNUNET_assert (&peer2 == peer);
218 GNUNET_assert (NULL == peer->io_read_handle);
219 transition (); /* to PEER1_HALFCLOSE_READ */
222 GNUNET_assert (NULL != peer->io_read_handle);
228 * The write completion function; called upon writing some data to stream or
231 * @param cls the closure from GNUNET_STREAM_write/read
232 * @param status the status of the stream at the time this function is called
233 * @param size the number of bytes read or written
236 write_completion (void *cls,
237 enum GNUNET_STREAM_Status status,
242 * Task for calling STREAM_write
244 * @param cls the peer data entity
245 * @param tc the task context
248 stream_write_task (void *cls,
249 const struct GNUNET_SCHEDULER_TaskContext *tc)
251 struct PeerData *peer = cls;
253 peer->io_write_handle =
254 GNUNET_STREAM_write (peer->socket,
256 strlen(data) - peer->bytes_wrote,
257 GNUNET_TIME_relative_multiply
258 (GNUNET_TIME_UNIT_SECONDS, 5),
261 switch (current_test)
263 case PEER1_HALFCLOSE_WRITE_FAIL:
264 GNUNET_assert (&peer1 == peer);
265 GNUNET_assert (NULL == peer->io_write_handle);
266 transition(); /* To PEER1_READ_SHUTDOWN */
268 case PEER1_READ_SHUTDOWN:
269 GNUNET_assert (&peer2 == peer);
270 GNUNET_assert (NULL == peer->io_write_handle);
271 transition (); /* To SUCCESS */
274 GNUNET_assert (NULL != peer->io_write_handle);
280 * Check whether peers successfully shut down.
283 peergroup_shutdown_callback (void *cls, const char *emsg)
287 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
288 "Shutdown of peers failed!\n");
292 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
293 "All peers successfully shut down!\n");
295 GNUNET_CONFIGURATION_destroy (config);
300 * Close sockets and stop testing deamons nicely
303 do_close (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
305 if (NULL != peer1.socket)
306 GNUNET_STREAM_close (peer1.socket);
307 if (NULL != peer2.socket)
308 GNUNET_STREAM_close (peer2.socket);
309 if (NULL != peer2_listen_socket)
310 GNUNET_STREAM_listen_close (peer2_listen_socket);
312 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: shutdown\n");
315 GNUNET_SCHEDULER_cancel (abort_task);
318 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Wait\n");
320 GNUNET_TESTING_daemons_stop (pg,
321 GNUNET_TIME_relative_multiply
322 (GNUNET_TIME_UNIT_SECONDS, 5),
323 &peergroup_shutdown_callback,
329 * Completion callback for shutdown
331 * @param cls the closure from GNUNET_STREAM_shutdown call
332 * @param operation the operation that was shutdown (SHUT_RD, SHUT_WR,
336 shutdown_completion (void *cls,
339 switch (current_test)
343 case PEER1_WRITE_SHUTDOWN:
344 GNUNET_assert (cls == &peer1);
345 GNUNET_assert (SHUT_WR == operation);
346 peer1.test_ok = GNUNET_YES;
347 /* Peer2 should read with error */
348 peer2.bytes_read = 0;
349 GNUNET_SCHEDULER_add_now (&stream_read_task, &peer2);
351 case PEER1_READ_SHUTDOWN:
352 peer1.test_ok = GNUNET_YES;
353 peer2.bytes_wrote = 0;
354 GNUNET_SCHEDULER_add_now (&stream_write_task, &peer2);
356 case PEER1_HALFCLOSE_READ:
357 case PEER1_HALFCLOSE_WRITE_FAIL:
359 GNUNET_assert (0); /* We shouldn't reach here */
365 * Task for calling STREAM_shutdown
367 * @param cls the peer entity
368 * @param tc the TaskContext
371 stream_shutdown_task (void *cls,
372 const struct GNUNET_SCHEDULER_TaskContext *tc)
374 struct PeerData *peer = cls;
376 peer->shutdown_handle = GNUNET_STREAM_shutdown (peer->socket,
377 peer->shutdown_operation,
378 &shutdown_completion,
380 GNUNET_assert (NULL != peer->shutdown_handle);
385 * Something went wrong and timed out. Kill everything and set error flag
388 do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
390 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: ABORT\n");
393 GNUNET_SCHEDULER_cancel (read_task);
395 result = GNUNET_SYSERR;
402 * The transition function; responsible for the transitions among tests
407 if ((GNUNET_YES == peer1.test_ok) && (GNUNET_YES == peer2.test_ok))
409 peer1.test_ok = GNUNET_NO;
410 peer2.test_ok = GNUNET_NO;
411 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
412 "TEST %d SUCCESSFULL\n", current_test);
413 switch (current_test)
416 current_test = PEER1_WRITE_SHUTDOWN;
417 /* Peer1 should shutdown writing */
418 peer1.shutdown_operation = SHUT_WR;
419 GNUNET_SCHEDULER_add_now (&stream_shutdown_task, &peer1);
421 case PEER1_WRITE_SHUTDOWN:
422 current_test = PEER1_HALFCLOSE_READ;
423 /* Peer2 should be able to write successfully */
424 peer2.bytes_wrote = 0;
425 GNUNET_SCHEDULER_add_now (&stream_write_task, &peer2);
427 /* Peer1 should be able to read successfully */
428 peer1.bytes_read = 0;
429 GNUNET_SCHEDULER_add_now (&stream_read_task, &peer1);
431 case PEER1_HALFCLOSE_READ:
432 current_test = PEER1_HALFCLOSE_WRITE_FAIL;
433 peer1.bytes_wrote = 0;
434 peer2.bytes_read = 0;
435 peer2.test_ok = GNUNET_YES;
436 GNUNET_SCHEDULER_add_now (&stream_write_task, &peer1);
438 case PEER1_HALFCLOSE_WRITE_FAIL:
439 current_test = PEER1_READ_SHUTDOWN;
440 peer1.shutdown_operation = SHUT_RD;
441 GNUNET_SCHEDULER_add_now (&stream_shutdown_task, &peer1);
443 case PEER1_READ_SHUTDOWN:
444 current_test = SUCCESS;
445 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
446 "All tests successful\n");
447 GNUNET_SCHEDULER_add_now (&do_close, NULL);
450 GNUNET_assert (0); /* We shouldn't reach here */
457 * The write completion function; called upon writing some data to stream or
460 * @param cls the closure from GNUNET_STREAM_write/read
461 * @param status the status of the stream at the time this function is called
462 * @param size the number of bytes read or written
465 write_completion (void *cls,
466 enum GNUNET_STREAM_Status status,
469 struct PeerData *peer = cls;
471 switch (current_test)
474 case PEER1_HALFCLOSE_READ:
476 GNUNET_assert (GNUNET_STREAM_OK == status);
477 GNUNET_assert (size <= strlen (data));
478 peer->bytes_wrote += size;
480 if (peer->bytes_wrote < strlen(data)) /* Have more data to send */
482 GNUNET_SCHEDULER_add_now (&stream_write_task, peer);
486 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
487 "Writing completed\n");
491 peer1.test_ok = GNUNET_YES;
492 transition (); /* to PEER1_WRITE_SHUTDOWN */
494 else /* This will happen during PEER1_HALFCLOSE_READ */
496 peer2.test_ok = GNUNET_YES;
497 transition (); /* to PEER1_HALFCLOSE_WRITE_FAIL */
501 case PEER1_HALFCLOSE_WRITE_FAIL:
502 GNUNET_assert (peer == &peer1);
503 GNUNET_assert (GNUNET_STREAM_SHUTDOWN == status);
504 GNUNET_assert (0 == size);
505 peer1.test_ok = GNUNET_YES;
507 case PEER1_READ_SHUTDOWN:
508 GNUNET_assert (peer == &peer2);
509 GNUNET_assert (GNUNET_STREAM_SHUTDOWN == status);
510 GNUNET_assert (0 == size);
511 peer2.test_ok = GNUNET_YES;
513 case PEER1_WRITE_SHUTDOWN:
515 GNUNET_assert (0); /* We shouldn't reach here */
521 * Function executed after stream has been established
523 * @param cls the closure from GNUNET_STREAM_open
524 * @param socket socket to use to communicate with the other side (read/write)
527 stream_open_cb (void *cls,
528 struct GNUNET_STREAM_Socket *socket)
530 struct PeerData *peer;
532 GNUNET_assert (socket == peer1.socket);
533 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
534 "%s: Stream established from peer1\n",
535 GNUNET_i2s (&peer1.our_id));
536 peer = (struct PeerData *) cls;
537 peer->bytes_wrote = 0;
538 GNUNET_assert (socket == peer1.socket);
539 GNUNET_assert (socket == peer->socket);
540 peer1.test_ok = GNUNET_NO;
541 peer2.test_ok = GNUNET_NO;
542 current_test = PEER1_WRITE;
543 GNUNET_SCHEDULER_add_now (&stream_write_task, peer);
550 * @param cls the closure from GNUNET_STREAM_write/read
551 * @param status the status of the stream at the time this function is called
552 * @param data traffic from the other side
553 * @param size the number of bytes available in data read
554 * @return number of bytes of processed from 'data' (any data remaining should be
555 * given to the next time the read processor is called).
558 input_processor (void *cls,
559 enum GNUNET_STREAM_Status status,
560 const void *input_data,
563 struct PeerData *peer;
565 peer = (struct PeerData *) cls;
567 switch (current_test)
570 case PEER1_HALFCLOSE_READ:
571 if (GNUNET_STREAM_TIMEOUT == status)
573 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
574 "Read operation timedout - reading again!\n");
575 GNUNET_assert (0 == size);
576 GNUNET_SCHEDULER_add_now (&stream_read_task, peer);
580 GNUNET_assert (GNUNET_STREAM_OK == status);
581 GNUNET_assert (size <= strlen (data));
582 GNUNET_assert (0 == strncmp ((const char *) data + peer->bytes_read,
583 (const char *) input_data,
585 peer->bytes_read += size;
587 if (peer->bytes_read < strlen (data))
589 GNUNET_SCHEDULER_add_now (&stream_read_task, peer);
593 if (&peer2 == peer) /* Peer2 has completed reading; should write */
595 peer2.test_ok = GNUNET_YES;
596 transition (); /* Transition to PEER1_WRITE_SHUTDOWN */
598 else /* Peer1 has completed reading. End of tests */
600 peer1.test_ok = GNUNET_YES;
601 transition (); /* to PEER1_HALFCLOSE_WRITE_FAIL */
605 case PEER1_WRITE_SHUTDOWN:
606 GNUNET_assert (GNUNET_STREAM_SHUTDOWN == status);
607 peer2.test_ok = GNUNET_YES;
609 case PEER1_HALFCLOSE_WRITE_FAIL:
610 case PEER1_READ_SHUTDOWN:
612 GNUNET_assert (0); /* We shouldn't reach here */
620 * Scheduler call back; to be executed when a new stream is connected
621 * Called from listen connect for peer2
624 stream_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
626 read_task = GNUNET_SCHEDULER_NO_TASK;
627 GNUNET_assert (NULL != cls);
628 peer2.bytes_read = 0;
629 GNUNET_SCHEDULER_add_now (&stream_read_task, &peer2);
634 * Functions of this type are called upon new stream connection from other peers
636 * @param cls the closure from GNUNET_STREAM_listen
637 * @param socket the socket representing the stream
638 * @param initiator the identity of the peer who wants to establish a stream
640 * @return GNUNET_OK to keep the socket open, GNUNET_SYSERR to close the
641 * stream (the socket will be invalid after the call)
644 stream_listen_cb (void *cls,
645 struct GNUNET_STREAM_Socket *socket,
646 const struct GNUNET_PeerIdentity *initiator)
648 GNUNET_assert (NULL != socket);
649 GNUNET_assert (NULL != initiator);
650 GNUNET_assert (socket != peer1.socket);
652 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
653 "%s: Peer connected: %s\n",
654 GNUNET_i2s (&peer2.our_id),
655 GNUNET_i2s(initiator));
657 peer2.socket = socket;
658 /* FIXME: reading should be done right now instead of a scheduled call */
659 read_task = GNUNET_SCHEDULER_add_now (&stream_read, (void *) socket);
665 * Listen success callback; connects a peer to stream as client
668 stream_connect (void)
670 /* Connect to stream library */
671 peer1.socket = GNUNET_STREAM_open (d1->cfg,
672 &d2->id, /* Null for local peer? */
676 GNUNET_STREAM_OPTION_END);
677 GNUNET_assert (NULL != peer1.socket);
682 * Callback to be called when testing peer group is ready
685 * @param emsg NULL on success
688 peergroup_ready (void *cls, const char *emsg)
692 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
693 "Starting peer group failed: %s\n", emsg);
696 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
697 "Peer group is now ready\n");
699 GNUNET_assert (2 == GNUNET_TESTING_daemons_running (pg));
701 d1 = GNUNET_TESTING_daemon_get (pg, 0);
702 GNUNET_assert (NULL != d1);
704 d2 = GNUNET_TESTING_daemon_get (pg, 1);
705 GNUNET_assert (NULL != d2);
707 GNUNET_TESTING_get_peer_identity (d1->cfg,
709 GNUNET_TESTING_get_peer_identity (d2->cfg,
711 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
713 GNUNET_i2s (&peer1.our_id),
714 GNUNET_i2s (&d1->id));
715 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
717 GNUNET_i2s (&peer2.our_id),
718 GNUNET_i2s (&d2->id));
720 peer2_listen_socket =
721 GNUNET_STREAM_listen (d2->cfg,
725 GNUNET_STREAM_OPTION_SIGNAL_LISTEN_SUCCESS,
727 GNUNET_STREAM_OPTION_END);
728 GNUNET_assert (NULL != peer2_listen_socket);
733 * Initialize framework and start test
736 run (void *cls, char *const *args, const char *cfgfile,
737 const struct GNUNET_CONFIGURATION_Handle *cfg)
739 struct GNUNET_TESTING_Host *hosts; /* FIXME: free hosts (DLL) */
741 /* GNUNET_log_setup ("test_stream_local", */
745 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
747 /* Duplicate the configuration */
748 config = GNUNET_CONFIGURATION_dup (cfg);
750 hosts = GNUNET_TESTING_hosts_load (config);
752 pg = GNUNET_TESTING_peergroup_start (config,
754 GNUNET_TIME_relative_multiply
755 (GNUNET_TIME_UNIT_SECONDS, 3),
760 GNUNET_assert (NULL != pg);
763 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
764 (GNUNET_TIME_UNIT_SECONDS, 40), &do_abort,
771 int main (int argc, char **argv)
775 char *argv2[] = { "test-stream-2peers-halfclose",
777 "-c", "test_stream_local.conf",
780 struct GNUNET_GETOPT_CommandLineOption options[] = {
781 GNUNET_GETOPT_OPTION_END
785 GNUNET_PROGRAM_run ((sizeof (argv2) / sizeof (char *)) - 1, argv2,
786 "test-stream-2peers-halfclose", "nohelp", options, &run, NULL);
788 if (GNUNET_OK != ret)
790 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "run failed with error code %d\n",
794 if (GNUNET_SYSERR == result)
796 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "test failed\n");
799 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "test ok\n");