libt2n: (tomj) added exception handling to every child after fork(). This is needed...
[libt2n] / test / timeout.cpp
CommitLineData
45a2ebc9
GE
1/***************************************************************************
2 * Copyright (C) 2004 by Intra2net AG *
3 * info@intra2net.com *
4 * *
5 ***************************************************************************/
6
7#include <sys/types.h>
8#include <unistd.h>
9#include <errno.h>
10#include <signal.h>
11#include <stdio.h>
12
13#include <iostream>
14#include <string>
15#include <sstream>
16#include <stdexcept>
17
18#include <boost/bind.hpp>
19
20#include <cppunit/extensions/TestFactoryRegistry.h>
21#include <cppunit/ui/text/TestRunner.h>
22#include <cppunit/extensions/HelperMacros.h>
23
24#include <boost/archive/binary_oarchive.hpp>
25#include <boost/archive/binary_iarchive.hpp>
26#include <boost/archive/xml_oarchive.hpp>
27#include <boost/archive/xml_iarchive.hpp>
28#include <boost/serialization/serialization.hpp>
29
30#include <container.hxx>
31#include <socket_client.hxx>
32#include <socket_server.hxx>
33#include <command_client.hxx>
34#include <command_server.hxx>
35
36#ifdef HAVE_CONFIG_H
37#include <config.h>
38#endif
39
40using namespace std;
41using namespace libt2n;
42using namespace CppUnit;
43
44string testfunc2(const string& str)
45{
46 if (str=="throw")
47 throw libt2n::t2n_runtime_error("throw me around");
48 string ret(str);
49 ret+=", testfunc() was here";
50 return ret;
51}
52
53class testfunc2_res : public libt2n::result
54{
55 private:
56 string res;
57
58 friend class boost::serialization::access;
59 template<class Archive>
60 void serialize(Archive & ar, const unsigned int version)
61 {
62 ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::result);
63 ar & BOOST_SERIALIZATION_NVP(res);
64 }
65
66 public:
67 testfunc2_res()
68 { }
69
70 testfunc2_res(const string& str)
71 {
72 res=str;
73 }
74
75 string get_data()
76 {
77 return res;
78 }
79};
80
81
82class testfunc2_cmd : public libt2n::command
83{
84 private:
85 string param;
86
87 friend class boost::serialization::access;
88 template<class Archive>
89 void serialize(Archive & ar, const unsigned int version)
90 {
91 ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::command);
92 ar & BOOST_SERIALIZATION_NVP(param);
93 }
94
95 public:
96 testfunc2_cmd()
97 { }
98
99 testfunc2_cmd(const string& str)
100 {
101 param=str;
102 }
103
104 libt2n::result* operator()()
105 {
106 return new testfunc2_res(testfunc2(param));
107 }
108};
109
110#include <boost/serialization/export.hpp>
111
112BOOST_CLASS_EXPORT(testfunc2_cmd)
113BOOST_CLASS_EXPORT(testfunc2_res)
114
115// this is an evil hack to get access to real_write, don't ever do this in an app!!!
116class real_write_connection: public socket_server_connection
117{
118 public:
119 void real_write(const std::string& data)
120 { socket_write(data); }
121};
122
c7857475
GE
123// this is an evil hack to get access to real_write, don't ever do this in an app!!!
124class real_write_client_connection: public socket_client_connection
125{
126 public:
127 void real_write(const std::string& data)
128 { socket_write(data); }
129};
130
45a2ebc9
GE
131class test_timeout : public TestFixture
132{
133 CPPUNIT_TEST_SUITE(test_timeout);
134
c7857475 135 CPPUNIT_TEST(ConnectTimeout);
45a2ebc9
GE
136 CPPUNIT_TEST(HelloTimeoutNothing);
137 CPPUNIT_TEST(HelloTimeoutSlowData);
138 CPPUNIT_TEST(CommandTimeout);
139 CPPUNIT_TEST(CommandSlowResponse);
c7857475 140 CPPUNIT_TEST(DisconnectOnWrite);
039e52da 141 CPPUNIT_TEST(WriteTwice);
c7857475
GE
142 CPPUNIT_TEST(DisconnectOnRead);
143 CPPUNIT_TEST(BreakAccept);
45a2ebc9
GE
144
145 CPPUNIT_TEST_SUITE_END();
146
b5922184
GE
147 pid_t child_pid;
148
45a2ebc9
GE
149 public:
150
151 typedef uint32_t packet_size_indicator;
152
153 void setUp()
154 { }
155
156 void tearDown()
b5922184
GE
157 {
158 // make sure the server-child is dead before the next test runs
159 kill(child_pid,SIGKILL);
160 sleep(1);
161 }
45a2ebc9
GE
162
163 void send_hello(string hello_string, socket_server* ss, unsigned int conn_id)
164 {
165 server_connection *sc=ss->get_connection(conn_id);
166 sc->write(hello_string);
167 }
168
169 void send_slow_raw_socket(string data, socket_server* ss, unsigned int conn_id)
170 {
171 socket_server_connection *ssc=dynamic_cast<socket_server_connection*>(ss->get_connection(conn_id));
172
173 // this is an evil hack to get access to real_write, don't ever do this in an app!!!
174 real_write_connection *rwc=(real_write_connection*)ssc;
175
176 // we write one char each 0.2 sec
177 for (int pos=0; pos < data.size(); pos++)
178 {
179 string onebyte;
180 onebyte+=data[pos];
181 rwc->real_write(onebyte);
182 usleep(200000);
183 }
184 }
185
c7857475
GE
186 void ConnectTimeout()
187 {
b5922184 188 switch(child_pid=fork())
c7857475
GE
189 {
190 case -1:
191 {
192 CPPUNIT_FAIL("fork error");
193 break;
194 }
195 case 0:
196 // child
197 {
441d41fe
TJ
198 try
199 {
200 socket_server ss("./socket");
201 } catch(...)
202 {
203 std::cerr << "exception in child. ignoring\n";
204 }
c7857475
GE
205
206 // don't call atexit and stuff
207 _exit(0);
208 }
209
210 default:
211 // parent
212 {
213 string data;
214
215 // wait till server is up
216 sleep(1);
217
218 string errormsg;
219
fb3345ad 220 socket_client_connection sc("./socket");
c7857475 221
b5922184
GE
222 CPPUNIT_ASSERT_EQUAL_MESSAGE("connection not closed",true,sc.connection::is_closed());
223
224 CPPUNIT_ASSERT_EQUAL_MESSAGE("wrong errormessage",string("no more retries left after connect error"),sc.get_last_error_msg());
c7857475
GE
225 }
226 }
227 }
228
45a2ebc9
GE
229 void HelloTimeoutNothing()
230 {
b5922184 231 switch(child_pid=fork())
45a2ebc9
GE
232 {
233 case -1:
234 {
235 CPPUNIT_FAIL("fork error");
236 break;
237 }
238 case 0:
239 // child
240 {
441d41fe
TJ
241 try
242 {
243 socket_server ss("./socket");
244
245 // max 10 sec
246 for (int i=0; i < 10; i++)
247 ss.fill_buffer(1000000);
248 } catch(...)
249 {
250 std::cerr << "exception in child. ignoring\n";
251 }
45a2ebc9 252
45a2ebc9
GE
253 // don't call atexit and stuff
254 _exit(0);
255 }
256
257 default:
258 // parent
259 {
260 string data;
261
262 // wait till server is up
263 sleep(1);
264 socket_client_connection sc("./socket");
b5922184 265 command_client cc(&sc,1000000,1000000);
45a2ebc9 266
b5922184 267 t2n_exception* ep=cc.get_constuctor_exception();
45a2ebc9 268
b5922184
GE
269 string errormsg;
270 if (ep)
271 errormsg=ep->what();
45a2ebc9
GE
272
273 CPPUNIT_ASSERT_EQUAL(string("timeout exceeded"),errormsg);
274 }
275 }
276 }
277
278 void HelloTimeoutSlowData()
279 {
b5922184 280 switch(child_pid=fork())
45a2ebc9
GE
281 {
282 case -1:
283 {
284 CPPUNIT_FAIL("fork error");
285 break;
286 }
287 case 0:
288 // child
289 {
441d41fe
TJ
290 try
291 {
292 socket_server ss("./socket");
45a2ebc9 293
441d41fe
TJ
294 // create a valid packet
295 ostringstream hello;
296 hello << "T2Nv" << PROTOCOL_VERSION << ';';
297 int byteordercheck=1;
298 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
299 hello << ';';
45a2ebc9 300
441d41fe
TJ
301 packet_size_indicator psize=htonl(hello.str().size());
302 std::string send_data(hello.str());
303 send_data.insert(0,(char*)&psize,sizeof(packet_size_indicator));
45a2ebc9 304
441d41fe
TJ
305 ss.add_callback(new_connection,bind(&test_timeout::send_slow_raw_socket, boost::ref(*this), send_data,&ss, _1));
306
307 // max 10 sec
308 for (int i=0; i < 10; i++)
309 ss.fill_buffer(1000000);
310 } catch(...)
311 {
312 std::cerr << "exception in child. ignoring\n";
313 }
45a2ebc9 314
45a2ebc9
GE
315 // don't call atexit and stuff
316 _exit(0);
317 }
318
319 default:
320 // parent
321 {
322 string data;
323
324 // wait till server is up
325 sleep(1);
326 socket_client_connection sc("./socket");
b5922184 327 command_client cc(&sc,1000000,1000000);
45a2ebc9 328
b5922184 329 t2n_exception* ep=cc.get_constuctor_exception();
45a2ebc9 330
b5922184
GE
331 string errormsg;
332 if (ep)
333 errormsg=ep->what();
45a2ebc9
GE
334
335 CPPUNIT_ASSERT_EQUAL(string("timeout exceeded"),errormsg);
336 }
337 }
338 }
339
340 void CommandTimeout()
341 {
b5922184 342 switch(child_pid=fork())
45a2ebc9
GE
343 {
344 case -1:
345 {
346 CPPUNIT_FAIL("fork error");
347 break;
348 }
349 case 0:
350 // child
351 {
441d41fe
TJ
352 try
353 {
354 socket_server ss("./socket");
45a2ebc9 355
441d41fe
TJ
356 ostringstream hello;
357 hello << "T2Nv" << PROTOCOL_VERSION << ';';
358 int byteordercheck=1;
359 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
360 hello << ';';
45a2ebc9 361
441d41fe
TJ
362 ss.add_callback(new_connection,bind(&test_timeout::send_hello, boost::ref(*this), hello.str(),&ss, _1));
363
364 // max 10 sec
365 for (int i=0; i < 10; i++)
366 ss.fill_buffer(1000000);
367 } catch(...)
368 {
369 std::cerr << "exception in child. ignoring\n";
370 }
45a2ebc9 371
45a2ebc9
GE
372 // don't call atexit and stuff
373 _exit(0);
374 }
375
376 default:
377 // parent
378 {
379 string data;
380
381 // wait till server is up
382 sleep(1);
383 socket_client_connection sc("./socket");
384
fb3345ad 385 command_client cc(&sc,1000000,1000000);
45a2ebc9
GE
386 result_container rc;
387
388 string errormsg;
389
390 try
391 {
392 cc.send_command(new testfunc2_cmd("hello"),rc);
393 }
394 catch(t2n_transfer_error &e)
395 { errormsg=e.what(); }
396 catch(...)
397 { throw; }
398
399 CPPUNIT_ASSERT_EQUAL(string("timeout exceeded"),errormsg);
400 }
401 }
402 }
403
404 void CommandSlowResponse()
405 {
b5922184 406 switch(child_pid=fork())
45a2ebc9
GE
407 {
408 case -1:
409 {
410 CPPUNIT_FAIL("fork error");
411 break;
412 }
413 case 0:
414 // child
415 {
441d41fe
TJ
416 try
417 {
418 socket_server ss("./socket");
45a2ebc9 419
441d41fe
TJ
420 ostringstream hello;
421 hello << "T2Nv" << PROTOCOL_VERSION << ';';
422 int byteordercheck=1;
423 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
424 hello << ';';
45a2ebc9 425
441d41fe 426 ss.add_callback(new_connection,bind(&test_timeout::send_hello, boost::ref(*this), hello.str(),&ss, _1));
45a2ebc9 427
441d41fe
TJ
428 // max 10 sec
429 for (int i=0; i < 10; i++)
430 {
431 ss.fill_buffer(1000000);
45a2ebc9 432
441d41fe
TJ
433 string data;
434 unsigned int cid;
45a2ebc9 435
441d41fe
TJ
436 if(ss.get_packet(data,cid))
437 {
438 // create a valid packet & send
439 string response="abcdefghijklmnopqrstuvwxyz";
440 packet_size_indicator psize=htonl(response.size());
441 std::string send_data(response);
442 send_data.insert(0,(char*)&psize,sizeof(packet_size_indicator));
443 send_slow_raw_socket(send_data,&ss,cid);
444 }
45a2ebc9 445 }
441d41fe
TJ
446 } catch(...)
447 {
448 std::cerr << "exception in child. ignoring\n";
45a2ebc9 449 }
441d41fe 450
45a2ebc9
GE
451 // don't call atexit and stuff
452 _exit(0);
453 }
454
455 default:
456 // parent
457 {
458 string data;
459
460 // wait till server is up
461 sleep(1);
462 socket_client_connection sc("./socket");
463
fb3345ad 464 command_client cc(&sc,1000000,1000000);
45a2ebc9
GE
465 result_container rc;
466
467 string errormsg;
468
469 try
470 {
471 cc.send_command(new testfunc2_cmd("hello"),rc);
472 }
473 catch(t2n_transfer_error &e)
474 { errormsg=e.what(); }
475 catch(...)
476 { throw; }
477
478 CPPUNIT_ASSERT_EQUAL(string("timeout exceeded"),errormsg);
479 }
480 }
481 }
482
c7857475
GE
483 void DisconnectOnWrite()
484 {
b5922184 485 switch(child_pid=fork())
c7857475
GE
486 {
487 case -1:
488 {
489 CPPUNIT_FAIL("fork error");
490 break;
491 }
492 case 0:
493 // child
494 {
441d41fe
TJ
495 try
496 {
497 socket_server ss("./socket");
498
499 // bail out as soon as we get something
500 ss.fill_buffer(-1);
501 } catch(...)
502 {
503 std::cerr << "exception in child. ignoring\n";
504 }
c7857475 505
c7857475
GE
506 // don't call atexit and stuff
507 _exit(0);
508 }
509
510 default:
511 // parent
512 {
513 string data;
514
515 // don't kill us on broken pipe
516 signal(SIGPIPE, SIG_IGN);
517
518 // wait till server is up
519 sleep(1);
520 socket_client_connection sc("./socket");
521
522 string errormsg;
523
e72e07f3 524 string huge(5000000,'x');
c7857475
GE
525
526 try
527 {
528 sc.write(huge);
529 }
530 catch(t2n_transfer_error &e)
531 { errormsg=e.what(); }
532 catch(...)
533 { throw; }
534
535 CPPUNIT_ASSERT_EQUAL(string("write() returned Broken pipe"),errormsg);
536 }
537 }
538 }
539
039e52da
GE
540 void WriteTwice()
541 {
542 switch(child_pid=fork())
543 {
544 case -1:
545 {
546 CPPUNIT_FAIL("fork error");
547 break;
548 }
549 case 0:
550 // child
551 {
441d41fe
TJ
552 try
553 {
554 socket_server ss("./socket");
555
556 // bail out as soon as we get something
557 ss.fill_buffer(-1);
558 ss.fill_buffer(-1);
559 } catch(...)
560 {
561 std::cerr << "exception in child. ignoring\n";
562 }
039e52da 563
039e52da
GE
564 // don't call atexit and stuff
565 _exit(0);
566 }
567
568 default:
569 // parent
570 {
571 string data;
572
573 // don't kill us on broken pipe
574 signal(SIGPIPE, SIG_IGN);
575
576 // wait till server is up
577 sleep(1);
578 socket_client_connection sc("./socket");
579
580 string errormsg;
581
582 sc.write("somedata");
583
584 sleep(1);
585
586 // server should disconnect now
587 try
588 {
589 sc.write("other data");
590 }
591 catch(t2n_transfer_error &e)
592 { errormsg=e.what(); }
593 catch(...)
594 { throw; }
595
596 CPPUNIT_ASSERT_EQUAL(string("write() returned Broken pipe"),errormsg);
597 }
598 }
599 }
600
c7857475
GE
601 void DisconnectOnRead()
602 {
b5922184 603 pid_t pid2;
c7857475 604
b5922184 605 switch(child_pid=fork())
c7857475
GE
606 {
607 case -1:
608 {
609 CPPUNIT_FAIL("fork error");
610 break;
611 }
612 case 0:
613 // child
614 {
441d41fe
TJ
615 try
616 {
617 // wait till server is up
618 sleep(1);
c7857475 619
441d41fe 620 socket_client_connection sc("./socket");
c7857475 621
441d41fe
TJ
622 // this is an evil hack to get access to real_write, don't ever do this in an app!!!
623 real_write_client_connection *rwc=(real_write_client_connection*)&sc;
624 rwc->real_write(string(10000,'x'));
625 } catch(...)
626 {
627 std::cerr << "exception in child. ignoring\n";
628 }
c7857475
GE
629
630 // don't call atexit and stuff
631 _exit(0);
632 }
633
634 default:
635 // parent
636 {
637 // don't kill us on broken pipe
638 signal(SIGPIPE, SIG_IGN);
639
640 socket_server ss("./socket");
641
642 time_t t0 = time(NULL);
643
644 // max 5 sec
645 while (time(NULL) < t0 + 5 )
646 {
647 ss.fill_buffer(1000000);
648
649 string data;
650 ss.get_packet(data);
651 }
652
653 // are we still alive and able to process data?
654
fb3345ad 655 switch(pid2=fork())
c7857475
GE
656 {
657 case -1:
658 {
659 CPPUNIT_FAIL("fork error");
660 break;
661 }
662 case 0:
663 // child
664 {
441d41fe
TJ
665 try
666 {
667 socket_client_connection *sc=new socket_client_connection("./socket");
668 sc->write(string(10000,'x'));
669 // socket is closed regularly
670 delete sc;
671 } catch(...)
672 {
673 std::cerr << "exception in child. ignoring\n";
674 }
c7857475
GE
675
676 // don't run regular cleanup, otherwise cppunit stuff gets called
677 _exit(0);
678 }
679
680 default:
681 // parent
682 {
683 string received;
684
685 t0 = time(NULL);
686
687 // max 10 sec
688 while (time(NULL) < t0 + 10 )
689 {
690 ss.fill_buffer(1000000);
691
692 if (ss.get_packet(received))
693 break;
694 }
695
696 CPPUNIT_ASSERT_EQUAL(string(10000,'x'),received);
697 }
698 }
699 }
700 }
fb3345ad 701 kill(pid2,SIGKILL);
c7857475
GE
702 }
703
704 void BreakAccept()
705 {
b5922184 706 pid_t pid2;
c7857475 707
b5922184 708 switch(child_pid=fork())
c7857475
GE
709 {
710 case -1:
711 {
712 CPPUNIT_FAIL("fork error");
713 break;
714 }
715 case 0:
716 // child
717 {
441d41fe
TJ
718 try
719 {
720 // wait till server is really up and waiting
721 sleep(2);
c7857475 722
441d41fe
TJ
723 // connect with very tight timeout and only 1 retry
724 socket_client_connection sc("./socket",50,1);
725 } catch(...)
726 {
727 std::cerr << "exception in child. ignoring\n";
728 }
c7857475
GE
729
730 // don't call atexit and stuff
731 _exit(0);
732 }
733
734 default:
735 // parent
736 {
737 // don't kill us on broken pipe
738 signal(SIGPIPE, SIG_IGN);
739
740 socket_server ss("./socket");
741
742 // server is "working" while client wants to connect
743 sleep(5);
744
745 time_t t0 = time(NULL);
746
747 // max 5 sec
748 while (time(NULL) < t0 + 5 )
749 {
750 ss.fill_buffer(1000000);
751
752 string data;
753 ss.get_packet(data);
754 }
755
756 // are we still alive and able to process data?
757
fb3345ad 758 switch(pid2=fork())
c7857475
GE
759 {
760 case -1:
761 {
762 CPPUNIT_FAIL("fork error");
763 break;
764 }
765 case 0:
766 // child
767 {
441d41fe
TJ
768 try
769 {
770 socket_client_connection *sc=new socket_client_connection("./socket");
771 sc->write(string(10000,'x'));
772 delete sc;
773 // socket is closed regularly
774 } catch(...)
775 {
776 std::cerr << "exception in child. ignoring\n";
777 }
c7857475
GE
778
779 // don't run regular cleanup, otherwise cppunit stuff gets called
780 _exit(0);
781 }
782
783 default:
784 // parent
785 {
786 string received;
787
788 t0 = time(NULL);
789
790 // max 10 sec
791 while (time(NULL) < t0 + 10 )
792 {
793 ss.fill_buffer(1000000);
794
795 if (ss.get_packet(received))
796 break;
797 }
798
799 CPPUNIT_ASSERT_EQUAL(string(10000,'x'),received);
800 }
801 }
802 }
803 }
fb3345ad 804 kill(pid2,SIGKILL);
c7857475 805 }
45a2ebc9
GE
806};
807
808CPPUNIT_TEST_SUITE_REGISTRATION(test_timeout);