e194b85bfd17056d9bcf572219af78f6d08d3cc3
[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                 socket_server ss("./socket");
199
200                 // don't call atexit and stuff
201                 _exit(0);
202             }
203
204             default:
205             // parent
206             {
207                 string data;
208
209                 // wait till server is up
210                 sleep(1);
211
212                 string errormsg;
213
214                 socket_client_connection sc("./socket");
215
216                 CPPUNIT_ASSERT_EQUAL_MESSAGE("connection not closed",true,sc.connection::is_closed());
217
218                 CPPUNIT_ASSERT_EQUAL_MESSAGE("wrong errormessage",string("no more retries left after connect error"),sc.get_last_error_msg());
219             }
220         }
221     }
222
223     void HelloTimeoutNothing()
224     {
225         switch(child_pid=fork())
226         {
227             case -1:
228             {
229                 CPPUNIT_FAIL("fork error");
230                 break;
231             }
232             case 0:
233             // child
234             {
235                 socket_server ss("./socket");
236
237                 // max 10 sec
238                 for (int i=0; i < 10; i++)
239                     ss.fill_buffer(1000000);
240                 // don't call atexit and stuff
241                 _exit(0);
242             }
243
244             default:
245             // parent
246             {
247                 string data;
248
249                 // wait till server is up
250                 sleep(1);
251                 socket_client_connection sc("./socket");
252                 command_client cc(&sc,1000000,1000000);
253
254                 t2n_exception* ep=cc.get_constuctor_exception();
255
256                 string errormsg;
257                 if (ep)
258                     errormsg=ep->what();
259
260                 CPPUNIT_ASSERT_EQUAL(string("timeout exceeded"),errormsg);
261             }
262         }
263     }
264
265     void HelloTimeoutSlowData()
266     {
267         switch(child_pid=fork())
268         {
269             case -1:
270             {
271                 CPPUNIT_FAIL("fork error");
272                 break;
273             }
274             case 0:
275             // child
276             {
277                 socket_server ss("./socket");
278
279                 // create a valid packet
280                 ostringstream hello;
281                 hello << "T2Nv" << PROTOCOL_VERSION << ';';
282                 int byteordercheck=1;
283                 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
284                 hello << ';';
285
286                 packet_size_indicator psize=htonl(hello.str().size());
287                 std::string send_data(hello.str());
288                 send_data.insert(0,(char*)&psize,sizeof(packet_size_indicator));
289
290                 ss.add_callback(new_connection,bind(&test_timeout::send_slow_raw_socket, boost::ref(*this), send_data,&ss, _1));
291
292                 // max 10 sec
293                 for (int i=0; i < 10; i++)
294                     ss.fill_buffer(1000000);
295                 // don't call atexit and stuff
296                 _exit(0);
297             }
298
299             default:
300             // parent
301             {
302                 string data;
303
304                 // wait till server is up
305                 sleep(1);
306                 socket_client_connection sc("./socket");
307                 command_client cc(&sc,1000000,1000000);
308
309                 t2n_exception* ep=cc.get_constuctor_exception();
310
311                 string errormsg;
312                 if (ep)
313                     errormsg=ep->what();
314
315                 CPPUNIT_ASSERT_EQUAL(string("timeout exceeded"),errormsg);
316             }
317         }
318     }
319
320     void CommandTimeout()
321     {
322         switch(child_pid=fork())
323         {
324             case -1:
325             {
326                 CPPUNIT_FAIL("fork error");
327                 break;
328             }
329             case 0:
330             // child
331             {
332                 socket_server ss("./socket");
333
334                 ostringstream hello;
335                 hello << "T2Nv" << PROTOCOL_VERSION << ';';
336                 int byteordercheck=1;
337                 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
338                 hello << ';';
339
340                 ss.add_callback(new_connection,bind(&test_timeout::send_hello, boost::ref(*this), hello.str(),&ss, _1));
341
342                 // max 10 sec
343                 for (int i=0; i < 10; i++)
344                     ss.fill_buffer(1000000);
345                 // don't call atexit and stuff
346                 _exit(0);
347             }
348
349             default:
350             // parent
351             {
352                 string data;
353
354                 // wait till server is up
355                 sleep(1);
356                 socket_client_connection sc("./socket");
357
358                 command_client cc(&sc,1000000,1000000);
359                 result_container rc;
360
361                 string errormsg;
362
363                 try
364                 {
365                     cc.send_command(new testfunc2_cmd("hello"),rc);
366                 }
367                 catch(t2n_transfer_error &e)
368                 { errormsg=e.what(); }
369                 catch(...)
370                 { throw; }
371
372                 CPPUNIT_ASSERT_EQUAL(string("timeout exceeded"),errormsg);
373             }
374         }
375     }
376
377     void CommandSlowResponse()
378     {
379         switch(child_pid=fork())
380         {
381             case -1:
382             {
383                 CPPUNIT_FAIL("fork error");
384                 break;
385             }
386             case 0:
387             // child
388             {
389                 socket_server ss("./socket");
390
391                 ostringstream hello;
392                 hello << "T2Nv" << PROTOCOL_VERSION << ';';
393                 int byteordercheck=1;
394                 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
395                 hello << ';';
396
397                 ss.add_callback(new_connection,bind(&test_timeout::send_hello, boost::ref(*this), hello.str(),&ss, _1));
398
399                 // max 10 sec
400                 for (int i=0; i < 10; i++)
401                 {
402                     ss.fill_buffer(1000000);
403
404                     string data;
405                     unsigned int cid;
406
407                     if(ss.get_packet(data,cid))
408                     {
409                         // create a valid packet & send
410                         string response="abcdefghijklmnopqrstuvwxyz";
411                         packet_size_indicator psize=htonl(response.size());
412                         std::string send_data(response);
413                         send_data.insert(0,(char*)&psize,sizeof(packet_size_indicator));
414                         send_slow_raw_socket(send_data,&ss,cid);
415                     }
416                 }
417                 // don't call atexit and stuff
418                 _exit(0);
419             }
420
421             default:
422             // parent
423             {
424                 string data;
425
426                 // wait till server is up
427                 sleep(1);
428                 socket_client_connection sc("./socket");
429
430                 command_client cc(&sc,1000000,1000000);
431                 result_container rc;
432
433                 string errormsg;
434
435                 try
436                 {
437                     cc.send_command(new testfunc2_cmd("hello"),rc);
438                 }
439                 catch(t2n_transfer_error &e)
440                 { errormsg=e.what(); }
441                 catch(...)
442                 { throw; }
443
444                 CPPUNIT_ASSERT_EQUAL(string("timeout exceeded"),errormsg);
445             }
446         }
447     }
448
449     void DisconnectOnWrite()
450     {
451         switch(child_pid=fork())
452         {
453             case -1:
454             {
455                 CPPUNIT_FAIL("fork error");
456                 break;
457             }
458             case 0:
459             // child
460             {
461                 socket_server ss("./socket");
462
463                 // bail out as soon as we get something
464                 ss.fill_buffer(-1);
465                 // don't call atexit and stuff
466                 _exit(0);
467             }
468
469             default:
470             // parent
471             {
472                 string data;
473
474                 // don't kill us on broken pipe
475                 signal(SIGPIPE, SIG_IGN);
476
477                 // wait till server is up
478                 sleep(1);
479                 socket_client_connection sc("./socket");
480
481                 string errormsg;
482
483                 string huge(5000000,'x');
484
485                 try
486                 {
487                     sc.write(huge);
488                 }
489                 catch(t2n_transfer_error &e)
490                 { errormsg=e.what(); }
491                 catch(...)
492                 { throw; }
493
494                 CPPUNIT_ASSERT_EQUAL(string("write() returned Broken pipe"),errormsg);
495             }
496         }
497     }
498
499     void WriteTwice()
500     {
501         switch(child_pid=fork())
502         {
503             case -1:
504             {
505                 CPPUNIT_FAIL("fork error");
506                 break;
507             }
508             case 0:
509             // child
510             {
511                 socket_server ss("./socket");
512
513                 // bail out as soon as we get something
514                 ss.fill_buffer(-1);
515                 // don't call atexit and stuff
516                 _exit(0);
517             }
518
519             default:
520             // parent
521             {
522                 string data;
523
524                 // don't kill us on broken pipe
525                 signal(SIGPIPE, SIG_IGN);
526
527                 // wait till server is up
528                 sleep(1);
529                 socket_client_connection sc("./socket");
530
531                 string errormsg;
532
533                 sc.write("somedata");
534
535                 sleep(1);
536
537                 // server should disconnect now
538                 try
539                 {
540                     sc.write("other data");
541                 }
542                 catch(t2n_transfer_error &e)
543                 { errormsg=e.what(); }
544                 catch(...)
545                 { throw; }
546
547                 CPPUNIT_ASSERT_EQUAL(string("write() returned Broken pipe"),errormsg);
548             }
549         }
550     }
551
552     void DisconnectOnRead()
553     {
554         pid_t pid2;
555
556         switch(child_pid=fork())
557         {
558             case -1:
559             {
560                 CPPUNIT_FAIL("fork error");
561                 break;
562             }
563             case 0:
564             // child
565             {
566                 // wait till server is up
567                 sleep(1);
568
569                 socket_client_connection sc("./socket");
570
571                 // this is an evil hack to get access to real_write, don't ever do this in an app!!!
572                 real_write_client_connection *rwc=(real_write_client_connection*)&sc;
573                 rwc->real_write(string(10000,'x'));
574
575                 // don't call atexit and stuff
576                 _exit(0);
577             }
578
579             default:
580             // parent
581             {
582                 // don't kill us on broken pipe
583                 signal(SIGPIPE, SIG_IGN);
584
585                 socket_server ss("./socket");
586
587                 time_t t0 = time(NULL);
588
589                 // max 5 sec
590                 while (time(NULL) < t0 + 5 )
591                 {
592                     ss.fill_buffer(1000000);
593
594                     string data;
595                     ss.get_packet(data);
596                 }
597
598                 // are we still alive and able to process data?
599
600                 switch(pid2=fork())
601                 {
602                     case -1:
603                     {
604                         CPPUNIT_FAIL("fork error");
605                         break;
606                     }
607                     case 0:
608                     // child
609                     {
610                         socket_client_connection *sc=new socket_client_connection("./socket");
611                         sc->write(string(10000,'x'));
612                         delete sc;
613                         // socket is closed regularly
614
615                         // don't run regular cleanup, otherwise cppunit stuff gets called
616                         _exit(0);
617                     }
618
619                     default:
620                     // parent
621                     {
622                         string received;
623
624                         t0 = time(NULL);
625
626                         // max 10 sec
627                         while (time(NULL) < t0 + 10 )
628                         {
629                             ss.fill_buffer(1000000);
630
631                             if (ss.get_packet(received))
632                                 break;
633                         }
634
635                         CPPUNIT_ASSERT_EQUAL(string(10000,'x'),received);
636                     }
637                 }
638             }
639         }
640         kill(pid2,SIGKILL);
641     }
642
643     void BreakAccept()
644     {
645         pid_t pid2;
646
647         switch(child_pid=fork())
648         {
649             case -1:
650             {
651                 CPPUNIT_FAIL("fork error");
652                 break;
653             }
654             case 0:
655             // child
656             {
657                 // wait till server is really up and waiting
658                 sleep(2);
659
660                 // connect with very tight timeout and only 1 retry
661                 socket_client_connection sc("./socket",50,1);
662
663                 // don't call atexit and stuff
664                 _exit(0);
665             }
666
667             default:
668             // parent
669             {
670                 // don't kill us on broken pipe
671                 signal(SIGPIPE, SIG_IGN);
672
673                 socket_server ss("./socket");
674
675                 // server is "working" while client wants to connect
676                 sleep(5);
677
678                 time_t t0 = time(NULL);
679
680                 // max 5 sec
681                 while (time(NULL) < t0 + 5 )
682                 {
683                     ss.fill_buffer(1000000);
684
685                     string data;
686                     ss.get_packet(data);
687                 }
688
689                 // are we still alive and able to process data?
690
691                 switch(pid2=fork())
692                 {
693                     case -1:
694                     {
695                         CPPUNIT_FAIL("fork error");
696                         break;
697                     }
698                     case 0:
699                     // child
700                     {
701                         socket_client_connection *sc=new socket_client_connection("./socket");
702                         sc->write(string(10000,'x'));
703                         delete sc;
704                         // socket is closed regularly
705
706                         // don't run regular cleanup, otherwise cppunit stuff gets called
707                         _exit(0);
708                     }
709
710                     default:
711                     // parent
712                     {
713                         string received;
714
715                         t0 = time(NULL);
716
717                         // max 10 sec
718                         while (time(NULL) < t0 + 10 )
719                         {
720                             ss.fill_buffer(1000000);
721
722                             if (ss.get_packet(received))
723                                 break;
724                         }
725
726                         CPPUNIT_ASSERT_EQUAL(string(10000,'x'),received);
727                     }
728                 }
729             }
730         }
731         kill(pid2,SIGKILL);
732     }
733 };
734
735 CPPUNIT_TEST_SUITE_REGISTRATION(test_timeout);