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