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