3434c0b44e7dc31b67c2fa4aec16eefcd258fc3e
[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 #define BOOST_TEST_DYN_LINK
36 #include <boost/test/unit_test.hpp>
37
38 #include <boost/archive/binary_oarchive.hpp>
39 #include <boost/archive/binary_iarchive.hpp>
40 #include <boost/archive/xml_oarchive.hpp>
41 #include <boost/archive/xml_iarchive.hpp>
42 #include <boost/serialization/serialization.hpp>
43
44 #include <container.hxx>
45 #include <socket_client.hxx>
46 #include <socket_server.hxx>
47 #include <command_client.hxx>
48 #include <command_server.hxx>
49
50 #include "test_fixtures.hxx"
51
52 #ifdef HAVE_CONFIG_H
53 #include <config.h>
54 #endif
55
56 using namespace std;
57 using namespace libt2n;
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
147 class test_timeoutFixture : public KillChildOnShutdownFixture
148 {
149 protected:
150     typedef uint32_t packet_size_indicator;
151
152     void send_hello(string hello_string, socket_server* ss, unsigned int conn_id)
153     {
154         server_connection *sc=ss->get_connection(conn_id);
155         sc->write(hello_string);
156     }
157
158     void send_slow_raw_socket(string data, socket_server* ss, unsigned int conn_id)
159     {
160         socket_server_connection *ssc=dynamic_cast<socket_server_connection*>(ss->get_connection(conn_id));
161
162         // this is an evil hack to get access to real_write, don't ever do this in an app!!!
163         real_write_connection *rwc=(real_write_connection*)ssc;
164
165         // we write one char each 0.2 sec
166         for (int pos=0; pos < data.size(); pos++)
167         {
168             string onebyte;
169             onebyte+=data[pos];
170             rwc->real_write(onebyte);
171             usleep(200000);
172         }
173     }
174
175 public:
176     test_timeoutFixture()
177     {
178     }
179
180     ~test_timeoutFixture()
181     {
182     }
183 };
184
185 BOOST_FIXTURE_TEST_SUITE(test_timeout, test_timeoutFixture)
186
187 BOOST_AUTO_TEST_CASE(ConnectTimeout)
188 {
189     switch(child_pid=fork())
190     {
191         case -1:
192         {
193             BOOST_FAIL("fork error");
194             break;
195         }
196         case 0:
197         // child
198         {
199             try
200             {
201                 socket_server ss("./socket");
202             } catch(...)
203             {
204                 std::cerr << "exception in child. ignoring\n";
205             }
206
207             // don't call atexit and stuff
208             _exit(0);
209         }
210
211         default:
212         // parent
213         {
214             string data;
215
216             // wait till server is up
217             sleep(1);
218
219             string errormsg;
220
221             socket_client_connection sc("./socket");
222
223             BOOST_CHECK_MESSAGE(sc.connection::is_closed() == true, "connection not closed");
224
225             BOOST_CHECK_MESSAGE(sc.get_last_error_msg() == string("no more retries left after connect error"), "wrong errormessage");
226         }
227     }
228 }
229
230 BOOST_AUTO_TEST_CASE(HelloTimeoutNothing)
231 {
232     switch(child_pid=fork())
233     {
234         case -1:
235         {
236             BOOST_FAIL("fork error");
237             break;
238         }
239         case 0:
240         // child
241         {
242             try
243             {
244                 socket_server ss("./socket");
245
246                 // max 10 sec
247                 for (int i=0; i < 10; i++)
248                     ss.fill_buffer(1000000);
249             } catch(...)
250             {
251                 std::cerr << "exception in child. ignoring\n";
252             }
253
254             // don't call atexit and stuff
255             _exit(0);
256         }
257
258         default:
259         // parent
260         {
261             string data;
262
263             // wait till server is up
264             sleep(1);
265             socket_client_connection sc("./socket");
266             command_client cc(&sc,1000000,1000000);
267
268             t2n_exception* ep=cc.get_constuctor_exception();
269
270             string errormsg;
271             if (ep)
272                 errormsg=ep->what();
273
274             BOOST_CHECK_EQUAL(string("timeout exceeded"),errormsg);
275         }
276     }
277 }
278
279 BOOST_AUTO_TEST_CASE(HelloTimeoutSlowData)
280 {
281     switch(child_pid=fork())
282     {
283         case -1:
284         {
285             BOOST_FAIL("fork error");
286             break;
287         }
288         case 0:
289         // child
290         {
291             try
292             {
293                 socket_server ss("./socket");
294
295                 // create a valid packet
296                 ostringstream hello;
297                 hello << "T2Nv" << PROTOCOL_VERSION << ';';
298                 int byteordercheck=1;
299                 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
300                 hello << ';';
301
302                 packet_size_indicator psize=htonl(hello.str().size());
303                 std::string send_data(hello.str());
304                 send_data.insert(0,(char*)&psize,sizeof(packet_size_indicator));
305
306                 ss.add_callback(new_connection,bind(&test_timeout::HelloTimeoutSlowData::send_slow_raw_socket, boost::ref(*this), send_data,&ss, _1));
307
308                 // max 10 sec
309                 for (int i=0; i < 10; i++)
310                     ss.fill_buffer(1000000);
311             } catch(...)
312             {
313                 std::cerr << "exception in child. ignoring\n";
314             }
315
316             // don't call atexit and stuff
317             _exit(0);
318         }
319
320         default:
321         // parent
322         {
323             string data;
324
325             // wait till server is up
326             sleep(1);
327             socket_client_connection sc("./socket");
328             command_client cc(&sc,1000000,1000000);
329
330             t2n_exception* ep=cc.get_constuctor_exception();
331
332             string errormsg;
333             if (ep)
334                 errormsg=ep->what();
335
336             BOOST_CHECK_EQUAL(string("timeout exceeded"),errormsg);
337         }
338     }
339 }
340
341 BOOST_AUTO_TEST_CASE(CommandTimeout)
342 {
343     switch(child_pid=fork())
344     {
345         case -1:
346         {
347             BOOST_FAIL("fork error");
348             break;
349         }
350         case 0:
351         // child
352         {
353             try
354             {
355                 socket_server ss("./socket");
356
357                 ostringstream hello;
358                 hello << "T2Nv" << PROTOCOL_VERSION << ';';
359                 int byteordercheck=1;
360                 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
361                 hello << ';';
362
363                 ss.add_callback(new_connection,bind(&test_timeout::CommandTimeout::send_hello, boost::ref(*this), hello.str(),&ss, _1));
364
365                 // max 10 sec
366                 for (int i=0; i < 10; i++)
367                     ss.fill_buffer(1000000);
368             } catch(...)
369             {
370                 std::cerr << "exception in child. ignoring\n";
371             }
372
373             // don't call atexit and stuff
374             _exit(0);
375         }
376
377         default:
378         // parent
379         {
380             string data;
381
382             // wait till server is up
383             sleep(1);
384             socket_client_connection sc("./socket");
385
386             command_client cc(&sc,1000000,1000000);
387             result_container rc;
388
389             string errormsg;
390
391             try
392             {
393                 cc.send_command(new testfunc2_cmd("hello"),rc);
394             }
395             catch(t2n_transfer_error &e)
396             { errormsg=e.what(); }
397             catch(...)
398             { throw; }
399
400             BOOST_CHECK_EQUAL(string("timeout exceeded"),errormsg);
401         }
402     }
403 }
404
405 BOOST_AUTO_TEST_CASE(CommandSlowResponse)
406 {
407     switch(child_pid=fork())
408     {
409         case -1:
410         {
411             BOOST_FAIL("fork error");
412             break;
413         }
414         case 0:
415         // child
416         {
417             try
418             {
419                 socket_server ss("./socket");
420
421                 ostringstream hello;
422                 hello << "T2Nv" << PROTOCOL_VERSION << ';';
423                 int byteordercheck=1;
424                 hello.write((char*)&byteordercheck,sizeof(byteordercheck));
425                 hello << ';';
426
427                 ss.add_callback(new_connection,bind(&test_timeout::CommandSlowResponse::send_hello, boost::ref(*this), hello.str(),&ss, _1));
428
429                 // max 10 sec
430                 for (int i=0; i < 10; i++)
431                 {
432                     ss.fill_buffer(1000000);
433
434                     string data;
435                     unsigned int cid;
436
437                     if(ss.get_packet(data,cid))
438                     {
439                         // create a valid packet & send
440                         string response="abcdefghijklmnopqrstuvwxyz";
441                         packet_size_indicator psize=htonl(response.size());
442                         std::string send_data(response);
443                         send_data.insert(0,(char*)&psize,sizeof(packet_size_indicator));
444                         send_slow_raw_socket(send_data,&ss,cid);
445                     }
446                 }
447             } catch(...)
448             {
449                 std::cerr << "exception in child. ignoring\n";
450             }
451
452             // don't call atexit and stuff
453             _exit(0);
454         }
455
456         default:
457         // parent
458         {
459             string data;
460
461             // wait till server is up
462             sleep(1);
463             socket_client_connection sc("./socket");
464
465             command_client cc(&sc,1000000,1000000);
466             result_container rc;
467
468             string errormsg;
469
470             try
471             {
472                 cc.send_command(new testfunc2_cmd("hello"),rc);
473             }
474             catch(t2n_transfer_error &e)
475             { errormsg=e.what(); }
476             catch(...)
477             { throw; }
478
479             BOOST_CHECK_EQUAL(string("timeout exceeded"),errormsg);
480         }
481     }
482 }
483
484 BOOST_AUTO_TEST_CASE(DisconnectOnWrite)
485 {
486     switch(child_pid=fork())
487     {
488         case -1:
489         {
490             BOOST_FAIL("fork error");
491             break;
492         }
493         case 0:
494         // child
495         {
496             try
497             {
498                 socket_server ss("./socket");
499
500                 // bail out as soon as we get something
501                 ss.fill_buffer(-1);
502             } catch(...)
503             {
504                 std::cerr << "exception in child. ignoring\n";
505             }
506
507             // don't call atexit and stuff
508             _exit(0);
509         }
510
511         default:
512         // parent
513         {
514             string data;
515
516             // don't kill us on broken pipe
517             signal(SIGPIPE, SIG_IGN);
518
519             // wait till server is up
520             sleep(1);
521             socket_client_connection sc("./socket");
522
523             string errormsg;
524
525             string huge(5000000,'x');
526
527             try
528             {
529                 sc.write(huge);
530             }
531             catch(t2n_transfer_error &e)
532             { errormsg=e.what(); }
533             catch(...)
534             { throw; }
535
536             BOOST_CHECK_EQUAL(string("write() returned Broken pipe"),errormsg);
537         }
538     }
539 }
540
541 BOOST_AUTO_TEST_CASE(WriteTwice)
542 {
543     switch(child_pid=fork())
544     {
545         case -1:
546         {
547             BOOST_FAIL("fork error");
548             break;
549         }
550         case 0:
551         // child
552         {
553             try
554             {
555                 socket_server ss("./socket");
556
557                 // bail out as soon as we get something
558                 ss.fill_buffer(-1);
559                 ss.fill_buffer(-1);
560             } catch(...)
561             {
562                 std::cerr << "exception in child. ignoring\n";
563             }
564
565             // don't call atexit and stuff
566             _exit(0);
567         }
568
569         default:
570         // parent
571         {
572             string data;
573
574             // don't kill us on broken pipe
575             signal(SIGPIPE, SIG_IGN);
576
577             // wait till server is up
578             sleep(1);
579             socket_client_connection sc("./socket");
580
581             string errormsg;
582
583             sc.write("somedata");
584
585             sleep(1);
586
587             // server should disconnect now
588             try
589             {
590                 sc.write("other data(2)");
591                 sleep(1);
592                 sc.write("other data(3)");
593             }
594             catch(t2n_transfer_error &e)
595             { errormsg=e.what(); }
596             catch(...)
597             { throw; }
598
599             BOOST_CHECK_EQUAL(string("write() returned Broken pipe"),errormsg);
600         }
601     }
602 }
603
604 BOOST_AUTO_TEST_CASE(DisconnectOnRead)
605 {
606     pid_t pid2;
607
608     switch(child_pid=fork())
609     {
610         case -1:
611         {
612             BOOST_FAIL("fork error");
613             break;
614         }
615         case 0:
616         // child
617         {
618             try
619             {
620                 // wait till server is up
621                 sleep(1);
622
623                 socket_client_connection sc("./socket");
624
625                 // this is an evil hack to get access to real_write, don't ever do this in an app!!!
626                 real_write_client_connection *rwc=(real_write_client_connection*)&sc;
627                 rwc->real_write(string(10000,'x'));
628             } catch(...)
629             {
630                 std::cerr << "exception in child. ignoring\n";
631             }
632
633             // don't call atexit and stuff
634             _exit(0);
635         }
636
637         default:
638         // parent
639         {
640             // don't kill us on broken pipe
641             signal(SIGPIPE, SIG_IGN);
642
643             socket_server ss("./socket");
644
645             time_t t0 = time(NULL);
646
647             // max 5 sec
648             while (time(NULL) < t0 + 5 )
649             {
650                 ss.fill_buffer(1000000);
651
652                 string data;
653                 ss.get_packet(data);
654             }
655
656             // are we still alive and able to process data?
657
658             switch(pid2=fork())
659             {
660                 case -1:
661                 {
662                     BOOST_FAIL("fork error");
663                     break;
664                 }
665                 case 0:
666                 // child
667                 {
668                     try
669                     {
670                         socket_client_connection *sc=new socket_client_connection("./socket");
671                         sc->write(string(10000,'x'));
672                         // socket is closed regularly
673                         delete sc;
674                     } catch(...)
675                     {
676                         std::cerr << "exception in child. ignoring\n";
677                     }
678
679                     // don't run regular cleanup, otherwise cppunit stuff gets called
680                     _exit(0);
681                 }
682
683                 default:
684                 // parent
685                 {
686                     string received;
687
688                     t0 = time(NULL);
689
690                     // max 10 sec
691                     while (time(NULL) < t0 + 10 )
692                     {
693                         ss.fill_buffer(1000000);
694
695                         if (ss.get_packet(received))
696                             break;
697                     }
698
699                     BOOST_CHECK_EQUAL(string(10000,'x'),received);
700                 }
701             }
702         }
703     }
704     kill(pid2,SIGKILL);
705 }
706
707 BOOST_AUTO_TEST_CASE(BreakAccept)
708 {
709     pid_t pid2;
710
711     switch(child_pid=fork())
712     {
713         case -1:
714         {
715             BOOST_FAIL("fork error");
716             break;
717         }
718         case 0:
719         // child
720         {
721             try
722             {
723                 // wait till server is really up and waiting
724                 sleep(2);
725
726                 // connect with very tight timeout and only 1 retry
727                 socket_client_connection sc("./socket",50,1);
728             } catch(...)
729             {
730                 std::cerr << "exception in child. ignoring\n";
731             }
732
733             // don't call atexit and stuff
734             _exit(0);
735         }
736
737         default:
738         // parent
739         {
740             // don't kill us on broken pipe
741             signal(SIGPIPE, SIG_IGN);
742
743             socket_server ss("./socket");
744
745             // server is "working" while client wants to connect
746             sleep(5);
747
748             time_t t0 = time(NULL);
749
750             // max 5 sec
751             while (time(NULL) < t0 + 5 )
752             {
753                 ss.fill_buffer(1000000);
754
755                 string data;
756                 ss.get_packet(data);
757             }
758
759             // are we still alive and able to process data?
760
761             switch(pid2=fork())
762             {
763                 case -1:
764                 {
765                     BOOST_FAIL("fork error");
766                     break;
767                 }
768                 case 0:
769                 // child
770                 {
771                     try
772                     {
773                         socket_client_connection *sc=new socket_client_connection("./socket");
774                         sc->write(string(10000,'x'));
775                         delete sc;
776                         // socket is closed regularly
777                     } catch(...)
778                     {
779                         std::cerr << "exception in child. ignoring\n";
780                     }
781
782                     // don't run regular cleanup, otherwise cppunit stuff gets called
783                     _exit(0);
784                 }
785
786                 default:
787                 // parent
788                 {
789                     string received;
790
791                     t0 = time(NULL);
792
793                     // max 10 sec
794                     while (time(NULL) < t0 + 10 )
795                     {
796                         ss.fill_buffer(1000000);
797
798                         if (ss.get_packet(received))
799                             break;
800                     }
801
802                     BOOST_CHECK_EQUAL(string(10000,'x'),received);
803                 }
804             }
805         }
806     }
807     kill(pid2,SIGKILL);
808 }
809
810 BOOST_AUTO_TEST_SUITE_END()