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