libt2n: (tomj) fix mysterious crashes in test::ReentrantServer: boost serialization...
[libt2n] / test / timeout.cpp
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
40 using namespace std;
41 using namespace libt2n;
42 using namespace CppUnit;
43
44 string 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
53 class 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
82 class 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
112 BOOST_CLASS_EXPORT(testfunc2_cmd)
113 BOOST_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!!!
116 class real_write_connection: public socket_server_connection
117 {
118     public:
119         void real_write(const std::string& data)
120             { socket_write(data); }
121 };
122
123 // this is an evil hack to get access to real_write, don't ever do this in an app!!!
124 class 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
131 class test_timeout : public TestFixture
132 {
133     CPPUNIT_TEST_SUITE(test_timeout);
134
135     CPPUNIT_TEST(ConnectTimeout);
136     CPPUNIT_TEST(HelloTimeoutNothing);
137     CPPUNIT_TEST(HelloTimeoutSlowData);
138     CPPUNIT_TEST(CommandTimeout);
139     CPPUNIT_TEST(CommandSlowResponse);
140     CPPUNIT_TEST(DisconnectOnWrite);
141     CPPUNIT_TEST(WriteTwice);
142     CPPUNIT_TEST(DisconnectOnRead);
143     CPPUNIT_TEST(BreakAccept);
144
145     CPPUNIT_TEST_SUITE_END();
146
147     pid_t child_pid;
148
149     public:
150
151     typedef uint32_t packet_size_indicator;
152
153     void setUp()
154     { }
155
156     void tearDown()
157     {
158         // make sure the server-child is dead before the next test runs
159         kill(child_pid,SIGKILL);
160         sleep(1);
161     }
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
186     void ConnectTimeout()
187     {
188         switch(child_pid=fork())
189         {
190             case -1:
191             {
192                 CPPUNIT_FAIL("fork error");
193                 break;
194             }
195             case 0:
196             // child
197             {
198                 try
199                 {
200                     socket_server ss("./socket");
201                 } catch(...)
202                 {
203                     std::cerr << "exception in child. ignoring\n";
204                 }
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
220                 socket_client_connection sc("./socket");
221
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());
225             }
226         }
227     }
228
229     void HelloTimeoutNothing()
230     {
231         switch(child_pid=fork())
232         {
233             case -1:
234             {
235                 CPPUNIT_FAIL("fork error");
236                 break;
237             }
238             case 0:
239             // child
240             {
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                 }
252
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");
265                 command_client cc(&sc,1000000,1000000);
266
267                 t2n_exception* ep=cc.get_constuctor_exception();
268
269                 string errormsg;
270                 if (ep)
271                     errormsg=ep->what();
272
273                 CPPUNIT_ASSERT_EQUAL(string("timeout exceeded"),errormsg);
274             }
275         }
276     }
277
278     void HelloTimeoutSlowData()
279     {
280         switch(child_pid=fork())
281         {
282             case -1:
283             {
284                 CPPUNIT_FAIL("fork error");
285                 break;
286             }
287             case 0:
288             // child
289             {
290                 try
291                 {
292                     socket_server ss("./socket");
293
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 << ';';
300
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));
304
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                 }
314
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");
327                 command_client cc(&sc,1000000,1000000);
328
329                 t2n_exception* ep=cc.get_constuctor_exception();
330
331                 string errormsg;
332                 if (ep)
333                     errormsg=ep->what();
334
335                 CPPUNIT_ASSERT_EQUAL(string("timeout exceeded"),errormsg);
336             }
337         }
338     }
339
340     void CommandTimeout()
341     {
342         switch(child_pid=fork())
343         {
344             case -1:
345             {
346                 CPPUNIT_FAIL("fork error");
347                 break;
348             }
349             case 0:
350             // child
351             {
352                 try
353                 {
354                     socket_server ss("./socket");
355
356                     ostringstream hello;
357                     hello << "T2Nv" << PROTOCOL_VERSION << ';';
358                     int byteordercheck=1;
359                     hello.write((char*)&byteordercheck,sizeof(byteordercheck));
360                     hello << ';';
361
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                 }
371
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
385                 command_client cc(&sc,1000000,1000000);
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     {
406         switch(child_pid=fork())
407         {
408             case -1:
409             {
410                 CPPUNIT_FAIL("fork error");
411                 break;
412             }
413             case 0:
414             // child
415             {
416                 try
417                 {
418                     socket_server ss("./socket");
419
420                     ostringstream hello;
421                     hello << "T2Nv" << PROTOCOL_VERSION << ';';
422                     int byteordercheck=1;
423                     hello.write((char*)&byteordercheck,sizeof(byteordercheck));
424                     hello << ';';
425
426                     ss.add_callback(new_connection,bind(&test_timeout::send_hello, boost::ref(*this), hello.str(),&ss, _1));
427
428                     // max 10 sec
429                     for (int i=0; i < 10; i++)
430                     {
431                         ss.fill_buffer(1000000);
432
433                         string data;
434                         unsigned int cid;
435
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                         }
445                     }
446                 } catch(...)
447                 {
448                     std::cerr << "exception in child. ignoring\n";
449                 }
450
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
464                 command_client cc(&sc,1000000,1000000);
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
483     void DisconnectOnWrite()
484     {
485         switch(child_pid=fork())
486         {
487             case -1:
488             {
489                 CPPUNIT_FAIL("fork error");
490                 break;
491             }
492             case 0:
493             // child
494             {
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                 }
505
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
524                 string huge(5000000,'x');
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
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             {
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                 }
563
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
601     void DisconnectOnRead()
602     {
603         pid_t pid2;
604
605         switch(child_pid=fork())
606         {
607             case -1:
608             {
609                 CPPUNIT_FAIL("fork error");
610                 break;
611             }
612             case 0:
613             // child
614             {
615                 try
616                 {
617                     // wait till server is up
618                     sleep(1);
619
620                     socket_client_connection sc("./socket");
621
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                 }
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
655                 switch(pid2=fork())
656                 {
657                     case -1:
658                     {
659                         CPPUNIT_FAIL("fork error");
660                         break;
661                     }
662                     case 0:
663                     // child
664                     {
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                         }
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         }
701         kill(pid2,SIGKILL);
702     }
703
704     void BreakAccept()
705     {
706         pid_t pid2;
707
708         switch(child_pid=fork())
709         {
710             case -1:
711             {
712                 CPPUNIT_FAIL("fork error");
713                 break;
714             }
715             case 0:
716             // child
717             {
718                 try
719                 {
720                     // wait till server is really up and waiting
721                     sleep(2);
722
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                 }
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
758                 switch(pid2=fork())
759                 {
760                     case -1:
761                     {
762                         CPPUNIT_FAIL("fork error");
763                         break;
764                     }
765                     case 0:
766                     // child
767                     {
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                         }
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         }
804         kill(pid2,SIGKILL);
805     }
806 };
807
808 CPPUNIT_TEST_SUITE_REGISTRATION(test_timeout);