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