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"
44 * Structure for holding peer's sockets and IO Handles
49 * Peer's stream socket
51 struct GNUNET_STREAM_Socket *socket;
54 * Peer's io write handle
56 struct GNUNET_STREAM_IOWriteHandle *io_write_handle;
59 * Peer's io read handle
61 struct GNUNET_STREAM_IOReadHandle *io_read_handle;
64 * Peer's shutdown handle
66 struct GNUNET_STREAM_ShutdownHandle *shutdown_handle;
71 struct GNUNET_PeerIdentity our_id;
74 * Bytes the peer has written
76 unsigned int bytes_wrote;
79 * Byte the peer has read
81 unsigned int bytes_read;
84 * GNUNET_YES if the peer has successfully completed the current test
89 * The shutdown operation that has to be used by the stream_shutdown_task
91 int shutdown_operation;
95 * The current peer group
97 static struct GNUNET_TESTING_PeerGroup *pg;
102 static struct GNUNET_TESTING_Daemon *d1;
107 static struct GNUNET_TESTING_Daemon *d2;
111 * Peer1 writes first and then calls for SHUT_WR
112 * Peer2 reads first and then calls for SHUT_RD
113 * Attempt to write again by Peer1 should be rejected
114 * Attempt to read again by Peer2 should be rejected
115 * Peer1 then reads from Peer2 which writes
117 static struct PeerData peer1;
118 static struct PeerData peer2;
119 static struct GNUNET_STREAM_ListenSocket *peer2_listen_socket;
120 static struct GNUNET_CONFIGURATION_Handle *config;
122 static GNUNET_SCHEDULER_TaskIdentifier abort_task;
123 static GNUNET_SCHEDULER_TaskIdentifier read_task;
125 static char *data = "ABCD";
129 * Enumeration for various tests that are to be passed in the same order as
135 * Peer1 writing; Peer2 reading
140 * Peer1 write shutdown; Peer2 should get an error when it tries to read;
142 PEER1_WRITE_SHUTDOWN,
145 * Peer1 reads; Peer2 writes (connection is halfclosed)
147 PEER1_HALFCLOSE_READ,
150 * Peer1 attempts to write; Should fail with stream already shutdown error
152 PEER1_HALFCLOSE_WRITE_FAIL,
155 * Peer1 read shutdown; Peer2 should get stream shutdown error during write
160 * All tests successfully finished
166 * Current running test
168 enum Test current_test;
173 * @param cls the closure from GNUNET_STREAM_write/read
174 * @param status the status of the stream at the time this function is called
175 * @param data traffic from the other side
176 * @param size the number of bytes available in data read
177 * @return number of bytes of processed from 'data' (any data remaining should be
178 * given to the next time the read processor is called).
181 input_processor (void *cls,
182 enum GNUNET_STREAM_Status status,
183 const void *input_data,
188 * The transition function; responsible for the transitions among tests
195 * Task for calling STREAM_read
197 * @param cls the peer data entity
198 * @param tc the task context
201 stream_read_task (void *cls,
202 const struct GNUNET_SCHEDULER_TaskContext *tc)
204 struct PeerData *peer = cls;
206 peer->io_read_handle = GNUNET_STREAM_read (peer->socket,
207 GNUNET_TIME_relative_multiply
208 (GNUNET_TIME_UNIT_SECONDS, 5),
211 switch (current_test)
213 case PEER1_WRITE_SHUTDOWN:
214 GNUNET_assert (&peer2 == peer);
215 GNUNET_assert (NULL == peer->io_read_handle);
216 transition (); /* to PEER1_HALFCLOSE_READ */
219 GNUNET_assert (NULL != peer->io_read_handle);
225 * The write completion function; called upon writing some data to stream or
228 * @param cls the closure from GNUNET_STREAM_write/read
229 * @param status the status of the stream at the time this function is called
230 * @param size the number of bytes read or written
233 write_completion (void *cls,
234 enum GNUNET_STREAM_Status status,
239 * Task for calling STREAM_write
241 * @param cls the peer data entity
242 * @param tc the task context
245 stream_write_task (void *cls,
246 const struct GNUNET_SCHEDULER_TaskContext *tc)
248 struct PeerData *peer = cls;
250 peer->io_write_handle =
251 GNUNET_STREAM_write (peer->socket,
253 strlen(data) - peer->bytes_wrote,
254 GNUNET_TIME_relative_multiply
255 (GNUNET_TIME_UNIT_SECONDS, 5),
258 switch (current_test)
260 case PEER1_HALFCLOSE_WRITE_FAIL:
261 GNUNET_assert (&peer1 == peer);
262 GNUNET_assert (NULL == peer->io_write_handle);
263 transition(); /* To PEER1_READ_SHUTDOWN */
265 case PEER1_READ_SHUTDOWN:
266 GNUNET_assert (&peer2 == peer);
267 GNUNET_assert (NULL == peer->io_write_handle);
268 transition (); /* To SUCCESS */
271 GNUNET_assert (NULL != peer->io_write_handle);
277 * Check whether peers successfully shut down.
280 peergroup_shutdown_callback (void *cls, const char *emsg)
284 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
285 "Shutdown of peers failed!\n");
289 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
290 "All peers successfully shut down!\n");
292 GNUNET_CONFIGURATION_destroy (config);
297 * Close sockets and stop testing deamons nicely
300 do_close (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
302 if (NULL != peer1.socket)
303 GNUNET_STREAM_close (peer1.socket);
304 if (NULL != peer2.socket)
305 GNUNET_STREAM_close (peer2.socket);
306 if (NULL != peer2_listen_socket)
307 GNUNET_STREAM_listen_close (peer2_listen_socket);
309 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: shutdown\n");
312 GNUNET_SCHEDULER_cancel (abort_task);
315 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: Wait\n");
317 GNUNET_TESTING_daemons_stop (pg,
318 GNUNET_TIME_relative_multiply
319 (GNUNET_TIME_UNIT_SECONDS, 5),
320 &peergroup_shutdown_callback,
326 * Completion callback for shutdown
328 * @param cls the closure from GNUNET_STREAM_shutdown call
329 * @param operation the operation that was shutdown (SHUT_RD, SHUT_WR,
333 shutdown_completion (void *cls,
336 switch (current_test)
340 case PEER1_WRITE_SHUTDOWN:
341 GNUNET_assert (cls == &peer1);
342 GNUNET_assert (SHUT_WR == operation);
343 peer1.test_ok = GNUNET_YES;
344 /* Peer2 should read with error */
345 peer2.bytes_read = 0;
346 GNUNET_SCHEDULER_add_now (&stream_read_task, &peer2);
348 case PEER1_READ_SHUTDOWN:
349 peer1.test_ok = GNUNET_YES;
350 peer2.bytes_wrote = 0;
351 GNUNET_SCHEDULER_add_now (&stream_write_task, &peer2);
353 case PEER1_HALFCLOSE_READ:
354 case PEER1_HALFCLOSE_WRITE_FAIL:
356 GNUNET_assert (0); /* We shouldn't reach here */
362 * Task for calling STREAM_shutdown
364 * @param cls the peer entity
365 * @param tc the TaskContext
368 stream_shutdown_task (void *cls,
369 const struct GNUNET_SCHEDULER_TaskContext *tc)
371 struct PeerData *peer = cls;
373 peer->shutdown_handle = GNUNET_STREAM_shutdown (peer->socket,
374 peer->shutdown_operation,
375 &shutdown_completion,
377 GNUNET_assert (NULL != peer->shutdown_handle);
382 * Something went wrong and timed out. Kill everything and set error flag
385 do_abort (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
387 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "test: ABORT\n");
390 GNUNET_SCHEDULER_cancel (read_task);
392 result = GNUNET_SYSERR;
399 * The transition function; responsible for the transitions among tests
404 if ((GNUNET_YES == peer1.test_ok) && (GNUNET_YES == peer2.test_ok))
406 peer1.test_ok = GNUNET_NO;
407 peer2.test_ok = GNUNET_NO;
408 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
409 "TEST %d SUCCESSFULL\n", current_test);
410 switch (current_test)
413 current_test = PEER1_WRITE_SHUTDOWN;
414 /* Peer1 should shutdown writing */
415 peer1.shutdown_operation = SHUT_WR;
416 GNUNET_SCHEDULER_add_now (&stream_shutdown_task, &peer1);
418 case PEER1_WRITE_SHUTDOWN:
419 current_test = PEER1_HALFCLOSE_READ;
420 /* Peer2 should be able to write successfully */
421 peer2.bytes_wrote = 0;
422 GNUNET_SCHEDULER_add_now (&stream_write_task, &peer2);
424 /* Peer1 should be able to read successfully */
425 peer1.bytes_read = 0;
426 GNUNET_SCHEDULER_add_now (&stream_read_task, &peer1);
428 case PEER1_HALFCLOSE_READ:
429 current_test = PEER1_HALFCLOSE_WRITE_FAIL;
430 peer1.bytes_wrote = 0;
431 peer2.bytes_read = 0;
432 peer2.test_ok = GNUNET_YES;
433 GNUNET_SCHEDULER_add_now (&stream_write_task, &peer1);
435 case PEER1_HALFCLOSE_WRITE_FAIL:
436 current_test = PEER1_READ_SHUTDOWN;
437 peer1.shutdown_operation = SHUT_RD;
438 GNUNET_SCHEDULER_add_now (&stream_shutdown_task, &peer1);
440 case PEER1_READ_SHUTDOWN:
441 current_test = SUCCESS;
442 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
443 "All tests successful\n");
444 GNUNET_SCHEDULER_add_now (&do_close, NULL);
447 GNUNET_assert (0); /* We shouldn't reach here */
454 * The write completion function; called upon writing some data to stream or
457 * @param cls the closure from GNUNET_STREAM_write/read
458 * @param status the status of the stream at the time this function is called
459 * @param size the number of bytes read or written
462 write_completion (void *cls,
463 enum GNUNET_STREAM_Status status,
466 struct PeerData *peer = cls;
468 switch (current_test)
471 case PEER1_HALFCLOSE_READ:
473 GNUNET_assert (GNUNET_STREAM_OK == status);
474 GNUNET_assert (size <= strlen (data));
475 peer->bytes_wrote += size;
477 if (peer->bytes_wrote < strlen(data)) /* Have more data to send */
479 GNUNET_SCHEDULER_add_now (&stream_write_task, peer);
483 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
484 "Writing completed\n");
488 peer1.test_ok = GNUNET_YES;
489 transition (); /* to PEER1_WRITE_SHUTDOWN */
491 else /* This will happen during PEER1_HALFCLOSE_READ */
493 peer2.test_ok = GNUNET_YES;
494 transition (); /* to PEER1_HALFCLOSE_WRITE_FAIL */
498 case PEER1_HALFCLOSE_WRITE_FAIL:
499 GNUNET_assert (peer == &peer1);
500 GNUNET_assert (GNUNET_STREAM_SHUTDOWN == status);
501 GNUNET_assert (0 == size);
502 peer1.test_ok = GNUNET_YES;
504 case PEER1_READ_SHUTDOWN:
505 GNUNET_assert (peer == &peer2);
506 GNUNET_assert (GNUNET_STREAM_SHUTDOWN == status);
507 GNUNET_assert (0 == size);
508 peer2.test_ok = GNUNET_YES;
510 case PEER1_WRITE_SHUTDOWN:
512 GNUNET_assert (0); /* We shouldn't reach here */
518 * Function executed after stream has been established
520 * @param cls the closure from GNUNET_STREAM_open
521 * @param socket socket to use to communicate with the other side (read/write)
524 stream_open_cb (void *cls,
525 struct GNUNET_STREAM_Socket *socket)
527 struct PeerData *peer;
529 GNUNET_assert (socket == peer1.socket);
530 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
531 "%s: Stream established from peer1\n",
532 GNUNET_i2s (&peer1.our_id));
533 peer = (struct PeerData *) cls;
534 peer->bytes_wrote = 0;
535 GNUNET_assert (socket == peer1.socket);
536 GNUNET_assert (socket == peer->socket);
537 peer1.test_ok = GNUNET_NO;
538 peer2.test_ok = GNUNET_NO;
539 current_test = PEER1_WRITE;
540 GNUNET_SCHEDULER_add_now (&stream_write_task, peer);
547 * @param cls the closure from GNUNET_STREAM_write/read
548 * @param status the status of the stream at the time this function is called
549 * @param data traffic from the other side
550 * @param size the number of bytes available in data read
551 * @return number of bytes of processed from 'data' (any data remaining should be
552 * given to the next time the read processor is called).
555 input_processor (void *cls,
556 enum GNUNET_STREAM_Status status,
557 const void *input_data,
560 struct PeerData *peer;
562 peer = (struct PeerData *) cls;
564 switch (current_test)
567 case PEER1_HALFCLOSE_READ:
568 if (GNUNET_STREAM_TIMEOUT == status)
570 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
571 "Read operation timedout - reading again!\n");
572 GNUNET_assert (0 == size);
573 GNUNET_SCHEDULER_add_now (&stream_read_task, peer);
577 GNUNET_assert (GNUNET_STREAM_OK == status);
578 GNUNET_assert (size <= strlen (data));
579 GNUNET_assert (0 == strncmp ((const char *) data + peer->bytes_read,
580 (const char *) input_data,
582 peer->bytes_read += size;
584 if (peer->bytes_read < strlen (data))
586 GNUNET_SCHEDULER_add_now (&stream_read_task, peer);
590 if (&peer2 == peer) /* Peer2 has completed reading; should write */
592 peer2.test_ok = GNUNET_YES;
593 transition (); /* Transition to PEER1_WRITE_SHUTDOWN */
595 else /* Peer1 has completed reading. End of tests */
597 peer1.test_ok = GNUNET_YES;
598 transition (); /* to PEER1_HALFCLOSE_WRITE_FAIL */
602 case PEER1_WRITE_SHUTDOWN:
603 GNUNET_assert (GNUNET_STREAM_SHUTDOWN == status);
604 peer2.test_ok = GNUNET_YES;
606 case PEER1_HALFCLOSE_WRITE_FAIL:
607 case PEER1_READ_SHUTDOWN:
609 GNUNET_assert (0); /* We shouldn't reach here */
617 * Scheduler call back; to be executed when a new stream is connected
618 * Called from listen connect for peer2
621 stream_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
623 read_task = GNUNET_SCHEDULER_NO_TASK;
624 GNUNET_assert (NULL != cls);
625 peer2.bytes_read = 0;
626 GNUNET_SCHEDULER_add_now (&stream_read_task, &peer2);
631 * Functions of this type are called upon new stream connection from other peers
633 * @param cls the closure from GNUNET_STREAM_listen
634 * @param socket the socket representing the stream
635 * @param initiator the identity of the peer who wants to establish a stream
637 * @return GNUNET_OK to keep the socket open, GNUNET_SYSERR to close the
638 * stream (the socket will be invalid after the call)
641 stream_listen_cb (void *cls,
642 struct GNUNET_STREAM_Socket *socket,
643 const struct GNUNET_PeerIdentity *initiator)
645 GNUNET_assert (NULL != socket);
646 GNUNET_assert (NULL != initiator);
647 GNUNET_assert (socket != peer1.socket);
649 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
650 "%s: Peer connected: %s\n",
651 GNUNET_i2s (&peer2.our_id),
652 GNUNET_i2s(initiator));
654 peer2.socket = socket;
655 /* FIXME: reading should be done right now instead of a scheduled call */
656 read_task = GNUNET_SCHEDULER_add_now (&stream_read, (void *) socket);
662 * Callback to be called when testing peer group is ready
665 * @param emsg NULL on success
668 peergroup_ready (void *cls, const char *emsg)
672 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
673 "Starting peer group failed: %s\n", emsg);
676 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
677 "Peer group is now ready\n");
679 GNUNET_assert (2 == GNUNET_TESTING_daemons_running (pg));
681 d1 = GNUNET_TESTING_daemon_get (pg, 0);
682 GNUNET_assert (NULL != d1);
684 d2 = GNUNET_TESTING_daemon_get (pg, 1);
685 GNUNET_assert (NULL != d2);
687 GNUNET_TESTING_get_peer_identity (d1->cfg,
689 GNUNET_TESTING_get_peer_identity (d2->cfg,
691 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
693 GNUNET_i2s (&peer1.our_id),
694 GNUNET_i2s (&d1->id));
695 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
697 GNUNET_i2s (&peer2.our_id),
698 GNUNET_i2s (&d2->id));
700 peer2_listen_socket = GNUNET_STREAM_listen (d2->cfg,
704 GNUNET_assert (NULL != peer2_listen_socket);
706 /* Connect to stream library */
707 peer1.socket = GNUNET_STREAM_open (d1->cfg,
708 &d2->id, /* Null for local peer? */
712 GNUNET_STREAM_OPTION_END);
713 GNUNET_assert (NULL != peer1.socket);
718 * Initialize framework and start test
721 run (void *cls, char *const *args, const char *cfgfile,
722 const struct GNUNET_CONFIGURATION_Handle *cfg)
724 struct GNUNET_TESTING_Host *hosts; /* FIXME: free hosts (DLL) */
726 /* GNUNET_log_setup ("test_stream_local", */
730 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
732 /* Duplicate the configuration */
733 config = GNUNET_CONFIGURATION_dup (cfg);
735 hosts = GNUNET_TESTING_hosts_load (config);
737 pg = GNUNET_TESTING_peergroup_start (config,
739 GNUNET_TIME_relative_multiply
740 (GNUNET_TIME_UNIT_SECONDS, 3),
745 GNUNET_assert (NULL != pg);
748 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
749 (GNUNET_TIME_UNIT_SECONDS, 40), &do_abort,
756 int main (int argc, char **argv)
760 char *argv2[] = { "test-stream-2peers-halfclose",
762 "-c", "test_stream_local.conf",
765 struct GNUNET_GETOPT_CommandLineOption options[] = {
766 GNUNET_GETOPT_OPTION_END
770 GNUNET_PROGRAM_run ((sizeof (argv2) / sizeof (char *)) - 1, argv2,
771 "test-stream-2peers-halfclose", "nohelp", options, &run, NULL);
773 if (GNUNET_OK != ret)
775 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "run failed with error code %d\n",
779 if (GNUNET_SYSERR == result)
781 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "test failed\n");
784 GNUNET_log (GNUNET_ERROR_TYPE_INFO, "test ok\n");