Change license from LGPL to GPL version 2 + linking exception. This fixes C++ templat...
[libt2n] / test / timeout.cpp
1 /*
2 Copyright (C) 2004 by Intra2net AG
3
4 The software in this package is distributed under the GNU General
5 Public License version 2 (with a special exception described below).
6
7 A copy of GNU General Public License (GPL) is included in this distribution,
8 in the file COPYING.GPL.
9
10 As a special exception, if other files instantiate templates or use macros
11 or inline functions from this file, or you compile this file and link it
12 with other works to produce a work based on this file, this file
13 does not by itself cause the resulting work to be covered
14 by the GNU General Public License.
15
16 However the source code for this file must still be made available
17 in accordance with section (3) of the GNU General Public License.
18
19 This exception does not invalidate any other reasons why a work based
20 on this file might be covered by the GNU General Public License.
21 */
22 #include <sys/types.h>
23 #include <unistd.h>
24 #include <errno.h>
25 #include <signal.h>
26 #include <stdio.h>
27
28 #include <iostream>
29 #include <string>
30 #include <sstream>
31 #include <stdexcept>
32
33 #include <boost/bind.hpp>
34
35 #include <cppunit/extensions/TestFactoryRegistry.h>
36 #include <cppunit/ui/text/TestRunner.h>
37 #include <cppunit/extensions/HelperMacros.h>
38
39 #include <boost/archive/binary_oarchive.hpp>
40 #include <boost/archive/binary_iarchive.hpp>
41 #include <boost/archive/xml_oarchive.hpp>
42 #include <boost/archive/xml_iarchive.hpp>
43 #include <boost/serialization/serialization.hpp>
44
45 #include <container.hxx>
46 #include <socket_client.hxx>
47 #include <socket_server.hxx>
48 #include <command_client.hxx>
49 #include <command_server.hxx>
50
51 #ifdef HAVE_CONFIG_H
52 #include <config.h>
53 #endif
54
55 using namespace std;
56 using namespace libt2n;
57 using namespace CppUnit;
58
59 string testfunc2(const string& str)
60 {
61     if (str=="throw")
62         throw libt2n::t2n_runtime_error("throw me around");
63     string ret(str);
64     ret+=", testfunc() was here";
65     return ret;
66 }
67
68 class testfunc2_res : public libt2n::result
69 {
70     private:
71         string res;
72
73         friend class boost::serialization::access;
74         template<class Archive>
75         void serialize(Archive & ar, const unsigned int version)
76         {
77             ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::result);
78             ar & BOOST_SERIALIZATION_NVP(res);
79         }
80
81     public:
82         testfunc2_res()
83             { }
84
85         testfunc2_res(const string& str)
86         {
87             res=str;
88         }
89
90         string get_data()
91         {
92             return res;
93         }
94 };
95
96
97 class testfunc2_cmd : public libt2n::command
98 {
99     private:
100         string param;
101
102         friend class boost::serialization::access;
103         template<class Archive>
104         void serialize(Archive & ar, const unsigned int version)
105         {
106             ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::command);
107             ar & BOOST_SERIALIZATION_NVP(param);
108         }
109
110     public:
111         testfunc2_cmd()
112             { }
113
114         testfunc2_cmd(const string& str)
115         {
116             param=str;
117         }
118
119         libt2n::result* operator()()
120         {
121             return new testfunc2_res(testfunc2(param));
122         }
123 };
124
125 #include <boost/serialization/export.hpp>
126
127 BOOST_CLASS_EXPORT(testfunc2_cmd)
128 BOOST_CLASS_EXPORT(testfunc2_res)
129
130 // this is an evil hack to get access to real_write, don't ever do this in an app!!!
131 class real_write_connection: public socket_server_connection
132 {
133     public:
134         void real_write(const std::string& data)
135             { socket_write(data); }
136 };
137
138 // this is an evil hack to get access to real_write, don't ever do this in an app!!!
139 class real_write_client_connection: public socket_client_connection
140 {
141     public:
142         void real_write(const std::string& data)
143             { socket_write(data); }
144 };
145
146 class test_timeout : public TestFixture
147 {
148     CPPUNIT_TEST_SUITE(test_timeout);
149
150     CPPUNIT_TEST(ConnectTimeout);
151     CPPUNIT_TEST(HelloTimeoutNothing);
152     CPPUNIT_TEST(HelloTimeoutSlowData);
153     CPPUNIT_TEST(CommandTimeout);
154     CPPUNIT_TEST(CommandSlowResponse);
155     CPPUNIT_TEST(DisconnectOnWrite);
156     CPPUNIT_TEST(WriteTwice);
157     CPPUNIT_TEST(DisconnectOnRead);
158     CPPUNIT_TEST(BreakAccept);
159
160     CPPUNIT_TEST_SUITE_END();
161
162     pid_t child_pid;
163
164     public:
165
166     typedef uint32_t packet_size_indicator;
167
168     void setUp()
169     { }
170
171     void tearDown()
172     {
173         // make sure the server-child is dead before the next test runs
174         kill(child_pid,SIGKILL);
175         sleep(1);
176     }
177
178     void send_hello(string hello_string, socket_server* ss, unsigned int conn_id)
179     {
180         server_connection *sc=ss->get_connection(conn_id);
181         sc->write(hello_string);
182     }
183
184     void send_slow_raw_socket(string data, socket_server* ss, unsigned int conn_id)
185     {
186         socket_server_connection *ssc=dynamic_cast<socket_server_connection*>(ss->get_connection(conn_id));
187
188         // this is an evil hack to get access to real_write, don't ever do this in an app!!!
189         real_write_connection *rwc=(real_write_connection*)ssc;
190
191         // we write one char each 0.2 sec
192         for (int pos=0; pos < data.size(); pos++)
193         {
194             string onebyte;
195             onebyte+=data[pos];
196             rwc->real_write(onebyte);
197             usleep(200000);
198         }
199     }
200
201     void ConnectTimeout()
202     {
203         switch(child_pid=fork())
204         {
205             case -1:
206             {
207                 CPPUNIT_FAIL("fork error");
208                 break;
209             }
210             case 0:
211             // child
212             {
213                 try
214                 {
215                     socket_server ss("./socket");
216                 } catch(...)
217                 {
218                     std::cerr << "exception in child. ignoring\n";
219                 }
220
221                 // don't call atexit and stuff
222                 _exit(0);
223             }
224
225             default:
226             // parent
227             {
228                 string data;
229
230                 // wait till server is up
231                 sleep(1);
232
233                 string errormsg;
234
235                 socket_client_connection sc("./socket");
236
237                 CPPUNIT_ASSERT_EQUAL_MESSAGE("connection not closed",true,sc.connection::is_closed());
238
239                 CPPUNIT_ASSERT_EQUAL_MESSAGE("wrong errormessage",string("no more retries left after connect error"),sc.get_last_error_msg());
240             }
241         }
242     }
243
244     void HelloTimeoutNothing()
245     {
246         switch(child_pid=fork())
247         {
248             case -1:
249             {
250                 CPPUNIT_FAIL("fork error");
251                 break;
252             }
253             case 0:
254             // child
255             {
256                 try
257                 {
258                     socket_server ss("./socket");
259
260                     // max 10 sec
261                     for (int i=0; i < 10; i++)
262                         ss.fill_buffer(1000000);
263                 } catch(...)
264                 {
265                     std::cerr << "exception in child. ignoring\n";
266                 }
267
268                 // don't call atexit and stuff
269                 _exit(0);
270             }
271
272             default:
273             // parent
274             {
275                 string data;
276
277                 // wait till server is up
278                 sleep(1);
279                 socket_client_connection sc("./socket");
280                 command_client cc(&sc,1000000,1000000);
281
282                 t2n_exception* ep=cc.get_constuctor_exception();
283
284                 string errormsg;
285                 if (ep)
286                     errormsg=ep->what();
287
288                 CPPUNIT_ASSERT_EQUAL(string("timeout exceeded"),errormsg);
289             }
290         }
291     }
292
293     void HelloTimeoutSlowData()
294     {
295         switch(child_pid=fork())
296         {
297             case -1:
298             {
299                 CPPUNIT_FAIL("fork error");
300                 break;
301             }
302             case 0:
303             // child
304             {
305                 try
306                 {
307                     socket_server ss("./socket");
308
309                     // create a valid packet
310                     ostringstream hello;
311                     hello << "T2Nv" << PROTOCOL_VERSION << ';';
312                     int byteordercheck=1;
313                     hello.write((char*)&byteordercheck,sizeof(byteordercheck));
314                     hello << ';';
315
316                     packet_size_indicator psize=htonl(hello.str().size());
317                     std::string send_data(hello.str());
318                     send_data.insert(0,(char*)&psize,sizeof(packet_size_indicator));
319
320                     ss.add_callback(new_connection,bind(&test_timeout::send_slow_raw_socket, boost::ref(*this), send_data,&ss, _1));
321
322                     // max 10 sec
323                     for (int i=0; i < 10; i++)
324                         ss.fill_buffer(1000000);
325                 } catch(...)
326                 {
327                     std::cerr << "exception in child. ignoring\n";
328                 }
329
330                 // don't call atexit and stuff
331                 _exit(0);
332             }
333
334             default:
335             // parent
336             {
337                 string data;
338
339                 // wait till server is up
340                 sleep(1);
341                 socket_client_connection sc("./socket");
342                 command_client cc(&sc,1000000,1000000);
343
344                 t2n_exception* ep=cc.get_constuctor_exception();
345
346                 string errormsg;
347                 if (ep)
348                     errormsg=ep->what();
349
350                 CPPUNIT_ASSERT_EQUAL(string("timeout exceeded"),errormsg);
351             }
352         }
353     }
354
355     void CommandTimeout()
356     {
357         switch(child_pid=fork())
358         {
359             case -1:
360             {
361                 CPPUNIT_FAIL("fork error");
362                 break;
363             }
364             case 0:
365             // child
366             {
367                 try
368                 {
369                     socket_server ss("./socket");
370
371                     ostringstream hello;
372                     hello << "T2Nv" << PROTOCOL_VERSION << ';';
373                     int byteordercheck=1;
374                     hello.write((char*)&byteordercheck,sizeof(byteordercheck));
375                     hello << ';';
376
377                     ss.add_callback(new_connection,bind(&test_timeout::send_hello, boost::ref(*this), hello.str(),&ss, _1));
378
379                     // max 10 sec
380                     for (int i=0; i < 10; i++)
381                         ss.fill_buffer(1000000);
382                 } catch(...)
383                 {
384                     std::cerr << "exception in child. ignoring\n";
385                 }
386
387                 // don't call atexit and stuff
388                 _exit(0);
389             }
390
391             default:
392             // parent
393             {
394                 string data;
395
396                 // wait till server is up
397                 sleep(1);
398                 socket_client_connection sc("./socket");
399
400                 command_client cc(&sc,1000000,1000000);
401                 result_container rc;
402
403                 string errormsg;
404
405                 try
406                 {
407                     cc.send_command(new testfunc2_cmd("hello"),rc);
408                 }
409                 catch(t2n_transfer_error &e)
410                 { errormsg=e.what(); }
411                 catch(...)
412                 { throw; }
413
414                 CPPUNIT_ASSERT_EQUAL(string("timeout exceeded"),errormsg);
415             }
416         }
417     }
418
419     void CommandSlowResponse()
420     {
421         switch(child_pid=fork())
422         {
423             case -1:
424             {
425                 CPPUNIT_FAIL("fork error");
426                 break;
427             }
428             case 0:
429             // child
430             {
431                 try
432                 {
433                     socket_server ss("./socket");
434
435                     ostringstream hello;
436                     hello << "T2Nv" << PROTOCOL_VERSION << ';';
437                     int byteordercheck=1;
438                     hello.write((char*)&byteordercheck,sizeof(byteordercheck));
439                     hello << ';';
440
441                     ss.add_callback(new_connection,bind(&test_timeout::send_hello, boost::ref(*this), hello.str(),&ss, _1));
442
443                     // max 10 sec
444                     for (int i=0; i < 10; i++)
445                     {
446                         ss.fill_buffer(1000000);
447
448                         string data;
449                         unsigned int cid;
450
451                         if(ss.get_packet(data,cid))
452                         {
453                             // create a valid packet & send
454                             string response="abcdefghijklmnopqrstuvwxyz";
455                             packet_size_indicator psize=htonl(response.size());
456                             std::string send_data(response);
457                             send_data.insert(0,(char*)&psize,sizeof(packet_size_indicator));
458                             send_slow_raw_socket(send_data,&ss,cid);
459                         }
460                     }
461                 } catch(...)
462                 {
463                     std::cerr << "exception in child. ignoring\n";
464                 }
465
466                 // don't call atexit and stuff
467                 _exit(0);
468             }
469
470             default:
471             // parent
472             {
473                 string data;
474
475                 // wait till server is up
476                 sleep(1);
477                 socket_client_connection sc("./socket");
478
479                 command_client cc(&sc,1000000,1000000);
480                 result_container rc;
481
482                 string errormsg;
483
484                 try
485                 {
486                     cc.send_command(new testfunc2_cmd("hello"),rc);
487                 }
488                 catch(t2n_transfer_error &e)
489                 { errormsg=e.what(); }
490                 catch(...)
491                 { throw; }
492
493                 CPPUNIT_ASSERT_EQUAL(string("timeout exceeded"),errormsg);
494             }
495         }
496     }
497
498     void DisconnectOnWrite()
499     {
500         switch(child_pid=fork())
501         {
502             case -1:
503             {
504                 CPPUNIT_FAIL("fork error");
505                 break;
506             }
507             case 0:
508             // child
509             {
510                 try
511                 {
512                     socket_server ss("./socket");
513
514                     // bail out as soon as we get something
515                     ss.fill_buffer(-1);
516                 } catch(...)
517                 {
518                     std::cerr << "exception in child. ignoring\n";
519                 }
520
521                 // don't call atexit and stuff
522                 _exit(0);
523             }
524
525             default:
526             // parent
527             {
528                 string data;
529
530                 // don't kill us on broken pipe
531                 signal(SIGPIPE, SIG_IGN);
532
533                 // wait till server is up
534                 sleep(1);
535                 socket_client_connection sc("./socket");
536
537                 string errormsg;
538
539                 string huge(5000000,'x');
540
541                 try
542                 {
543                     sc.write(huge);
544                 }
545                 catch(t2n_transfer_error &e)
546                 { errormsg=e.what(); }
547                 catch(...)
548                 { throw; }
549
550                 CPPUNIT_ASSERT_EQUAL(string("write() returned Broken pipe"),errormsg);
551             }
552         }
553     }
554
555     void WriteTwice()
556     {
557         switch(child_pid=fork())
558         {
559             case -1:
560             {
561                 CPPUNIT_FAIL("fork error");
562                 break;
563             }
564             case 0:
565             // child
566             {
567                 try
568                 {
569                     socket_server ss("./socket");
570
571                     // bail out as soon as we get something
572                     ss.fill_buffer(-1);
573                     ss.fill_buffer(-1);
574                 } catch(...)
575                 {
576                     std::cerr << "exception in child. ignoring\n";
577                 }
578
579                 // don't call atexit and stuff
580                 _exit(0);
581             }
582
583             default:
584             // parent
585             {
586                 string data;
587
588                 // don't kill us on broken pipe
589                 signal(SIGPIPE, SIG_IGN);
590
591                 // wait till server is up
592                 sleep(1);
593                 socket_client_connection sc("./socket");
594
595                 string errormsg;
596
597                 sc.write("somedata");
598
599                 sleep(1);
600
601                 // server should disconnect now
602                 try
603                 {
604                     sc.write("other data");
605                 }
606                 catch(t2n_transfer_error &e)
607                 { errormsg=e.what(); }
608                 catch(...)
609                 { throw; }
610
611                 CPPUNIT_ASSERT_EQUAL(string("write() returned Broken pipe"),errormsg);
612             }
613         }
614     }
615
616     void DisconnectOnRead()
617     {
618         pid_t pid2;
619
620         switch(child_pid=fork())
621         {
622             case -1:
623             {
624                 CPPUNIT_FAIL("fork error");
625                 break;
626             }
627             case 0:
628             // child
629             {
630                 try
631                 {
632                     // wait till server is up
633                     sleep(1);
634
635                     socket_client_connection sc("./socket");
636
637                     // this is an evil hack to get access to real_write, don't ever do this in an app!!!
638                     real_write_client_connection *rwc=(real_write_client_connection*)&sc;
639                     rwc->real_write(string(10000,'x'));
640                 } catch(...)
641                 {
642                     std::cerr << "exception in child. ignoring\n";
643                 }
644
645                 // don't call atexit and stuff
646                 _exit(0);
647             }
648
649             default:
650             // parent
651             {
652                 // don't kill us on broken pipe
653                 signal(SIGPIPE, SIG_IGN);
654
655                 socket_server ss("./socket");
656
657                 time_t t0 = time(NULL);
658
659                 // max 5 sec
660                 while (time(NULL) < t0 + 5 )
661                 {
662                     ss.fill_buffer(1000000);
663
664                     string data;
665                     ss.get_packet(data);
666                 }
667
668                 // are we still alive and able to process data?
669
670                 switch(pid2=fork())
671                 {
672                     case -1:
673                     {
674                         CPPUNIT_FAIL("fork error");
675                         break;
676                     }
677                     case 0:
678                     // child
679                     {
680                         try
681                         {
682                             socket_client_connection *sc=new socket_client_connection("./socket");
683                             sc->write(string(10000,'x'));
684                             // socket is closed regularly
685                             delete sc;
686                         } catch(...)
687                         {
688                             std::cerr << "exception in child. ignoring\n";
689                         }
690
691                         // don't run regular cleanup, otherwise cppunit stuff gets called
692                         _exit(0);
693                     }
694
695                     default:
696                     // parent
697                     {
698                         string received;
699
700                         t0 = time(NULL);
701
702                         // max 10 sec
703                         while (time(NULL) < t0 + 10 )
704                         {
705                             ss.fill_buffer(1000000);
706
707                             if (ss.get_packet(received))
708                                 break;
709                         }
710
711                         CPPUNIT_ASSERT_EQUAL(string(10000,'x'),received);
712                     }
713                 }
714             }
715         }
716         kill(pid2,SIGKILL);
717     }
718
719     void BreakAccept()
720     {
721         pid_t pid2;
722
723         switch(child_pid=fork())
724         {
725             case -1:
726             {
727                 CPPUNIT_FAIL("fork error");
728                 break;
729             }
730             case 0:
731             // child
732             {
733                 try
734                 {
735                     // wait till server is really up and waiting
736                     sleep(2);
737
738                     // connect with very tight timeout and only 1 retry
739                     socket_client_connection sc("./socket",50,1);
740                 } catch(...)
741                 {
742                     std::cerr << "exception in child. ignoring\n";
743                 }
744
745                 // don't call atexit and stuff
746                 _exit(0);
747             }
748
749             default:
750             // parent
751             {
752                 // don't kill us on broken pipe
753                 signal(SIGPIPE, SIG_IGN);
754
755                 socket_server ss("./socket");
756
757                 // server is "working" while client wants to connect
758                 sleep(5);
759
760                 time_t t0 = time(NULL);
761
762                 // max 5 sec
763                 while (time(NULL) < t0 + 5 )
764                 {
765                     ss.fill_buffer(1000000);
766
767                     string data;
768                     ss.get_packet(data);
769                 }
770
771                 // are we still alive and able to process data?
772
773                 switch(pid2=fork())
774                 {
775                     case -1:
776                     {
777                         CPPUNIT_FAIL("fork error");
778                         break;
779                     }
780                     case 0:
781                     // child
782                     {
783                         try
784                         {
785                             socket_client_connection *sc=new socket_client_connection("./socket");
786                             sc->write(string(10000,'x'));
787                             delete sc;
788                             // socket is closed regularly
789                         } catch(...)
790                         {
791                             std::cerr << "exception in child. ignoring\n";
792                         }
793
794                         // don't run regular cleanup, otherwise cppunit stuff gets called
795                         _exit(0);
796                     }
797
798                     default:
799                     // parent
800                     {
801                         string received;
802
803                         t0 = time(NULL);
804
805                         // max 10 sec
806                         while (time(NULL) < t0 + 10 )
807                         {
808                             ss.fill_buffer(1000000);
809
810                             if (ss.get_packet(received))
811                                 break;
812                         }
813
814                         CPPUNIT_ASSERT_EQUAL(string(10000,'x'),received);
815                     }
816                 }
817             }
818         }
819         kill(pid2,SIGKILL);
820     }
821 };
822
823 CPPUNIT_TEST_SUITE_REGISTRATION(test_timeout);